Merge tag 'llvmlinux-for-v3.18' of git://git.linuxfoundation.org/llvmlinux/kernel
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 15 Oct 2014 05:30:52 +0000 (07:30 +0200)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 15 Oct 2014 05:30:52 +0000 (07:30 +0200)
Pull LLVM updates from Behan Webster:
 "These patches remove the use of VLAIS using a new SHASH_DESC_ON_STACK
  macro.

  Some of the previously accepted VLAIS removal patches haven't used
  this macro.  I will push new patches to consistently use this macro in
  all those older cases for 3.19"

[ More LLVM patches coming in through subsystem trees, and LLVM itself
  needs some fixes that are already in many distributions but not in
  released versions of LLVM.  Some day this will all "just work"  - Linus ]

* tag 'llvmlinux-for-v3.18' of git://git.linuxfoundation.org/llvmlinux/kernel:
  crypto: LLVMLinux: Remove VLAIS usage from crypto/testmgr.c
  security, crypto: LLVMLinux: Remove VLAIS from ima_crypto.c
  crypto: LLVMLinux: Remove VLAIS usage from libcrc32c.c
  crypto: LLVMLinux: Remove VLAIS usage from crypto/hmac.c
  crypto, dm: LLVMLinux: Remove VLAIS usage from dm-crypt
  crypto: LLVMLinux: Remove VLAIS from crypto/.../qat_algs.c
  crypto: LLVMLinux: Remove VLAIS from crypto/omap_sham.c
  crypto: LLVMLinux: Remove VLAIS from crypto/n2_core.c
  crypto: LLVMLinux: Remove VLAIS from crypto/mv_cesa.c
  crypto: LLVMLinux: Remove VLAIS from crypto/ccp/ccp-crypto-sha.c
  btrfs: LLVMLinux: Remove VLAIS
  crypto: LLVMLinux: Add macro to remove use of VLAIS in crypto code

254 files changed:
Documentation/ABI/testing/sysfs-class-power
Documentation/acpi/enumeration.txt
Documentation/devicetree/bindings/clock/exynos3250-clock.txt
Documentation/devicetree/bindings/clock/gpio-gate-clock.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/maxim,max77686.txt
Documentation/devicetree/bindings/clock/maxim,max77802.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/pxa-clock.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt
Documentation/devicetree/bindings/clock/sunxi.txt
Documentation/devicetree/bindings/gpio/gpio-restart.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iommu/arm,smmu.txt
Documentation/devicetree/bindings/mfd/arizona.txt
Documentation/devicetree/bindings/mfd/atmel-gpbr.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/hi6421.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/max14577.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt [moved from Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt with 98% similarity]
Documentation/devicetree/bindings/mfd/rk808.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/rn5t618.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/stmpe.txt
Documentation/devicetree/bindings/mfd/twl4030-power.txt
Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/reset/st-reset.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/reset/syscon-reboot.txt [new file with mode: 0644]
Documentation/kernel-parameters.txt
Documentation/power/charger-manager.txt
Documentation/power/power_supply_class.txt
MAINTAINERS
arch/arm/boot/dts/exynos3250.dtsi
arch/arm/boot/dts/pxa27x.dtsi
arch/arm/boot/dts/sun5i-a10s.dtsi
arch/arm/boot/dts/sun5i-a13.dtsi
arch/arm/boot/dts/sun7i-a20.dtsi
arch/arm/mach-omap2/io.c
arch/arm/mach-omap2/prm_common.c
arch/arm/mach-pxa/include/mach/pxa2xx-regs.h
drivers/base/core.c
drivers/block/rbd.c
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/at91/clk-pll.c
drivers/clk/at91/clk-usb.c
drivers/clk/clk-axi-clkgen.c
drivers/clk/clk-fractional-divider.c
drivers/clk/clk-gate.c
drivers/clk/clk-gpio-gate.c [new file with mode: 0644]
drivers/clk/clk-max-gen.c [new file with mode: 0644]
drivers/clk/clk-max-gen.h [new file with mode: 0644]
drivers/clk/clk-max77686.c
drivers/clk/clk-max77802.c [new file with mode: 0644]
drivers/clk/clk-palmas.c
drivers/clk/clk-twl6040.c
drivers/clk/clk-wm831x.c
drivers/clk/clk.c
drivers/clk/hisilicon/clk-hix5hd2.c
drivers/clk/mvebu/armada-370.c
drivers/clk/mvebu/armada-375.c
drivers/clk/mvebu/common.c
drivers/clk/mvebu/common.h
drivers/clk/mvebu/kirkwood.c
drivers/clk/pxa/Makefile [new file with mode: 0644]
drivers/clk/pxa/clk-pxa.c [new file with mode: 0644]
drivers/clk/pxa/clk-pxa.h [new file with mode: 0644]
drivers/clk/pxa/clk-pxa27x.c [new file with mode: 0644]
drivers/clk/qcom/clk-pll.c
drivers/clk/qcom/clk-pll.h
drivers/clk/qcom/clk-rcg.c
drivers/clk/qcom/clk-rcg.h
drivers/clk/qcom/clk-rcg2.c
drivers/clk/qcom/common.c
drivers/clk/qcom/common.h
drivers/clk/qcom/gcc-ipq806x.c
drivers/clk/qcom/mmcc-apq8084.c
drivers/clk/qcom/mmcc-msm8960.c
drivers/clk/qcom/mmcc-msm8974.c
drivers/clk/rockchip/Makefile
drivers/clk/rockchip/clk-cpu.c [new file with mode: 0644]
drivers/clk/rockchip/clk-pll.c
drivers/clk/rockchip/clk-rk3188.c
drivers/clk/rockchip/clk-rk3288.c
drivers/clk/rockchip/clk.c
drivers/clk/rockchip/clk.h
drivers/clk/samsung/clk-exynos3250.c
drivers/clk/samsung/clk-exynos4.c
drivers/clk/samsung/clk-exynos5260.c
drivers/clk/samsung/clk-s3c2410-dclk.c
drivers/clk/samsung/clk-s3c2412.c
drivers/clk/samsung/clk-s3c2443.c
drivers/clk/shmobile/clk-rcar-gen2.c
drivers/clk/sunxi/Makefile
drivers/clk/sunxi/clk-factors.c
drivers/clk/sunxi/clk-factors.h
drivers/clk/sunxi/clk-mod0.c [new file with mode: 0644]
drivers/clk/sunxi/clk-sun6i-apb0-gates.c
drivers/clk/sunxi/clk-sun6i-apb0.c
drivers/clk/sunxi/clk-sun6i-ar100.c
drivers/clk/sunxi/clk-sun8i-apb0.c
drivers/clk/sunxi/clk-sun8i-mbus.c [new file with mode: 0644]
drivers/clk/sunxi/clk-sunxi.c
drivers/clk/tegra/clk-tegra124.c
drivers/clk/tegra/clk.c
drivers/clk/ti/clk-dra7-atl.c
drivers/clk/ti/clk.c
drivers/clk/ti/clockdomain.c
drivers/clk/ti/divider.c
drivers/clk/zynq/clkc.c
drivers/clk/zynq/pll.c
drivers/cpufreq/kirkwood-cpufreq.c
drivers/i2c/busses/i2c-cros-ec-tunnel.c
drivers/infiniband/hw/usnic/usnic_uiom.c
drivers/input/keyboard/cros_ec_keyb.c
drivers/iommu/amd_iommu.c
drivers/iommu/amd_iommu_init.c
drivers/iommu/amd_iommu_types.h
drivers/iommu/arm-smmu.c
drivers/iommu/dmar.c
drivers/iommu/exynos-iommu.c
drivers/iommu/fsl_pamu_domain.c
drivers/iommu/intel-iommu.c
drivers/iommu/intel_irq_remapping.c
drivers/iommu/iommu.c
drivers/iommu/irq_remapping.c
drivers/iommu/irq_remapping.h
drivers/iommu/msm_iommu.c
drivers/iommu/omap-iommu.c
drivers/iommu/omap-iommu.h
drivers/iommu/tegra-gart.c
drivers/iommu/tegra-smmu.c
drivers/mfd/88pm860x-i2c.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/arizona-core.c
drivers/mfd/arizona-irq.c
drivers/mfd/axp20x.c
drivers/mfd/cros_ec.c
drivers/mfd/cros_ec_spi.c
drivers/mfd/da9052-core.c
drivers/mfd/da9052-i2c.c
drivers/mfd/da9052-spi.c
drivers/mfd/hi6421-pmic-core.c [new file with mode: 0644]
drivers/mfd/htc-i2cpld.c
drivers/mfd/intel_soc_pmic_core.c
drivers/mfd/lpc_ich.c
drivers/mfd/lpc_sch.c
drivers/mfd/max14577.c
drivers/mfd/max77686.c
drivers/mfd/max77693.c
drivers/mfd/max8925-i2c.c
drivers/mfd/mc13xxx-core.c
drivers/mfd/menelaus.c
drivers/mfd/mfd-core.c
drivers/mfd/pcf50633-core.c
drivers/mfd/qcom-spmi-pmic.c [new file with mode: 0644]
drivers/mfd/rk808.c [new file with mode: 0644]
drivers/mfd/rn5t618.c [new file with mode: 0644]
drivers/mfd/rtsx_pcr.c
drivers/mfd/rtsx_usb.c
drivers/mfd/sm501.c
drivers/mfd/stmpe.c
drivers/mfd/ti_am335x_tscadc.c
drivers/mfd/tps65217.c
drivers/mfd/tps65910.c
drivers/mfd/twl4030-irq.c
drivers/mfd/twl4030-power.c
drivers/mfd/twl6040.c
drivers/mfd/wm5102-tables.c
drivers/mfd/wm5110-tables.c
drivers/mfd/wm8994-irq.c
drivers/mfd/wm8994-regmap.c
drivers/power/Kconfig
drivers/power/ab8500_fg.c
drivers/power/bq27x00_battery.c
drivers/power/charger-manager.c
drivers/power/gpio-charger.c
drivers/power/max14577_charger.c
drivers/power/max17040_battery.c
drivers/power/max8925_power.c
drivers/power/power_supply_core.c
drivers/power/power_supply_leds.c
drivers/power/power_supply_sysfs.c
drivers/power/reset/Kconfig
drivers/power/reset/Makefile
drivers/power/reset/gpio-restart.c [new file with mode: 0644]
drivers/power/reset/ltc2952-poweroff.c [new file with mode: 0644]
drivers/power/reset/msm-poweroff.c
drivers/power/reset/st-poweroff.c [new file with mode: 0644]
drivers/power/reset/syscon-reboot.c [new file with mode: 0644]
drivers/power/reset/xgene-reboot.c
drivers/power/sbs-battery.c
drivers/regulator/max14577.c
drivers/vfio/vfio_iommu_type1.c
fs/ceph/acl.c
fs/ceph/addr.c
fs/ceph/caps.c
fs/ceph/debugfs.c
fs/ceph/dir.c
fs/ceph/file.c
fs/ceph/inode.c
fs/ceph/ioctl.c
fs/ceph/mds_client.c
fs/ceph/mds_client.h
fs/ceph/super.h
fs/ceph/xattr.c
fs/namespace.c
include/asm-generic/clkdev.h
include/dt-bindings/clock/exynos3250.h
include/dt-bindings/clock/exynos4.h
include/dt-bindings/clock/hix5hd2-clock.h
include/dt-bindings/clock/maxim,max77686.h [new file with mode: 0644]
include/dt-bindings/clock/maxim,max77802.h [new file with mode: 0644]
include/dt-bindings/clock/pxa-clock.h [new file with mode: 0644]
include/dt-bindings/clock/rk3188-cru-common.h
include/dt-bindings/clock/rk3288-cru.h
include/dt-bindings/clock/rockchip,rk808.h [new file with mode: 0644]
include/dt-bindings/clock/tegra124-car.h
include/linux/ceph/libceph.h
include/linux/ceph/pagelist.h
include/linux/ceph/rados.h
include/linux/clk-private.h
include/linux/clk-provider.h
include/linux/clk.h
include/linux/clk/ti.h
include/linux/device.h
include/linux/dmar.h
include/linux/iommu.h
include/linux/mfd/arizona/registers.h
include/linux/mfd/core.h
include/linux/mfd/cros_ec.h
include/linux/mfd/da9052/da9052.h
include/linux/mfd/davinci_voicecodec.h
include/linux/mfd/hi6421-pmic.h [new file with mode: 0644]
include/linux/mfd/max14577-private.h
include/linux/mfd/max14577.h
include/linux/mfd/max77693-private.h
include/linux/mfd/max77693.h
include/linux/mfd/rk808.h [new file with mode: 0644]
include/linux/mfd/rn5t618.h [new file with mode: 0644]
include/linux/mfd/ti_am335x_tscadc.h
include/linux/mfd/ti_ssp.h [deleted file]
include/linux/mfd/tps65217.h
include/linux/pci_ids.h
include/linux/power_supply.h
net/ceph/Kconfig
net/ceph/ceph_common.c
net/ceph/ceph_strings.c
net/ceph/debugfs.c
net/ceph/messenger.c
net/ceph/mon_client.c
net/ceph/osd_client.c
net/ceph/osdmap.c
net/ceph/pagelist.c
sound/soc/codecs/arizona.c
virt/kvm/iommu.c

index 78c7baca35879103d6de56d08e1c7bd4724bc9aa..909e7602c717aa4555e36be1ef6d1591e420c7cd 100644 (file)
@@ -18,3 +18,17 @@ Description:
                This file is writeable and can be used to set the assumed
                battery 'full level'. As batteries age, this value has to be
                amended over time.
+
+What:          /sys/class/power_supply/max14577-charger/device/fast_charge_timer
+Date:          October 2014
+KernelVersion: 3.18.0
+Contact:       Krzysztof Kozlowski <k.kozlowski@samsung.com>
+Description:
+               This entry shows and sets the maximum time the max14577
+               charger operates in fast-charge mode. When the timer expires
+               the device will terminate fast-charge mode (charging current
+               will drop to 0 A) and will trigger interrupt.
+
+               Valid values:
+               - 5, 6 or 7 (hours),
+               - 0: disabled.
index e182be5e3c83cb553341f7969678370e649fcb2d..b60d2ab69497bc66b47e752d1f7588b3cdbb93ab 100644 (file)
@@ -312,3 +312,30 @@ a code like this:
 
 There are also devm_* versions of these functions which release the
 descriptors once the device is released.
+
+MFD devices
+~~~~~~~~~~~
+The MFD devices register their children as platform devices. For the child
+devices there needs to be an ACPI handle that they can use to reference
+parts of the ACPI namespace that relate to them. In the Linux MFD subsystem
+we provide two ways:
+
+       o The children share the parent ACPI handle.
+       o The MFD cell can specify the ACPI id of the device.
+
+For the first case, the MFD drivers do not need to do anything. The
+resulting child platform device will have its ACPI_COMPANION() set to point
+to the parent device.
+
+If the ACPI namespace has a device that we can match using an ACPI id,
+the id should be set like:
+
+       static struct mfd_cell my_subdevice_cell = {
+               .name = "my_subdevice",
+               /* set the resources relative to the parent */
+               .acpi_pnpid = "XYZ0001",
+       };
+
+The ACPI id "XYZ0001" is then used to lookup an ACPI device directly under
+the MFD device and if found, that ACPI companion device is bound to the
+resulting child platform device.
index aadc9c59e2d17ea31e2ccdc82a3c688de4106a66..f57d9dd9ea8530ae9c8d02723b1bc15d38002497 100644 (file)
@@ -7,6 +7,8 @@ Required Properties:
 
 - compatible: should be one of the following.
   - "samsung,exynos3250-cmu" - controller compatible with Exynos3250 SoC.
+  - "samsung,exynos3250-cmu-dmc" - controller compatible with
+    Exynos3250 SoC for Dynamic Memory Controller domain.
 
 - reg: physical base address of the controller and length of memory mapped
   region.
@@ -20,7 +22,7 @@ All available clocks are defined as preprocessor macros in
 dt-bindings/clock/exynos3250.h header and can be used in device
 tree sources.
 
-Example 1: An example of a clock controller node is listed below.
+Example 1: Examples of clock controller nodes are listed below.
 
        cmu: clock-controller@10030000 {
                compatible = "samsung,exynos3250-cmu";
@@ -28,6 +30,12 @@ Example 1: An example of a clock controller node is listed below.
                #clock-cells = <1>;
        };
 
+       cmu_dmc: clock-controller@105C0000 {
+               compatible = "samsung,exynos3250-cmu-dmc";
+               reg = <0x105C0000 0x2000>;
+               #clock-cells = <1>;
+       };
+
 Example 2: UART controller node that consumes the clock generated by the clock
           controller. Refer to the standard clock bindings for information
           about 'clocks' and 'clock-names' property.
diff --git a/Documentation/devicetree/bindings/clock/gpio-gate-clock.txt b/Documentation/devicetree/bindings/clock/gpio-gate-clock.txt
new file mode 100644 (file)
index 0000000..d3379ff
--- /dev/null
@@ -0,0 +1,21 @@
+Binding for simple gpio gated clock.
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be "gpio-gate-clock".
+- #clock-cells : from common clock binding; shall be set to 0.
+- enable-gpios : GPIO reference for enabling and disabling the clock.
+
+Optional properties:
+- clocks: Maximum of one parent clock is supported.
+
+Example:
+       clock {
+               compatible = "gpio-gate-clock";
+               clocks = <&parentclk>;
+               #clock-cells = <0>;
+               enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+       };
index 96ce71bbd745ebb09a4e9bbe44378cdf0a728cc7..9c40739a661a835af3658af49f183ef98354bbc1 100644 (file)
@@ -9,13 +9,21 @@ The MAX77686 contains three 32.768khz clock outputs that can be controlled
 Following properties should be presend in main device node of the MFD chip.
 
 Required properties:
-- #clock-cells: simple one-cell clock specifier format is used, where the
-  only cell is used as an index of the clock inside the provider. Following
-  indices are allowed:
+
+- #clock-cells: from common clock binding; shall be set to 1.
+
+Optional properties:
+- clock-output-names: From common clock binding.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume. Following indices are allowed:
     - 0: 32khz_ap clock,
     - 1: 32khz_cp clock,
     - 2: 32khz_pmic clock.
 
+Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77686.h
+header and can be used in device tree sources.
+
 Example: Node of the MFD chip
 
        max77686: max77686@09 {
@@ -34,5 +42,5 @@ Example: Clock consumer node
                compatible = "bar,foo";
                /* ... */
                clock-names = "my-clock";
-               clocks = <&max77686 2>;
+               clocks = <&max77686 MAX77686_CLK_PMIC>;
        };
diff --git a/Documentation/devicetree/bindings/clock/maxim,max77802.txt b/Documentation/devicetree/bindings/clock/maxim,max77802.txt
new file mode 100644 (file)
index 0000000..c6dc783
--- /dev/null
@@ -0,0 +1,44 @@
+Binding for Maxim MAX77802 32k clock generator block
+
+This is a part of device tree bindings of MAX77802 multi-function device.
+More information can be found in bindings/mfd/max77802.txt file.
+
+The MAX77802 contains two 32.768khz clock outputs that can be controlled
+(gated/ungated) over I2C.
+
+Following properties should be present in main device node of the MFD chip.
+
+Required properties:
+- #clock-cells: From common clock binding; shall be set to 1.
+
+Optional properties:
+- clock-output-names: From common clock binding.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume. Following indices are allowed:
+     - 0: 32khz_ap clock,
+     - 1: 32khz_cp clock.
+
+Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77802.h
+header and can be used in device tree sources.
+
+Example: Node of the MFD chip
+
+       max77802: max77802@09 {
+               compatible = "maxim,max77802";
+               interrupt-parent = <&wakeup_eint>;
+               interrupts = <26 0>;
+               reg = <0x09>;
+               #clock-cells = <1>;
+
+               /* ... */
+       };
+
+Example: Clock consumer node
+
+       foo@0 {
+               compatible = "bar,foo";
+               /* ... */
+               clock-names = "my-clock";
+               clocks = <&max77802 MAX77802_CLK_32K_AP>;
+       };
diff --git a/Documentation/devicetree/bindings/clock/pxa-clock.txt b/Documentation/devicetree/bindings/clock/pxa-clock.txt
new file mode 100644 (file)
index 0000000..4b4a902
--- /dev/null
@@ -0,0 +1,16 @@
+* Clock bindings for Marvell PXA chips
+
+Required properties:
+- compatible: Should be "marvell,pxa-clocks"
+- #clock-cells: Should be <1>
+
+The clock consumer should specify the desired clock by having the clock
+ID in its "clocks" phandle cell (see include/.../pxa-clock.h).
+
+Examples:
+
+pxa2xx_clks: pxa2xx_clks@41300004 {
+        compatible = "marvell,pxa-clocks";
+        #clock-cells = <1>;
+        status = "okay";
+};
index 8f1424f0fa439cdfac3e16e0e6e90ef039218ab0..a5f52238c80d5f9b9560914f59e653e772b42bea 100644 (file)
@@ -15,6 +15,7 @@ Required Properties:
     - "renesas,r8a7779-mstp-clocks" for R8A7779 (R-Car H1) MSTP gate clocks
     - "renesas,r8a7790-mstp-clocks" for R8A7790 (R-Car H2) MSTP gate clocks
     - "renesas,r8a7791-mstp-clocks" for R8A7791 (R-Car M2) MSTP gate clocks
+    - "renesas,r8a7794-mstp-clocks" for R8A7794 (R-Car E2) MSTP gate clocks
     - "renesas,sh73a0-mstp-clocks" for SH73A0 (SH-MobileAG5) MSTP gate clocks
     - "renesas,cpg-mstp-clock" for generic MSTP gate clocks
   - reg: Base address and length of the I/O mapped registers used by the MSTP
index 7b41c2fe54dbcfbd950b9b650d6bfc2ae374a0c0..e6ad35b894f919f537dbf58cbdea6fffd9cddfc9 100644 (file)
@@ -8,6 +8,7 @@ Required Properties:
   - compatible: Must be one of
     - "renesas,r8a7790-cpg-clocks" for the r8a7790 CPG
     - "renesas,r8a7791-cpg-clocks" for the r8a7791 CPG
+    - "renesas,r8a7794-cpg-clocks" for the r8a7794 CPG
     - "renesas,rcar-gen2-cpg-clocks" for the generic R-Car Gen2 CPG
 
   - reg: Base address and length of the memory resource used by the CPG
index d3a5c3c6d6774602667a69e9e93e7511039ef3f4..ed116df9c3e769a2ceef9cb4d1dd1eaf44473f38 100644 (file)
@@ -46,7 +46,11 @@ Required properties:
        "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31
        "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
        "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
+       "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13
+       "allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10
+       "allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10
        "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
+       "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23
        "allwinner,sun7i-a20-out-clk" - for the external output clocks
        "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
        "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
diff --git a/Documentation/devicetree/bindings/gpio/gpio-restart.txt b/Documentation/devicetree/bindings/gpio/gpio-restart.txt
new file mode 100644 (file)
index 0000000..af3701b
--- /dev/null
@@ -0,0 +1,54 @@
+Drive a GPIO line that can be used to restart the system from a restart
+handler.
+
+This binding supports level and edge triggered reset.  At driver load
+time, the driver will request the given gpio line and install a restart
+handler. If the optional properties 'open-source' is not found, the GPIO line
+will be driven in the inactive state.  Otherwise its not driven until
+the restart is initiated.
+
+When the system is restarted, the restart handler will be invoked in
+priority order.  The gpio is configured as an output, and driven active,
+triggering a level triggered reset condition. This will also cause an
+inactive->active edge condition, triggering positive edge triggered
+reset. After a delay specified by active-delay, the GPIO is set to
+inactive, thus causing an active->inactive edge, triggering negative edge
+triggered reset. After a delay specified by inactive-delay, the GPIO
+is driven active again.  After a delay specified by wait-delay, the
+restart handler completes allowing other restart handlers to be attempted.
+
+Required properties:
+- compatible : should be "gpio-restart".
+- gpios : The GPIO to set high/low, see "gpios property" in
+  Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be
+  low to reset the board set it to "Active Low", otherwise set
+  gpio to "Active High".
+
+Optional properties:
+- open-source : Treat the GPIO as being open source and defer driving
+  it to when the restart is initiated.  If this optional property is not
+  specified, the GPIO is initialized as an output in its inactive state.
+- priority : A priority ranging from 0 to 255 (default 128) according to
+  the following guidelines:
+       0:      Restart handler of last resort, with limited restart
+               capabilities
+       128:    Default restart handler; use if no other restart handler is
+               expected to be available, and/or if restart functionality is
+               sufficient to restart the entire system
+       255:    Highest priority restart handler, will preempt all other
+               restart handlers
+- active-delay: Delay (default 100) to wait after driving gpio active [ms]
+- inactive-delay: Delay (default 100) to wait after driving gpio inactive [ms]
+- wait-delay: Delay (default 3000) to wait after completing restart
+  sequence [ms]
+
+Examples:
+
+gpio-restart {
+       compatible = "gpio-restart";
+       gpios = <&gpio 4 0>;
+       priority = <128>;
+       active-delay = <100>;
+       inactive-delay = <100>;
+       wait-delay = <3000>;
+};
index 2d0f7cd867eaede3cad0a0d163e131b46716be2b..06760503a819f5fbc15e747626ce3f734b2888ff 100644 (file)
@@ -14,6 +14,7 @@ conditions.
                         "arm,smmu-v1"
                         "arm,smmu-v2"
                         "arm,mmu-400"
+                        "arm,mmu-401"
                         "arm,mmu-500"
 
                   depending on the particular implementation and/or the
index 5c7e7230984a5c064da0ef8613ded1caf78f2bb9..7bd1273f571a40fa46b7c5c0540a4fe7c9370c4a 100644 (file)
@@ -42,6 +42,13 @@ Optional properties:
     the chip default will be used.  If present exactly five values must
     be specified.
 
+  - wlf,inmode : A list of INn_MODE register values, where n is the number
+    of input signals. Valid values are 0 (Differential), 1 (Single-ended) and
+    2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default.
+    If present, values must be specified less than or equal to the number of
+    input singals. If values less than the number of input signals, elements
+    that has not been specifed are set to 0 by default.
+
   - DCVDD-supply, MICVDD-supply : Power supplies, only need to be specified if
     they are being externally supplied. As covered in
     Documentation/devicetree/bindings/regulator/regulator.txt
diff --git a/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt b/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt
new file mode 100644 (file)
index 0000000..a285695
--- /dev/null
@@ -0,0 +1,15 @@
+* Device tree bindings for Atmel GPBR (General Purpose Backup Registers)
+
+The GPBR are a set of battery-backed registers.
+
+Required properties:
+- compatible:          "atmel,at91sam9260-gpbr", "syscon"
+- reg:                 contains offset/length value of the GPBR memory
+                       region.
+
+Example:
+
+gpbr: gpbr@fffffd50 {
+       compatible = "atmel,at91sam9260-gpbr", "syscon";
+       reg = <0xfffffd50 0x10>;
+};
diff --git a/Documentation/devicetree/bindings/mfd/hi6421.txt b/Documentation/devicetree/bindings/mfd/hi6421.txt
new file mode 100644 (file)
index 0000000..0d5a446
--- /dev/null
@@ -0,0 +1,38 @@
+* HI6421 Multi-Functional Device (MFD), by HiSilicon Ltd.
+
+Required parent device properties:
+- compatible   : contains "hisilicon,hi6421-pmic";
+- reg          : register range space of hi6421;
+
+Supported Hi6421 sub-devices include:
+
+Device                     IRQ Names              Supply Names   Description
+------                     ---------              ------------   -----------
+regulators               :  None                 : None         : Regulators
+
+Required child device properties:
+None.
+
+Example:
+       hi6421 {
+               compatible = "hisilicon,hi6421-pmic";
+               reg = <0xfcc00000 0x0180>; /* 0x60 << 2 */
+
+               regulators {
+                       // supply for MLC NAND/ eMMC
+                       hi6421_vout0_reg: hi6421_vout0 {
+                               regulator-name = "VOUT0";
+                               regulator-min-microvolt = <2850000>;
+                               regulator-max-microvolt = <2850000>;
+                       };
+
+                       // supply for 26M Oscillator
+                       hi6421_vout1_reg: hi6421_vout1 {
+                               regulator-name = "VOUT1";
+                               regulator-min-microvolt = <1700000>;
+                               regulator-max-microvolt = <2000000>;
+                               regulator-boot-on;
+                               regulator-always-on;
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/mfd/max14577.txt b/Documentation/devicetree/bindings/mfd/max14577.txt
new file mode 100644 (file)
index 0000000..236264c
--- /dev/null
@@ -0,0 +1,146 @@
+Maxim MAX14577/77836 Multi-Function Device
+
+MAX14577 is a Multi-Function Device with Micro-USB Interface Circuit, Li+
+Battery Charger and SFOUT LDO output for powering USB devices. It is
+interfaced to host controller using I2C.
+
+MAX77836 additionally contains PMIC (with two LDO regulators) and Fuel Gauge.
+
+
+Required properties:
+- compatible : Must be "maxim,max14577" or "maxim,max77836".
+- reg : I2C slave address for the max14577 chip (0x25 for max14577/max77836)
+- interrupts : IRQ line for the chip.
+- interrupt-parent :  The parent interrupt controller.
+
+
+Required nodes:
+ - charger :
+       Node for configuring the charger driver.
+       Required properties:
+               - compatible : "maxim,max14577-charger"
+                       or "maxim,max77836-charger"
+               - maxim,fast-charge-uamp : Current in uA for Fast Charge;
+                       Valid values:
+                       - for max14577: 90000 - 950000;
+                       - for max77836: 45000 - 475000;
+               - maxim,eoc-uamp : Current in uA for End-Of-Charge mode;
+                       Valid values:
+                       - for max14577: 50000 - 200000;
+                       - for max77836: 5000 - 100000;
+               - maxim,ovp-uvolt : OverVoltage Protection Threshold in uV;
+                       In an overvoltage condition, INT asserts and charging
+                       stops. Valid values:
+                       - 6000000, 6500000, 7000000, 7500000;
+               - maxim,constant-uvolt : Battery Constant Voltage in uV;
+                       Valid values:
+                       - 4000000 - 4280000 (step by 20000);
+                       - 4350000;
+
+
+Optional nodes:
+- max14577-muic/max77836-muic :
+       Node used only by extcon consumers.
+       Required properties:
+               - compatible : "maxim,max14577-muic" or "maxim,max77836-muic"
+
+- regulators :
+       Required properties:
+               - compatible : "maxim,max14577-regulator"
+                       or "maxim,max77836-regulator"
+
+       May contain a sub-node per regulator from the list below. Each
+       sub-node should contain the constraints and initialization information
+       for that regulator. See regulator.txt for a description of standard
+       properties for these sub-nodes.
+
+       List of valid regulator names:
+       - for max14577: CHARGER, SAFEOUT.
+       - for max77836: CHARGER, SAFEOUT, LDO1, LDO2.
+
+       The SAFEOUT is a fixed voltage regulator so there is no need to specify
+       voltages for it.
+
+
+Example:
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+max14577@25 {
+       compatible = "maxim,max14577";
+       reg = <0x25>;
+       interrupt-parent = <&gpx1>;
+       interrupts = <5 IRQ_TYPE_NONE>;
+
+       muic: max14577-muic {
+               compatible = "maxim,max14577-muic";
+       };
+
+       regulators {
+               compatible = "maxim,max14577-regulator";
+
+               SAFEOUT {
+                       regulator-name = "SAFEOUT";
+               };
+               CHARGER {
+                       regulator-name = "CHARGER";
+                       regulator-min-microamp = <90000>;
+                       regulator-max-microamp = <950000>;
+                       regulator-boot-on;
+               };
+       };
+
+       charger {
+               compatible = "maxim,max14577-charger";
+
+               maxim,constant-uvolt = <4350000>;
+               maxim,fast-charge-uamp = <450000>;
+               maxim,eoc-uamp = <50000>;
+               maxim,ovp-uvolt = <6500000>;
+       };
+};
+
+
+max77836@25 {
+       compatible = "maxim,max77836";
+       reg = <0x25>;
+       interrupt-parent = <&gpx1>;
+       interrupts = <5 IRQ_TYPE_NONE>;
+
+       muic: max77836-muic {
+               compatible = "maxim,max77836-muic";
+       };
+
+       regulators {
+               compatible = "maxim,max77836-regulator";
+
+               SAFEOUT {
+                       regulator-name = "SAFEOUT";
+               };
+               CHARGER {
+                       regulator-name = "CHARGER";
+                       regulator-min-microamp = <90000>;
+                       regulator-max-microamp = <950000>;
+                       regulator-boot-on;
+               };
+               LDO1 {
+                       regulator-name = "LDO1";
+                       regulator-min-microvolt = <2700000>;
+                       regulator-max-microvolt = <2700000>;
+               };
+               LDO2 {
+                       regulator-name = "LDO2";
+                       regulator-min-microvolt = <800000>;
+                       regulator-max-microvolt = <3950000>;
+               };
+       };
+
+       charger {
+               compatible = "maxim,max77836-charger";
+
+               maxim,constant-uvolt = <4350000>;
+               maxim,fast-charge-uamp = <225000>;
+               maxim,eoc-uamp = <7500>;
+               maxim,ovp-uvolt = <6500000>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt
new file mode 100644 (file)
index 0000000..7182b88
--- /dev/null
@@ -0,0 +1,64 @@
+          Qualcomm SPMI PMICs multi-function device bindings
+
+The Qualcomm SPMI series presently includes PM8941, PM8841 and PMA8084
+PMICs.  These PMICs use a QPNP scheme through SPMI interface.
+QPNP is effectively a partitioning scheme for dividing the SPMI extended
+register space up into logical pieces, and set of fixed register
+locations/definitions within these regions, with some of these regions
+specifically used for interrupt handling.
+
+The QPNP PMICs are used with the Qualcomm Snapdragon series SoCs, and are
+interfaced to the chip via the SPMI (System Power Management Interface) bus.
+Support for multiple independent functions are implemented by splitting the
+16-bit SPMI slave address space into 256 smaller fixed-size regions, 256 bytes
+each. A function can consume one or more of these fixed-size register regions.
+
+Required properties:
+- compatible:      Should contain one of:
+                     "qcom,pm8941"
+                     "qcom,pm8841"
+                     "qcom,pma8084"
+                     or generalized "qcom,spmi-pmic".
+- reg:             Specifies the SPMI USID slave address for this device.
+                   For more information see:
+                   Documentation/devicetree/bindings/spmi/spmi.txt
+
+Required properties for peripheral child nodes:
+- compatible:      Should contain "qcom,xxx", where "xxx" is a peripheral name.
+
+Optional properties for peripheral child nodes:
+- interrupts:      Interrupts are specified as a 4-tuple. For more information
+                   see:
+                   Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
+- interrupt-names: Corresponding interrupt name to the interrupts property
+
+Each child node of SPMI slave id represents a function of the PMIC. In the
+example below the rtc device node represents a peripheral of pm8941
+SID = 0. The regulator device node represents a peripheral of pm8941 SID = 1.
+
+Example:
+
+       spmi {
+               compatible = "qcom,spmi-pmic-arb";
+
+               pm8941@0 {
+                       compatible = "qcom,pm8941", "qcom,spmi-pmic";
+                       reg = <0x0 SPMI_USID>;
+
+                       rtc {
+                               compatible = "qcom,rtc";
+                               interrupts = <0x0 0x61 0x1 IRQ_TYPE_EDGE_RISING>;
+                               interrupt-names = "alarm";
+                       };
+               };
+
+               pm8941@1 {
+                       compatible = "qcom,pm8941", "qcom,spmi-pmic";
+                       reg = <0x1 SPMI_USID>;
+
+                       regulator {
+                               compatible = "qcom,regulator";
+                               regulator-name = "8941_boost";
+                       };
+               };
+       };
similarity index 98%
rename from Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
rename to Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
index 03518dc8b6bdc71257458e053e1acd275870945f..f24f334091648bbecbe3fd314ea08aaf2f0252cc 100644 (file)
@@ -61,6 +61,7 @@ The below bindings specify the set of valid subnodes.
        Definition: must be one of:
                    "qcom,pm8058-rtc"
                    "qcom,pm8921-rtc"
+                   "qcom,pm8941-rtc"
 
 - reg:
        Usage: required
diff --git a/Documentation/devicetree/bindings/mfd/rk808.txt b/Documentation/devicetree/bindings/mfd/rk808.txt
new file mode 100644 (file)
index 0000000..9e6e259
--- /dev/null
@@ -0,0 +1,177 @@
+RK808 Power Management Integrated Circuit
+
+Required properties:
+- compatible: "rockchip,rk808"
+- reg: I2C slave address
+- interrupt-parent: The parent interrupt controller.
+- interrupts: the interrupt outputs of the controller.
+- #clock-cells: from common clock binding; shall be set to 1 (multiple clock
+  outputs). See <dt-bindings/clock/rockchip,rk808.h> for clock IDs.
+
+Optional properties:
+- clock-output-names: From common clock binding to override the
+  default output clock name
+- rockchip,system-power-controller: Telling whether or not this pmic is controlling
+  the system power.
+- vcc1-supply:  The input supply for DCDC_REG1
+- vcc2-supply:  The input supply for DCDC_REG2
+- vcc3-supply:  The input supply for DCDC_REG3
+- vcc4-supply:  The input supply for DCDC_REG4
+- vcc6-supply:  The input supply for LDO_REG1 and LDO_REG2
+- vcc7-supply:  The input supply for LDO_REG3 and LDO_REG7
+- vcc8-supply:  The input supply for SWITCH_REG1
+- vcc9-supply:  The input supply for LDO_REG4 and LDO_REG5
+- vcc10-supply: The input supply for LDO_REG6
+- vcc11-supply: The input supply for LDO_REG8
+- vcc12-supply: The input supply for SWITCH_REG2
+
+Regulators: All the regulators of RK808 to be instantiated shall be
+listed in a child node named 'regulators'. Each regulator is represented
+by a child node of the 'regulators' node.
+
+       regulator-name {
+               /* standard regulator bindings here */
+       };
+
+Following regulators of the RK808 PMIC block are supported. Note that
+the 'n' in regulator name, as in DCDC_REGn or LDOn, represents the DCDC or LDO
+number as described in RK808 datasheet.
+
+       - DCDC_REGn
+               - valid values for n are 1 to 4.
+       - LDO_REGn
+               - valid values for n are 1 to 8.
+       - SWITCH_REGn
+               - valid values for n are 1 to 2
+
+Standard regulator bindings are used inside regulator subnodes. Check
+  Documentation/devicetree/bindings/regulator/regulator.txt
+for more details
+
+Example:
+       rk808: pmic@1b {
+               compatible = "rockchip,rk808";
+               clock-output-names = "xin32k", "rk808-clkout2";
+               interrupt-parent = <&gpio0>;
+               interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&pmic_int>;
+               reg = <0x1b>;
+               rockchip,system-power-controller;
+               wakeup-source;
+               #clock-cells = <1>;
+
+               vcc8-supply = <&vcc_18>;
+               vcc9-supply = <&vcc_io>;
+               vcc10-supply = <&vcc_io>;
+               vcc12-supply = <&vcc_io>;
+               vddio-supply = <&vccio_pmu>;
+
+               regulators {
+                       vdd_cpu: DCDC_REG1 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <750000>;
+                               regulator-max-microvolt = <1300000>;
+                               regulator-name = "vdd_arm";
+                       };
+
+                       vdd_gpu: DCDC_REG2 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <850000>;
+                               regulator-max-microvolt = <1250000>;
+                               regulator-name = "vdd_gpu";
+                       };
+
+                       vcc_ddr: DCDC_REG3 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-name = "vcc_ddr";
+                       };
+
+                       vcc_io: DCDC_REG4 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-name = "vcc_io";
+                       };
+
+                       vccio_pmu: LDO_REG1 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-name = "vccio_pmu";
+                       };
+
+                       vcc_tp: LDO_REG2 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-name = "vcc_tp";
+                       };
+
+                       vdd_10: LDO_REG3 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <1000000>;
+                               regulator-name = "vdd_10";
+                       };
+
+                       vcc18_lcd: LDO_REG4 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-name = "vcc18_lcd";
+                       };
+
+                       vccio_sd: LDO_REG5 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-name = "vccio_sd";
+                       };
+
+                       vdd10_lcd: LDO_REG6 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <1000000>;
+                               regulator-name = "vdd10_lcd";
+                       };
+
+                       vcc_18: LDO_REG7 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-name = "vcc_18";
+                       };
+
+                       vcca_codec: LDO_REG8 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-name = "vcca_codec";
+                       };
+
+                       vcc_wl: SWITCH_REG1 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-name = "vcc_wl";
+                       };
+
+                       vcc_lcd: SWITCH_REG2 {
+                               regulator-always-on;
+                               regulator-boot-on;
+                               regulator-name = "vcc_lcd";
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/mfd/rn5t618.txt b/Documentation/devicetree/bindings/mfd/rn5t618.txt
new file mode 100644 (file)
index 0000000..937785a
--- /dev/null
@@ -0,0 +1,36 @@
+* Ricoh RN5T618 PMIC
+
+Ricoh RN5T618 is a power management IC which integrates 3 step-down
+DCDC converters, 7 low-dropout regulators, a Li-ion battery charger,
+fuel gauge, ADC, GPIOs and a watchdog timer. It can be controlled
+through a I2C interface.
+
+Required properties:
+ - compatible: should be "ricoh,rn5t618"
+ - reg: the I2C slave address of the device
+
+Sub-nodes:
+ - regulators: the node is required if the regulator functionality is
+   needed. The valid regulator names are: DCDC1, DCDC2, DCDC3, LDO1,
+   LDO2, LDO3, LDO4, LDO5, LDORTC1 and LDORTC2.
+   The common bindings for each individual regulator can be found in:
+   Documentation/devicetree/bindings/regulator/regulator.txt
+
+Example:
+
+       pmic@32 {
+               compatible = "ricoh,rn5t618";
+               reg = <0x32>;
+
+               regulators {
+                       DCDC1 {
+                               regulator-min-microvolt = <1050000>;
+                               regulator-max-microvolt = <1050000>;
+                       };
+
+                       DCDC2 {
+                               regulator-min-microvolt = <1175000>;
+                               regulator-max-microvolt = <1175000>;
+                       };
+               };
+       };
index 56edb55206854aeba75d63ce2c85ab707208d12a..3fb68bfefc8b30eb149d8ddf36d7b0eadccb1ded 100644 (file)
@@ -13,6 +13,7 @@ Optional properties:
  - interrupt-parent             : Specifies which IRQ controller we're connected to
  - wakeup-source                : Marks the input device as wakable
  - st,autosleep-timeout         : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024
+ - irq-gpio                     : If present, which GPIO to use for event IRQ
 
 Example:
 
index b9ee7b98d3e234195ede810f6c32e6afa5a22411..3d19963312ce0a8c9e0a06f597f899df3ad08905 100644 (file)
@@ -23,8 +23,13 @@ down during off-idle. Note that this does not work on all boards
 depending on how the external oscillator is wired.
 
 Optional properties:
-- ti,use_poweroff: With this flag, the chip will initiates an ACTIVE-to-OFF or
-                  SLEEP-to-OFF transition when the system poweroffs.
+
+- ti,system-power-controller: This indicates that TWL4030 is the
+  power supply master of the system. With this flag, the chip will
+  initiate an ACTIVE-to-OFF or SLEEP-to-OFF transition when the
+  system poweroffs.
+
+- ti,use_poweroff: Deprecated name for ti,system-power-controller
 
 Example:
 &i2c1 {
diff --git a/Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt b/Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt
new file mode 100644 (file)
index 0000000..0c94c63
--- /dev/null
@@ -0,0 +1,26 @@
+Binding for the LTC2952 PowerPath controller
+
+This chip is used to externally trigger a system shut down. Once the trigger has
+been sent, the chips' watchdog has to be reset to gracefully shut down.
+If the Linux systems decides to shut down it powers off the platform via the
+kill signal.
+
+Required properties:
+
+- compatible:          Must contain: "lltc,ltc2952"
+- trigger-gpios:       phandle + gpio-specifier for the GPIO connected to the
+                       chip's trigger line
+- watchdog-gpios:      phandle + gpio-specifier for the GPIO connected to the
+                       chip's watchdog line
+- kill-gpios:          phandle + gpio-specifier for the GPIO connected to the
+                       chip's kill line
+
+Example:
+
+ltc2952 {
+       compatible = "lltc,ltc2952";
+
+       trigger-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
+       watchdog-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+       kill-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
+};
diff --git a/Documentation/devicetree/bindings/power/reset/st-reset.txt b/Documentation/devicetree/bindings/power/reset/st-reset.txt
new file mode 100644 (file)
index 0000000..809af54
--- /dev/null
@@ -0,0 +1,11 @@
+*Device-Tree bindings for ST SW reset functionality
+
+Required properties:
+- compatible: should be "st,<chip>-restart".
+- st,syscfg: should be a phandle of the syscfg node.
+
+Example node:
+       restart {
+               compatible = "st,stih416-restart";
+               st,syscfg = <&syscfg_sbc>;
+       };
diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt b/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt
new file mode 100644 (file)
index 0000000..1190631
--- /dev/null
@@ -0,0 +1,23 @@
+Generic SYSCON mapped register reset driver
+
+This is a generic reset driver using syscon to map the reset register.
+The reset is generally performed with a write to the reset register
+defined by the register map pointed by syscon reference plus the offset
+with the mask defined in the reboot node.
+
+Required properties:
+- compatible: should contain "syscon-reboot"
+- regmap: this is phandle to the register map node
+- offset: offset in the register map for the reboot register (in bytes)
+- mask: the reset value written to the reboot register (32 bit access)
+
+Default will be little endian mode, 32 bit access only.
+
+Examples:
+
+       reboot {
+          compatible = "syscon-reboot";
+          regmap = <&regmapnode>;
+          offset = <0x0>;
+          mask = <0x1>;
+       };
index 04e9f5505faa8fa4133c198a946cdefab241824d..b62bdcb1eb39fc33cb12ffb08b82dc38ecbbf88a 100644 (file)
@@ -605,11 +605,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        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.
+                       Prevents the clock framework from automatically gating
+                       clocks that have not been explicitly enabled by a Linux
+                       device driver but are enabled in hardware at reset or
+                       by the bootloader/firmware. Note that this does not
+                       force such clocks to be always-on nor does it reserve
+                       those clocks in any way. This parameter 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 b4f7f4b23f648e3e129bf3b8d31b68e17e004ee2..9ff1105e58d64fd7df6f0f17f63fc0e7f14bcc7d 100644 (file)
@@ -29,7 +29,7 @@ Charger Manager supports the following:
        While the battery is being charged and the system is in suspend-to-RAM,
        we may need to monitor the battery health by looking at the ambient or
        battery temperature. We can accomplish this by waking up the system
-       periodically. However, such a method wakes up devices unncessary for
+       periodically. However, such a method wakes up devices unnecessarily for
        monitoring the battery health and tasks, and user processes that are
        supposed to be kept suspended. That, in turn, incurs unnecessary power
        consumption and slow down charging process. Or even, such peak power
index 48cff881cb8a13aac48c4455ac44d44f685eaf46..82dacc06e355c1624ca66e5668448f1a767dbfa9 100644 (file)
@@ -101,6 +101,10 @@ VOLTAGE_MAX, VOLTAGE_MIN - same as _DESIGN voltage values except that
 these ones should be used if hardware could only guess (measure and
 retain) the thresholds of a given power supply.
 
+VOLTAGE_BOOT - Reports the voltage measured during boot
+
+CURRENT_BOOT - Reports the current measured during boot
+
 CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when
 battery considered full/empty.
 
@@ -123,6 +127,8 @@ the current drawn from a charging source.
 CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge
 condition.
 
+CALIBRATE - battery or coulomb counter calibration status
+
 CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger.
 CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the
 power supply object.
index ee1bc5bc20ad221d3d2af470ae2b55f2daf76db0..b0f17d59078eef2185695c34bbf44610d398455f 100644 (file)
@@ -6010,6 +6010,15 @@ S:       Supported
 F:     drivers/mcb/
 F:     include/linux/mcb.h
 
+MEN F21BMC (Board Management Controller)
+M:     Andreas Werner <andreas.werner@men.de>
+S:     Supported
+F:     drivers/mfd/menf21bmc.c
+F:     drivers/watchdog/menf21bmc_wdt.c
+F:     drivers/leds/leds-menf21bmc.c
+F:     drivers/hwmon/menf21bmc_hwmon.c
+F:     Documentation/hwmon/menf21bmc
+
 METAG ARCHITECTURE
 M:     James Hogan <james.hogan@imgtec.com>
 L:     linux-metag@vger.kernel.org
index 8831c48c2bc93b260d15a0cbb255d64467eb3a4d..693a3275606f2ab4c25e7647c5606ac42e88139d 100644 (file)
                        #clock-cells = <1>;
                };
 
+               cmu_dmc: clock-controller@105C0000 {
+                       compatible = "samsung,exynos3250-cmu-dmc";
+                       reg = <0x105C0000 0x2000>;
+                       #clock-cells = <1>;
+               };
+
                rtc: rtc@10070000 {
                        compatible = "samsung,exynos3250-rtc";
                        reg = <0x10070000 0x100>;
index a7054694598594cb91aefb77b791c6f3f1706018..80fc5d7e9ef9685cc04fb8771f6205aaeaf5932a 100644 (file)
@@ -1,5 +1,6 @@
 /* The pxa3xx skeleton simply augments the 2xx version */
-/include/ "pxa2xx.dtsi"
+#include "pxa2xx.dtsi"
+#include "dt-bindings/clock/pxa2xx-clock.h"
 
 / {
        model = "Marvell PXA27x familiy SoC";
                        #pwm-cells = <1>;
                };
        };
+
+       clocks {
+              /*
+               * The muxing of external clocks/internal dividers for osc* clock
+               * sources has been hidden under the carpet by now.
+               */
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               pxa2xx_clks: pxa2xx_clks@41300004 {
+                       compatible = "marvell,pxa-clocks";
+                       #clock-cells = <1>;
+                       status = "okay";
+               };
+       };
+
 };
index d73a2287b37ab7affcb826c2ad864d8eefdb0c59..531272c0e526aec013c22ec6a7d6a3a044f88467 100644 (file)
 
                mbus_clk: clk@01c2015c {
                        #clock-cells = <0>;
-                       compatible = "allwinner,sun4i-a10-mod0-clk";
+                       compatible = "allwinner,sun5i-a13-mbus-clk";
                        reg = <0x01c2015c 0x4>;
                        clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
                        clock-output-names = "mbus";
index c4b5d7825b9f78c89c0e088f07d77bc60e98e813..b131068f4f351ca92e7df07d63641cec143c5fc2 100644 (file)
 
                mbus_clk: clk@01c2015c {
                        #clock-cells = <0>;
-                       compatible = "allwinner,sun4i-a10-mod0-clk";
+                       compatible = "allwinner,sun5i-a13-mbus-clk";
                        reg = <0x01c2015c 0x4>;
                        clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
                        clock-output-names = "mbus";
index a96b9946506949d89e839310ac4980cd61111a17..82097c905c48cb5e99387db684c113fb6d37cb94 100644 (file)
 
                mbus_clk: clk@01c2015c {
                        #clock-cells = <0>;
-                       compatible = "allwinner,sun4i-a10-mod0-clk";
+                       compatible = "allwinner,sun5i-a13-mbus-clk";
                        reg = <0x01c2015c 0x4>;
                        clocks = <&osc24M>, <&pll6 2>, <&pll5 1>;
                        clock-output-names = "mbus";
index b8ad045bcb8dfbe528a40b93657e0bf373e1832d..03cbb16898a3f0eeef87144ea70f5b8d96849650 100644 (file)
@@ -723,8 +723,16 @@ int __init omap_clk_init(void)
        ti_clk_init_features();
 
        ret = of_prcm_init();
-       if (!ret)
-               ret = omap_clk_soc_init();
+       if (ret)
+               return ret;
+
+       of_clk_init(NULL);
+
+       ti_dt_clk_init_retry_clks();
+
+       ti_dt_clockdomains_setup();
+
+       ret = omap_clk_soc_init();
 
        return ret;
 }
index 74054b81360079aa835a80a68b0ddc453b393829..ee2b5222eac07f279e87a6126d7ed5a864d12d73 100644 (file)
@@ -525,8 +525,6 @@ int __init of_prcm_init(void)
                memmap_index++;
        }
 
-       ti_dt_clockdomains_setup();
-
        return 0;
 }
 
index ee6ced1cea7f1705cda89be3bff0b3d51356e4cf..f1dd62946b3699f6f25b7dd2683d98de9df141a1 100644 (file)
 #define CCCR_M_MASK    0x0060  /* Memory Frequency to Run Mode Frequency Multiplier */
 #define CCCR_L_MASK    0x001f  /* Crystal Frequency to Memory Frequency Multiplier */
 
+#define CCCR_CPDIS_BIT (31)
+#define CCCR_PPDIS_BIT (30)
+#define CCCR_LCD_26_BIT        (27)
+#define CCCR_A_BIT     (25)
+
+#define CCSR_N2_MASK   CCCR_N_MASK
+#define CCSR_M_MASK    CCCR_M_MASK
+#define CCSR_L_MASK    CCCR_L_MASK
+#define CCSR_N2_SHIFT  7
+
 #define CKEN_AC97CONF   (31)    /* AC97 Controller Configuration */
 #define CKEN_CAMERA    (24)    /* Camera Interface Clock Enable */
 #define CKEN_SSP1      (23)    /* SSP1 Unit Clock Enable */
index 28b808c73e8ed14344472188794b94a14efea72f..14d162952c3bc21bdadf3966ccef0c7b2c43df91 100644 (file)
@@ -1211,6 +1211,9 @@ void device_del(struct device *dev)
         */
        if (platform_notify_remove)
                platform_notify_remove(dev);
+       if (dev->bus)
+               blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+                                            BUS_NOTIFY_REMOVED_DEVICE, dev);
        kobject_uevent(&dev->kobj, KOBJ_REMOVE);
        cleanup_device_parent(dev);
        kobject_del(&dev->kobj);
index 4b97baf8afa317347ca7ff531f2d8828394084a2..0a54c588e433751f39084459193a5da7700d34db 100644 (file)
@@ -210,6 +210,12 @@ enum obj_request_type {
        OBJ_REQUEST_NODATA, OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES
 };
 
+enum obj_operation_type {
+       OBJ_OP_WRITE,
+       OBJ_OP_READ,
+       OBJ_OP_DISCARD,
+};
+
 enum obj_req_flags {
        OBJ_REQ_DONE,           /* completion flag: not done = 0, done = 1 */
        OBJ_REQ_IMG_DATA,       /* object usage: standalone = 0, image = 1 */
@@ -276,6 +282,7 @@ enum img_req_flags {
        IMG_REQ_WRITE,          /* I/O direction: read = 0, write = 1 */
        IMG_REQ_CHILD,          /* initiator: block = 0, child image = 1 */
        IMG_REQ_LAYERED,        /* ENOENT handling: normal = 0, layered = 1 */
+       IMG_REQ_DISCARD,        /* discard: normal = 0, discard request = 1 */
 };
 
 struct rbd_img_request {
@@ -785,6 +792,20 @@ static int parse_rbd_opts_token(char *c, void *private)
        return 0;
 }
 
+static char* obj_op_name(enum obj_operation_type op_type)
+{
+       switch (op_type) {
+       case OBJ_OP_READ:
+               return "read";
+       case OBJ_OP_WRITE:
+               return "write";
+       case OBJ_OP_DISCARD:
+               return "discard";
+       default:
+               return "???";
+       }
+}
+
 /*
  * Get a ceph client with specific addr and configuration, if one does
  * not exist create it.  Either way, ceph_opts is consumed by this
@@ -1600,6 +1621,21 @@ static bool img_request_write_test(struct rbd_img_request *img_request)
        return test_bit(IMG_REQ_WRITE, &img_request->flags) != 0;
 }
 
+/*
+ * Set the discard flag when the img_request is an discard request
+ */
+static void img_request_discard_set(struct rbd_img_request *img_request)
+{
+       set_bit(IMG_REQ_DISCARD, &img_request->flags);
+       smp_mb();
+}
+
+static bool img_request_discard_test(struct rbd_img_request *img_request)
+{
+       smp_mb();
+       return test_bit(IMG_REQ_DISCARD, &img_request->flags) != 0;
+}
+
 static void img_request_child_set(struct rbd_img_request *img_request)
 {
        set_bit(IMG_REQ_CHILD, &img_request->flags);
@@ -1636,6 +1672,17 @@ static bool img_request_layered_test(struct rbd_img_request *img_request)
        return test_bit(IMG_REQ_LAYERED, &img_request->flags) != 0;
 }
 
+static enum obj_operation_type
+rbd_img_request_op_type(struct rbd_img_request *img_request)
+{
+       if (img_request_write_test(img_request))
+               return OBJ_OP_WRITE;
+       else if (img_request_discard_test(img_request))
+               return OBJ_OP_DISCARD;
+       else
+               return OBJ_OP_READ;
+}
+
 static void
 rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request)
 {
@@ -1722,6 +1769,21 @@ static void rbd_osd_write_callback(struct rbd_obj_request *obj_request)
        obj_request_done_set(obj_request);
 }
 
+static void rbd_osd_discard_callback(struct rbd_obj_request *obj_request)
+{
+       dout("%s: obj %p result %d %llu\n", __func__, obj_request,
+               obj_request->result, obj_request->length);
+       /*
+        * There is no such thing as a successful short discard.  Set
+        * it to our originally-requested length.
+        */
+       obj_request->xferred = obj_request->length;
+       /* discarding a non-existent object is not a problem */
+       if (obj_request->result == -ENOENT)
+               obj_request->result = 0;
+       obj_request_done_set(obj_request);
+}
+
 /*
  * For a simple stat call there's nothing to do.  We'll do more if
  * this is part of a write sequence for a layered image.
@@ -1773,6 +1835,11 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req,
        case CEPH_OSD_OP_STAT:
                rbd_osd_stat_callback(obj_request);
                break;
+       case CEPH_OSD_OP_DELETE:
+       case CEPH_OSD_OP_TRUNCATE:
+       case CEPH_OSD_OP_ZERO:
+               rbd_osd_discard_callback(obj_request);
+               break;
        case CEPH_OSD_OP_CALL:
        case CEPH_OSD_OP_NOTIFY_ACK:
        case CEPH_OSD_OP_WATCH:
@@ -1823,7 +1890,7 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request)
  */
 static struct ceph_osd_request *rbd_osd_req_create(
                                        struct rbd_device *rbd_dev,
-                                       bool write_request,
+                                       enum obj_operation_type op_type,
                                        unsigned int num_ops,
                                        struct rbd_obj_request *obj_request)
 {
@@ -1831,16 +1898,18 @@ static struct ceph_osd_request *rbd_osd_req_create(
        struct ceph_osd_client *osdc;
        struct ceph_osd_request *osd_req;
 
-       if (obj_request_img_data_test(obj_request)) {
+       if (obj_request_img_data_test(obj_request) &&
+               (op_type == OBJ_OP_DISCARD || op_type == OBJ_OP_WRITE)) {
                struct rbd_img_request *img_request = obj_request->img_request;
-
-               rbd_assert(write_request ==
-                               img_request_write_test(img_request));
-               if (write_request)
-                       snapc = img_request->snapc;
+               if (op_type == OBJ_OP_WRITE) {
+                       rbd_assert(img_request_write_test(img_request));
+               } else {
+                       rbd_assert(img_request_discard_test(img_request));
+               }
+               snapc = img_request->snapc;
        }
 
-       rbd_assert(num_ops == 1 || (write_request && num_ops == 2));
+       rbd_assert(num_ops == 1 || ((op_type == OBJ_OP_WRITE) && num_ops == 2));
 
        /* Allocate and initialize the request, for the num_ops ops */
 
@@ -1850,7 +1919,7 @@ static struct ceph_osd_request *rbd_osd_req_create(
        if (!osd_req)
                return NULL;    /* ENOMEM */
 
-       if (write_request)
+       if (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD)
                osd_req->r_flags = CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK;
        else
                osd_req->r_flags = CEPH_OSD_FLAG_READ;
@@ -1865,9 +1934,10 @@ static struct ceph_osd_request *rbd_osd_req_create(
 }
 
 /*
- * Create a copyup osd request based on the information in the
- * object request supplied.  A copyup request has three osd ops,
- * a copyup method call, a hint op, and a write op.
+ * Create a copyup osd request based on the information in the object
+ * request supplied.  A copyup request has two or three osd ops, a
+ * copyup method call, potentially a hint op, and a write or truncate
+ * or zero op.
  */
 static struct ceph_osd_request *
 rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
@@ -1877,18 +1947,24 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
        struct rbd_device *rbd_dev;
        struct ceph_osd_client *osdc;
        struct ceph_osd_request *osd_req;
+       int num_osd_ops = 3;
 
        rbd_assert(obj_request_img_data_test(obj_request));
        img_request = obj_request->img_request;
        rbd_assert(img_request);
-       rbd_assert(img_request_write_test(img_request));
+       rbd_assert(img_request_write_test(img_request) ||
+                       img_request_discard_test(img_request));
+
+       if (img_request_discard_test(img_request))
+               num_osd_ops = 2;
 
-       /* Allocate and initialize the request, for the three ops */
+       /* Allocate and initialize the request, for all the ops */
 
        snapc = img_request->snapc;
        rbd_dev = img_request->rbd_dev;
        osdc = &rbd_dev->rbd_client->client->osdc;
-       osd_req = ceph_osdc_alloc_request(osdc, snapc, 3, false, GFP_ATOMIC);
+       osd_req = ceph_osdc_alloc_request(osdc, snapc, num_osd_ops,
+                                               false, GFP_ATOMIC);
        if (!osd_req)
                return NULL;    /* ENOMEM */
 
@@ -2057,7 +2133,8 @@ static bool rbd_dev_parent_get(struct rbd_device *rbd_dev)
 static struct rbd_img_request *rbd_img_request_create(
                                        struct rbd_device *rbd_dev,
                                        u64 offset, u64 length,
-                                       bool write_request)
+                                       enum obj_operation_type op_type,
+                                       struct ceph_snap_context *snapc)
 {
        struct rbd_img_request *img_request;
 
@@ -2065,20 +2142,17 @@ static struct rbd_img_request *rbd_img_request_create(
        if (!img_request)
                return NULL;
 
-       if (write_request) {
-               down_read(&rbd_dev->header_rwsem);
-               ceph_get_snap_context(rbd_dev->header.snapc);
-               up_read(&rbd_dev->header_rwsem);
-       }
-
        img_request->rq = NULL;
        img_request->rbd_dev = rbd_dev;
        img_request->offset = offset;
        img_request->length = length;
        img_request->flags = 0;
-       if (write_request) {
+       if (op_type == OBJ_OP_DISCARD) {
+               img_request_discard_set(img_request);
+               img_request->snapc = snapc;
+       } else if (op_type == OBJ_OP_WRITE) {
                img_request_write_set(img_request);
-               img_request->snapc = rbd_dev->header.snapc;
+               img_request->snapc = snapc;
        } else {
                img_request->snap_id = rbd_dev->spec->snap_id;
        }
@@ -2093,8 +2167,7 @@ static struct rbd_img_request *rbd_img_request_create(
        kref_init(&img_request->kref);
 
        dout("%s: rbd_dev %p %s %llu/%llu -> img %p\n", __func__, rbd_dev,
-               write_request ? "write" : "read", offset, length,
-               img_request);
+               obj_op_name(op_type), offset, length, img_request);
 
        return img_request;
 }
@@ -2118,7 +2191,8 @@ static void rbd_img_request_destroy(struct kref *kref)
                rbd_dev_parent_put(img_request->rbd_dev);
        }
 
-       if (img_request_write_test(img_request))
+       if (img_request_write_test(img_request) ||
+               img_request_discard_test(img_request))
                ceph_put_snap_context(img_request->snapc);
 
        kmem_cache_free(rbd_img_request_cache, img_request);
@@ -2134,8 +2208,8 @@ static struct rbd_img_request *rbd_parent_request_create(
        rbd_assert(obj_request->img_request);
        rbd_dev = obj_request->img_request->rbd_dev;
 
-       parent_request = rbd_img_request_create(rbd_dev->parent,
-                                               img_offset, length, false);
+       parent_request = rbd_img_request_create(rbd_dev->parent, img_offset,
+                                               length, OBJ_OP_READ, NULL);
        if (!parent_request)
                return NULL;
 
@@ -2176,11 +2250,18 @@ static bool rbd_img_obj_end_request(struct rbd_obj_request *obj_request)
        result = obj_request->result;
        if (result) {
                struct rbd_device *rbd_dev = img_request->rbd_dev;
+               enum obj_operation_type op_type;
+
+               if (img_request_discard_test(img_request))
+                       op_type = OBJ_OP_DISCARD;
+               else if (img_request_write_test(img_request))
+                       op_type = OBJ_OP_WRITE;
+               else
+                       op_type = OBJ_OP_READ;
 
                rbd_warn(rbd_dev, "%s %llx at %llx (%llx)",
-                       img_request_write_test(img_request) ? "write" : "read",
-                       obj_request->length, obj_request->img_offset,
-                       obj_request->offset);
+                       obj_op_name(op_type), obj_request->length,
+                       obj_request->img_offset, obj_request->offset);
                rbd_warn(rbd_dev, "  result %d xferred %x",
                        result, xferred);
                if (!img_request->result)
@@ -2244,6 +2325,67 @@ out:
                rbd_img_request_complete(img_request);
 }
 
+/*
+ * Add individual osd ops to the given ceph_osd_request and prepare
+ * them for submission. num_ops is the current number of
+ * osd operations already to the object request.
+ */
+static void rbd_img_obj_request_fill(struct rbd_obj_request *obj_request,
+                               struct ceph_osd_request *osd_request,
+                               enum obj_operation_type op_type,
+                               unsigned int num_ops)
+{
+       struct rbd_img_request *img_request = obj_request->img_request;
+       struct rbd_device *rbd_dev = img_request->rbd_dev;
+       u64 object_size = rbd_obj_bytes(&rbd_dev->header);
+       u64 offset = obj_request->offset;
+       u64 length = obj_request->length;
+       u64 img_end;
+       u16 opcode;
+
+       if (op_type == OBJ_OP_DISCARD) {
+               if (!offset && length == object_size &&
+                   (!img_request_layered_test(img_request) ||
+                    !obj_request_overlaps_parent(obj_request))) {
+                       opcode = CEPH_OSD_OP_DELETE;
+               } else if ((offset + length == object_size)) {
+                       opcode = CEPH_OSD_OP_TRUNCATE;
+               } else {
+                       down_read(&rbd_dev->header_rwsem);
+                       img_end = rbd_dev->header.image_size;
+                       up_read(&rbd_dev->header_rwsem);
+
+                       if (obj_request->img_offset + length == img_end)
+                               opcode = CEPH_OSD_OP_TRUNCATE;
+                       else
+                               opcode = CEPH_OSD_OP_ZERO;
+               }
+       } else if (op_type == OBJ_OP_WRITE) {
+               opcode = CEPH_OSD_OP_WRITE;
+               osd_req_op_alloc_hint_init(osd_request, num_ops,
+                                       object_size, object_size);
+               num_ops++;
+       } else {
+               opcode = CEPH_OSD_OP_READ;
+       }
+
+       osd_req_op_extent_init(osd_request, num_ops, opcode, offset, length,
+                               0, 0);
+       if (obj_request->type == OBJ_REQUEST_BIO)
+               osd_req_op_extent_osd_data_bio(osd_request, num_ops,
+                                       obj_request->bio_list, length);
+       else if (obj_request->type == OBJ_REQUEST_PAGES)
+               osd_req_op_extent_osd_data_pages(osd_request, num_ops,
+                                       obj_request->pages, length,
+                                       offset & ~PAGE_MASK, false, false);
+
+       /* Discards are also writes */
+       if (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD)
+               rbd_osd_req_format_write(obj_request);
+       else
+               rbd_osd_req_format_read(obj_request);
+}
+
 /*
  * Split up an image request into one or more object requests, each
  * to a different object.  The "type" parameter indicates whether
@@ -2259,28 +2401,26 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
        struct rbd_device *rbd_dev = img_request->rbd_dev;
        struct rbd_obj_request *obj_request = NULL;
        struct rbd_obj_request *next_obj_request;
-       bool write_request = img_request_write_test(img_request);
        struct bio *bio_list = NULL;
        unsigned int bio_offset = 0;
        struct page **pages = NULL;
+       enum obj_operation_type op_type;
        u64 img_offset;
        u64 resid;
-       u16 opcode;
 
        dout("%s: img %p type %d data_desc %p\n", __func__, img_request,
                (int)type, data_desc);
 
-       opcode = write_request ? CEPH_OSD_OP_WRITE : CEPH_OSD_OP_READ;
        img_offset = img_request->offset;
        resid = img_request->length;
        rbd_assert(resid > 0);
+       op_type = rbd_img_request_op_type(img_request);
 
        if (type == OBJ_REQUEST_BIO) {
                bio_list = data_desc;
                rbd_assert(img_offset ==
                           bio_list->bi_iter.bi_sector << SECTOR_SHIFT);
-       } else {
-               rbd_assert(type == OBJ_REQUEST_PAGES);
+       } else if (type == OBJ_REQUEST_PAGES) {
                pages = data_desc;
        }
 
@@ -2289,7 +2429,6 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
                const char *object_name;
                u64 offset;
                u64 length;
-               unsigned int which = 0;
 
                object_name = rbd_segment_name(rbd_dev, img_offset);
                if (!object_name)
@@ -2321,7 +2460,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
                                                                GFP_ATOMIC);
                        if (!obj_request->bio_list)
                                goto out_unwind;
-               } else {
+               } else if (type == OBJ_REQUEST_PAGES) {
                        unsigned int page_count;
 
                        obj_request->pages = pages;
@@ -2332,38 +2471,19 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
                        pages += page_count;
                }
 
-               osd_req = rbd_osd_req_create(rbd_dev, write_request,
-                                            (write_request ? 2 : 1),
-                                            obj_request);
+               osd_req = rbd_osd_req_create(rbd_dev, op_type,
+                                       (op_type == OBJ_OP_WRITE) ? 2 : 1,
+                                       obj_request);
                if (!osd_req)
                        goto out_unwind;
+
                obj_request->osd_req = osd_req;
                obj_request->callback = rbd_img_obj_callback;
-               rbd_img_request_get(img_request);
-
-               if (write_request) {
-                       osd_req_op_alloc_hint_init(osd_req, which,
-                                            rbd_obj_bytes(&rbd_dev->header),
-                                            rbd_obj_bytes(&rbd_dev->header));
-                       which++;
-               }
-
-               osd_req_op_extent_init(osd_req, which, opcode, offset, length,
-                                      0, 0);
-               if (type == OBJ_REQUEST_BIO)
-                       osd_req_op_extent_osd_data_bio(osd_req, which,
-                                       obj_request->bio_list, length);
-               else
-                       osd_req_op_extent_osd_data_pages(osd_req, which,
-                                       obj_request->pages, length,
-                                       offset & ~PAGE_MASK, false, false);
+               obj_request->img_offset = img_offset;
 
-               if (write_request)
-                       rbd_osd_req_format_write(obj_request);
-               else
-                       rbd_osd_req_format_read(obj_request);
+               rbd_img_obj_request_fill(obj_request, osd_req, op_type, 0);
 
-               obj_request->img_offset = img_offset;
+               rbd_img_request_get(img_request);
 
                img_offset += length;
                resid -= length;
@@ -2386,7 +2506,8 @@ rbd_img_obj_copyup_callback(struct rbd_obj_request *obj_request)
        struct page **pages;
        u32 page_count;
 
-       rbd_assert(obj_request->type == OBJ_REQUEST_BIO);
+       rbd_assert(obj_request->type == OBJ_REQUEST_BIO ||
+               obj_request->type == OBJ_REQUEST_NODATA);
        rbd_assert(obj_request_img_data_test(obj_request));
        img_request = obj_request->img_request;
        rbd_assert(img_request);
@@ -2424,11 +2545,10 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)
        struct ceph_osd_client *osdc;
        struct rbd_device *rbd_dev;
        struct page **pages;
+       enum obj_operation_type op_type;
        u32 page_count;
        int img_result;
        u64 parent_length;
-       u64 offset;
-       u64 length;
 
        rbd_assert(img_request_child_test(img_request));
 
@@ -2492,26 +2612,10 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)
        osd_req_op_cls_request_data_pages(osd_req, 0, pages, parent_length, 0,
                                                false, false);
 
-       /* Then the hint op */
+       /* Add the other op(s) */
 
-       osd_req_op_alloc_hint_init(osd_req, 1, rbd_obj_bytes(&rbd_dev->header),
-                                  rbd_obj_bytes(&rbd_dev->header));
-
-       /* And the original write request op */
-
-       offset = orig_request->offset;
-       length = orig_request->length;
-       osd_req_op_extent_init(osd_req, 2, CEPH_OSD_OP_WRITE,
-                                       offset, length, 0, 0);
-       if (orig_request->type == OBJ_REQUEST_BIO)
-               osd_req_op_extent_osd_data_bio(osd_req, 2,
-                                       orig_request->bio_list, length);
-       else
-               osd_req_op_extent_osd_data_pages(osd_req, 2,
-                                       orig_request->pages, length,
-                                       offset & ~PAGE_MASK, false, false);
-
-       rbd_osd_req_format_write(orig_request);
+       op_type = rbd_img_request_op_type(orig_request->img_request);
+       rbd_img_obj_request_fill(orig_request, osd_req, op_type, 1);
 
        /* All set, send it off. */
 
@@ -2728,7 +2832,7 @@ static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request)
 
        rbd_assert(obj_request->img_request);
        rbd_dev = obj_request->img_request->rbd_dev;
-       stat_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+       stat_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1,
                                                   stat_request);
        if (!stat_request->osd_req)
                goto out;
@@ -2748,11 +2852,10 @@ out:
        return ret;
 }
 
-static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request)
+static bool img_obj_request_simple(struct rbd_obj_request *obj_request)
 {
        struct rbd_img_request *img_request;
        struct rbd_device *rbd_dev;
-       bool known;
 
        rbd_assert(obj_request_img_data_test(obj_request));
 
@@ -2760,22 +2863,44 @@ static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request)
        rbd_assert(img_request);
        rbd_dev = img_request->rbd_dev;
 
+       /* Reads */
+       if (!img_request_write_test(img_request) &&
+           !img_request_discard_test(img_request))
+               return true;
+
+       /* Non-layered writes */
+       if (!img_request_layered_test(img_request))
+               return true;
+
+       /*
+        * Layered writes outside of the parent overlap range don't
+        * share any data with the parent.
+        */
+       if (!obj_request_overlaps_parent(obj_request))
+               return true;
+
        /*
-        * Only writes to layered images need special handling.
-        * Reads and non-layered writes are simple object requests.
-        * Layered writes that start beyond the end of the overlap
-        * with the parent have no parent data, so they too are
-        * simple object requests.  Finally, if the target object is
-        * known to already exist, its parent data has already been
-        * copied, so a write to the object can also be handled as a
-        * simple object request.
+        * Entire-object layered writes - we will overwrite whatever
+        * parent data there is anyway.
         */
-       if (!img_request_write_test(img_request) ||
-               !img_request_layered_test(img_request) ||
-               !obj_request_overlaps_parent(obj_request) ||
-               ((known = obj_request_known_test(obj_request)) &&
-                       obj_request_exists_test(obj_request))) {
+       if (!obj_request->offset &&
+           obj_request->length == rbd_obj_bytes(&rbd_dev->header))
+               return true;
+
+       /*
+        * If the object is known to already exist, its parent data has
+        * already been copied.
+        */
+       if (obj_request_known_test(obj_request) &&
+           obj_request_exists_test(obj_request))
+               return true;
+
+       return false;
+}
 
+static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request)
+{
+       if (img_obj_request_simple(obj_request)) {
                struct rbd_device *rbd_dev;
                struct ceph_osd_client *osdc;
 
@@ -2791,7 +2916,7 @@ static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request)
         * start by reading the data for the full target object from
         * the parent so we can use it for a copyup to the target.
         */
-       if (known)
+       if (obj_request_known_test(obj_request))
                return rbd_img_obj_parent_read_full(obj_request);
 
        /* We don't know whether the target exists.  Go find out. */
@@ -2932,7 +3057,7 @@ static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, u64 notify_id)
                return -ENOMEM;
 
        ret = -ENOMEM;
-       obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+       obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1,
                                                  obj_request);
        if (!obj_request->osd_req)
                goto out;
@@ -2995,7 +3120,7 @@ static struct rbd_obj_request *rbd_obj_watch_request_helper(
        if (!obj_request)
                return ERR_PTR(-ENOMEM);
 
-       obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
+       obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_WRITE, 1,
                                                  obj_request);
        if (!obj_request->osd_req) {
                ret = -ENOMEM;
@@ -3133,7 +3258,7 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev,
        obj_request->pages = pages;
        obj_request->page_count = page_count;
 
-       obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+       obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1,
                                                  obj_request);
        if (!obj_request->osd_req)
                goto out;
@@ -3183,11 +3308,20 @@ out:
 static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq)
 {
        struct rbd_img_request *img_request;
+       struct ceph_snap_context *snapc = NULL;
        u64 offset = (u64)blk_rq_pos(rq) << SECTOR_SHIFT;
        u64 length = blk_rq_bytes(rq);
-       bool wr = rq_data_dir(rq) == WRITE;
+       enum obj_operation_type op_type;
+       u64 mapping_size;
        int result;
 
+       if (rq->cmd_flags & REQ_DISCARD)
+               op_type = OBJ_OP_DISCARD;
+       else if (rq->cmd_flags & REQ_WRITE)
+               op_type = OBJ_OP_WRITE;
+       else
+               op_type = OBJ_OP_READ;
+
        /* Ignore/skip any zero-length requests */
 
        if (!length) {
@@ -3196,9 +3330,9 @@ static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq)
                goto err_rq;
        }
 
-       /* Disallow writes to a read-only device */
+       /* Only reads are allowed to a read-only device */
 
-       if (wr) {
+       if (op_type != OBJ_OP_READ) {
                if (rbd_dev->mapping.read_only) {
                        result = -EROFS;
                        goto err_rq;
@@ -3226,21 +3360,35 @@ static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq)
                goto err_rq;    /* Shouldn't happen */
        }
 
-       if (offset + length > rbd_dev->mapping.size) {
+       down_read(&rbd_dev->header_rwsem);
+       mapping_size = rbd_dev->mapping.size;
+       if (op_type != OBJ_OP_READ) {
+               snapc = rbd_dev->header.snapc;
+               ceph_get_snap_context(snapc);
+       }
+       up_read(&rbd_dev->header_rwsem);
+
+       if (offset + length > mapping_size) {
                rbd_warn(rbd_dev, "beyond EOD (%llu~%llu > %llu)", offset,
-                        length, rbd_dev->mapping.size);
+                        length, mapping_size);
                result = -EIO;
                goto err_rq;
        }
 
-       img_request = rbd_img_request_create(rbd_dev, offset, length, wr);
+       img_request = rbd_img_request_create(rbd_dev, offset, length, op_type,
+                                            snapc);
        if (!img_request) {
                result = -ENOMEM;
                goto err_rq;
        }
        img_request->rq = rq;
 
-       result = rbd_img_request_fill(img_request, OBJ_REQUEST_BIO, rq->bio);
+       if (op_type == OBJ_OP_DISCARD)
+               result = rbd_img_request_fill(img_request, OBJ_REQUEST_NODATA,
+                                             NULL);
+       else
+               result = rbd_img_request_fill(img_request, OBJ_REQUEST_BIO,
+                                             rq->bio);
        if (result)
                goto err_img_request;
 
@@ -3255,7 +3403,9 @@ err_img_request:
 err_rq:
        if (result)
                rbd_warn(rbd_dev, "%s %llx at %llx result %d",
-                        wr ? "write" : "read", length, offset, result);
+                        obj_op_name(op_type), length, offset, result);
+       if (snapc)
+               ceph_put_snap_context(snapc);
        blk_end_request_all(rq, result);
 }
 
@@ -3393,7 +3543,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev,
        obj_request->pages = pages;
        obj_request->page_count = page_count;
 
-       obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+       obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1,
                                                  obj_request);
        if (!obj_request->osd_req)
                goto out;
@@ -3610,6 +3760,13 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
        blk_queue_io_min(q, segment_size);
        blk_queue_io_opt(q, segment_size);
 
+       /* enable the discard support */
+       queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+       q->limits.discard_granularity = segment_size;
+       q->limits.discard_alignment = segment_size;
+       q->limits.max_discard_sectors = segment_size / SECTOR_SIZE;
+       q->limits.discard_zeroes_data = 1;
+
        blk_queue_merge_bvec(q, rbd_merge_bvec);
        disk->queue = q;
 
@@ -4924,7 +5081,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev)
                ret = image_id ? 0 : -ENOMEM;
                if (!ret)
                        rbd_dev->image_format = 1;
-       } else if (ret > sizeof (__le32)) {
+       } else if (ret >= 0) {
                void *p = response;
 
                image_id = ceph_extract_encoded_string(&p, p + ret,
@@ -4932,8 +5089,6 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev)
                ret = PTR_ERR_OR_ZERO(image_id);
                if (!ret)
                        rbd_dev->image_format = 2;
-       } else {
-               ret = -EINVAL;
        }
 
        if (!ret) {
@@ -5087,7 +5242,8 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
        set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only);
 
-       rbd_dev->rq_wq = alloc_workqueue("%s", 0, 0, rbd_dev->disk->disk_name);
+       rbd_dev->rq_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
+                                        rbd_dev->disk->disk_name);
        if (!rbd_dev->rq_wq) {
                ret = -ENOMEM;
                goto err_out_mapping;
index 84e0590e31dc754fd1562d8bd99d2c3273090f3f..455fd17d938eaa6525f4af01e0d0d2f786bd08d8 100644 (file)
@@ -32,12 +32,23 @@ config COMMON_CLK_WM831X
 
 source "drivers/clk/versatile/Kconfig"
 
+config COMMON_CLK_MAX_GEN
+        bool
+
 config COMMON_CLK_MAX77686
        tristate "Clock driver for Maxim 77686 MFD"
        depends on MFD_MAX77686
+       select COMMON_CLK_MAX_GEN
        ---help---
          This driver supports Maxim 77686 crystal oscillator clock. 
 
+config COMMON_CLK_MAX77802
+       tristate "Clock driver for Maxim 77802 PMIC"
+       depends on MFD_MAX77686
+       select COMMON_CLK_MAX_GEN
+       ---help---
+         This driver supports Maxim 77802 crystal oscillator clock.
+
 config COMMON_CLK_RK808
        tristate "Clock driver for RK808"
        depends on MFD_RK808
@@ -118,6 +129,11 @@ config COMMON_CLK_PALMAS
          This driver supports TI Palmas devices 32KHz output KG and KG_AUDIO
          using common clock framework.
 
+config COMMON_CLK_PXA
+       def_bool COMMON_CLK && ARCH_PXA
+       ---help---
+         Sypport for the Marvell PXA SoC.
+
 source "drivers/clk/qcom/Kconfig"
 
 endmenu
index 99f53d5f876618be908525c3cbc4b9c3803119a2..d5fba5bc6e1bc1f07991be58f367c30a253a58f7 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_COMMON_CLK)        += clk-gate.o
 obj-$(CONFIG_COMMON_CLK)       += clk-mux.o
 obj-$(CONFIG_COMMON_CLK)       += clk-composite.o
 obj-$(CONFIG_COMMON_CLK)       += clk-fractional-divider.o
+obj-$(CONFIG_COMMON_CLK)       += clk-gpio-gate.o
 ifeq ($(CONFIG_OF), y)
 obj-$(CONFIG_COMMON_CLK)       += clk-conf.o
 endif
@@ -22,7 +23,9 @@ obj-$(CONFIG_ARCH_CLPS711X)           += clk-clps711x.o
 obj-$(CONFIG_ARCH_EFM32)               += clk-efm32gg.o
 obj-$(CONFIG_ARCH_HIGHBANK)            += clk-highbank.o
 obj-$(CONFIG_MACH_LOONGSON1)           += clk-ls1x.o
+obj-$(CONFIG_COMMON_CLK_MAX_GEN)       += clk-max-gen.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)      += clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_MAX77802)      += clk-max77802.o
 obj-$(CONFIG_ARCH_MOXART)              += clk-moxart.o
 obj-$(CONFIG_ARCH_NOMADIK)             += clk-nomadik.o
 obj-$(CONFIG_ARCH_NSPIRE)              += clk-nspire.o
@@ -49,6 +52,7 @@ obj-$(CONFIG_ARCH_MMP)                        += mmp/
 endif
 obj-$(CONFIG_PLAT_ORION)               += mvebu/
 obj-$(CONFIG_ARCH_MXS)                 += mxs/
+obj-$(CONFIG_COMMON_CLK_PXA)           += pxa/
 obj-$(CONFIG_COMMON_CLK_QCOM)          += qcom/
 obj-$(CONFIG_ARCH_ROCKCHIP)            += rockchip/
 obj-$(CONFIG_COMMON_CLK_SAMSUNG)       += samsung/
index cf6ed023504cbc4afbf98c523b7e48e09fab0d56..6ec79dbc0840ad8940e9e9ab599a0f865f1cd881 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/io.h>
+#include <linux/kernel.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #define PLL_DIV(reg)           ((reg) & PLL_DIV_MASK)
 #define PLL_MUL(reg, layout)   (((reg) >> (layout)->mul_shift) & \
                                 (layout)->mul_mask)
+#define PLL_MUL_MIN            2
+#define PLL_MUL_MASK(layout)   ((layout)->mul_mask)
+#define PLL_MUL_MAX(layout)    (PLL_MUL_MASK(layout) + 1)
 #define PLL_ICPR_SHIFT(id)     ((id) * 16)
 #define PLL_ICPR_MASK(id)      (0xffff << PLL_ICPR_SHIFT(id))
-#define PLL_MAX_COUNT          0x3ff
+#define PLL_MAX_COUNT          0x3f
 #define PLL_COUNT_SHIFT                8
 #define PLL_OUT_SHIFT          14
 #define PLL_MAX_ID             1
@@ -147,115 +151,113 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
                                         unsigned long parent_rate)
 {
        struct clk_pll *pll = to_clk_pll(hw);
-       const struct clk_pll_layout *layout = pll->layout;
-       struct at91_pmc *pmc = pll->pmc;
-       int offset = PLL_REG(pll->id);
-       u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask;
-       u8 div = PLL_DIV(tmp);
-       u16 mul = PLL_MUL(tmp, layout);
-       if (!div || !mul)
+
+       if (!pll->div || !pll->mul)
                return 0;
 
-       return (parent_rate * (mul + 1)) / div;
+       return (parent_rate / pll->div) * (pll->mul + 1);
 }
 
 static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
                                     unsigned long parent_rate,
                                     u32 *div, u32 *mul,
                                     u32 *index) {
-       unsigned long maxrate;
-       unsigned long minrate;
-       unsigned long divrate;
-       unsigned long bestdiv = 1;
-       unsigned long bestmul;
-       unsigned long tmpdiv;
-       unsigned long roundup;
-       unsigned long rounddown;
-       unsigned long remainder;
-       unsigned long bestremainder;
-       unsigned long maxmul;
-       unsigned long maxdiv;
-       unsigned long mindiv;
-       int i = 0;
        const struct clk_pll_layout *layout = pll->layout;
        const struct clk_pll_characteristics *characteristics =
                                                        pll->characteristics;
+       unsigned long bestremainder = ULONG_MAX;
+       unsigned long maxdiv, mindiv, tmpdiv;
+       long bestrate = -ERANGE;
+       unsigned long bestdiv;
+       unsigned long bestmul;
+       int i = 0;
 
-       /* Minimum divider = 1 */
-       /* Maximum multiplier = max_mul */
-       maxmul = layout->mul_mask + 1;
-       maxrate = (parent_rate * maxmul) / 1;
-
-       /* Maximum divider = max_div */
-       /* Minimum multiplier = 2 */
-       maxdiv = PLL_DIV_MAX;
-       minrate = (parent_rate * 2) / maxdiv;
-
+       /* Check if parent_rate is a valid input rate */
        if (parent_rate < characteristics->input.min ||
-           parent_rate < characteristics->input.max)
-               return -ERANGE;
-
-       if (parent_rate < minrate || parent_rate > maxrate)
+           parent_rate > characteristics->input.max)
                return -ERANGE;
 
-       for (i = 0; i < characteristics->num_output; i++) {
-               if (parent_rate >= characteristics->output[i].min &&
-                   parent_rate <= characteristics->output[i].max)
-                       break;
-       }
-
-       if (i >= characteristics->num_output)
-               return -ERANGE;
-
-       bestmul = rate / parent_rate;
-       rounddown = parent_rate % rate;
-       roundup = rate - rounddown;
-       bestremainder = roundup < rounddown ? roundup : rounddown;
-
-       if (!bestremainder) {
-               if (div)
-                       *div = bestdiv;
-               if (mul)
-                       *mul = bestmul;
-               if (index)
-                       *index = i;
-               return rate;
-       }
-
-       maxdiv = 255 / (bestmul + 1);
-       if (parent_rate / maxdiv < characteristics->input.min)
-               maxdiv = parent_rate / characteristics->input.min;
-       mindiv = parent_rate / characteristics->input.max;
-       if (parent_rate % characteristics->input.max)
-               mindiv++;
-
-       for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) {
-               divrate = parent_rate / tmpdiv;
-
-               rounddown = rate % divrate;
-               roundup = divrate - rounddown;
-               remainder = roundup < rounddown ? roundup : rounddown;
-
+       /*
+        * Calculate minimum divider based on the minimum multiplier, the
+        * parent_rate and the requested rate.
+        * Should always be 2 according to the input and output characteristics
+        * of the PLL blocks.
+        */
+       mindiv = (parent_rate * PLL_MUL_MIN) / rate;
+       if (!mindiv)
+               mindiv = 1;
+
+       /*
+        * Calculate the maximum divider which is limited by PLL register
+        * layout (limited by the MUL or DIV field size).
+        */
+       maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate);
+       if (maxdiv > PLL_DIV_MAX)
+               maxdiv = PLL_DIV_MAX;
+
+       /*
+        * Iterate over the acceptable divider values to find the best
+        * divider/multiplier pair (the one that generates the closest
+        * rate to the requested one).
+        */
+       for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
+               unsigned long remainder;
+               unsigned long tmprate;
+               unsigned long tmpmul;
+
+               /*
+                * Calculate the multiplier associated with the current
+                * divider that provide the closest rate to the requested one.
+                */
+               tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv);
+               tmprate = (parent_rate / tmpdiv) * tmpmul;
+               if (tmprate > rate)
+                       remainder = tmprate - rate;
+               else
+                       remainder = rate - tmprate;
+
+               /*
+                * Compare the remainder with the best remainder found until
+                * now and elect a new best multiplier/divider pair if the
+                * current remainder is smaller than the best one.
+                */
                if (remainder < bestremainder) {
                        bestremainder = remainder;
-                       bestmul = rate / divrate;
                        bestdiv = tmpdiv;
+                       bestmul = tmpmul;
+                       bestrate = tmprate;
                }
 
+               /*
+                * We've found a perfect match!
+                * Stop searching now and use this multiplier/divider pair.
+                */
                if (!remainder)
                        break;
        }
 
-       rate = (parent_rate / bestdiv) * bestmul;
+       /* We haven't found any multiplier/divider pair => return -ERANGE */
+       if (bestrate < 0)
+               return bestrate;
+
+       /* Check if bestrate is a valid output rate  */
+       for (i = 0; i < characteristics->num_output; i++) {
+               if (bestrate >= characteristics->output[i].min &&
+                   bestrate <= characteristics->output[i].max)
+                       break;
+       }
+
+       if (i >= characteristics->num_output)
+               return -ERANGE;
 
        if (div)
                *div = bestdiv;
        if (mul)
-               *mul = bestmul;
+               *mul = bestmul - 1;
        if (index)
                *index = i;
 
-       return rate;
+       return bestrate;
 }
 
 static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
index 7d1d26a4bd04459bb509a7004fd0043476b70ff9..24b5b020753a9e4a66a5d3db7c8f7ad8bee0b928 100644 (file)
@@ -238,16 +238,22 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
                                          unsigned long *parent_rate)
 {
        struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
+       struct clk *parent = __clk_get_parent(hw->clk);
        unsigned long bestrate = 0;
        int bestdiff = -1;
        unsigned long tmprate;
        int tmpdiff;
        int i = 0;
 
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
+               unsigned long tmp_parent_rate;
+
                if (!usb->divisors[i])
                        continue;
-               tmprate = *parent_rate / usb->divisors[i];
+
+               tmp_parent_rate = rate * usb->divisors[i];
+               tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate);
+               tmprate = tmp_parent_rate / usb->divisors[i];
                if (tmprate < rate)
                        tmpdiff = rate - tmprate;
                else
@@ -256,6 +262,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
                if (bestdiff < 0 || bestdiff > tmpdiff) {
                        bestrate = tmprate;
                        bestdiff = tmpdiff;
+                       *parent_rate = tmp_parent_rate;
                }
 
                if (!bestdiff)
@@ -272,10 +279,13 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
        int i;
        struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
        struct at91_pmc *pmc = usb->pmc;
-       unsigned long div = parent_rate / rate;
+       unsigned long div;
 
-       if (parent_rate % rate)
+       if (!rate || parent_rate % rate)
                return -EINVAL;
+
+       div = parent_rate / rate;
+
        for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
                if (usb->divisors[i] == div) {
                        tmp = pmc_read(pmc, AT91_CKGR_PLLBR) &
@@ -311,7 +321,7 @@ at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name,
        init.ops = &at91rm9200_usb_ops;
        init.parent_names = &parent_name;
        init.num_parents = 1;
-       init.flags = 0;
+       init.flags = CLK_SET_RATE_PARENT;
 
        usb->hw.init = &init;
        usb->pmc = pmc;
index 1127ee46b8024548151b9920246ef286cc887b99..e619285c6defddc731ce4eac34c3b5527bf5e47b 100644 (file)
@@ -544,7 +544,6 @@ static int axi_clkgen_remove(struct platform_device *pdev)
 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,
index ede685ca0d20b03ef5b9b6e6bd37c4d57e0db625..82a59d0086cc79d654c4ceaf4aa42a8491467dbc 100644 (file)
@@ -36,7 +36,7 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
        m = (val & fd->mmask) >> fd->mshift;
        n = (val & fd->nmask) >> fd->nshift;
 
-       ret = parent_rate * m;
+       ret = (u64)parent_rate * m;
        do_div(ret, n);
 
        return ret;
index 4a58c55255bd884f3f1e7deea52048712b297729..51fd87fb7ba691e8a52e40ddc1ee4524ecd3e781 100644 (file)
@@ -45,7 +45,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
 {
        struct clk_gate *gate = to_clk_gate(hw);
        int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
-       unsigned long flags = 0;
+       unsigned long uninitialized_var(flags);
        u32 reg;
 
        set ^= enable;
diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c
new file mode 100644 (file)
index 0000000..08e4322
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2013 - 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Jyri Sarha <jsarha@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Gpio gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+/**
+ * DOC: basic gpio gated clock which can be enabled and disabled
+ *      with gpio output
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gpio
+ * rate - inherits rate from parent.  No clk_set_rate support
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define to_clk_gpio(_hw) container_of(_hw, struct clk_gpio, hw)
+
+static int clk_gpio_gate_enable(struct clk_hw *hw)
+{
+       struct clk_gpio *clk = to_clk_gpio(hw);
+
+       gpiod_set_value(clk->gpiod, 1);
+
+       return 0;
+}
+
+static void clk_gpio_gate_disable(struct clk_hw *hw)
+{
+       struct clk_gpio *clk = to_clk_gpio(hw);
+
+       gpiod_set_value(clk->gpiod, 0);
+}
+
+static int clk_gpio_gate_is_enabled(struct clk_hw *hw)
+{
+       struct clk_gpio *clk = to_clk_gpio(hw);
+
+       return gpiod_get_value(clk->gpiod);
+}
+
+const struct clk_ops clk_gpio_gate_ops = {
+       .enable = clk_gpio_gate_enable,
+       .disable = clk_gpio_gate_disable,
+       .is_enabled = clk_gpio_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gpio_gate_ops);
+
+/**
+ * clk_register_gpio - register a gpip clock with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @parent_name: name of this clock's parent
+ * @gpiod: gpio descriptor to gate this clock
+ */
+struct clk *clk_register_gpio_gate(struct device *dev, const char *name,
+               const char *parent_name, struct gpio_desc *gpiod,
+               unsigned long flags)
+{
+       struct clk_gpio *clk_gpio = NULL;
+       struct clk *clk = ERR_PTR(-EINVAL);
+       struct clk_init_data init = { NULL };
+       unsigned long gpio_flags;
+       int err;
+
+       if (gpiod_is_active_low(gpiod))
+               gpio_flags = GPIOF_OUT_INIT_HIGH;
+       else
+               gpio_flags = GPIOF_OUT_INIT_LOW;
+
+       if (dev)
+               err = devm_gpio_request_one(dev, desc_to_gpio(gpiod),
+                                           gpio_flags, name);
+       else
+               err = gpio_request_one(desc_to_gpio(gpiod), gpio_flags, name);
+
+       if (err) {
+               pr_err("%s: %s: Error requesting clock control gpio %u\n",
+                      __func__, name, desc_to_gpio(gpiod));
+               return ERR_PTR(err);
+       }
+
+       if (dev)
+               clk_gpio = devm_kzalloc(dev, sizeof(struct clk_gpio),
+                                       GFP_KERNEL);
+       else
+               clk_gpio = kzalloc(sizeof(struct clk_gpio), GFP_KERNEL);
+
+       if (!clk_gpio) {
+               clk = ERR_PTR(-ENOMEM);
+               goto clk_register_gpio_gate_err;
+       }
+
+       init.name = name;
+       init.ops = &clk_gpio_gate_ops;
+       init.flags = flags | CLK_IS_BASIC;
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+
+       clk_gpio->gpiod = gpiod;
+       clk_gpio->hw.init = &init;
+
+       clk = clk_register(dev, &clk_gpio->hw);
+
+       if (!IS_ERR(clk))
+               return clk;
+
+       if (!dev)
+               kfree(clk_gpio);
+
+clk_register_gpio_gate_err:
+       gpiod_put(gpiod);
+
+       return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_gpio_gate);
+
+#ifdef CONFIG_OF
+/**
+ * The clk_register_gpio_gate has to be delayed, because the EPROBE_DEFER
+ * can not be handled properly at of_clk_init() call time.
+ */
+
+struct clk_gpio_gate_delayed_register_data {
+       struct device_node *node;
+       struct mutex lock;
+       struct clk *clk;
+};
+
+static struct clk *of_clk_gpio_gate_delayed_register_get(
+               struct of_phandle_args *clkspec,
+               void *_data)
+{
+       struct clk_gpio_gate_delayed_register_data *data = _data;
+       struct clk *clk;
+       const char *clk_name = data->node->name;
+       const char *parent_name;
+       struct gpio_desc *gpiod;
+       int gpio;
+
+       mutex_lock(&data->lock);
+
+       if (data->clk) {
+               mutex_unlock(&data->lock);
+               return data->clk;
+       }
+
+       gpio = of_get_named_gpio_flags(data->node, "enable-gpios", 0, NULL);
+       if (gpio < 0) {
+               mutex_unlock(&data->lock);
+               if (gpio != -EPROBE_DEFER)
+                       pr_err("%s: %s: Can't get 'enable-gpios' DT property\n",
+                              __func__, clk_name);
+               return ERR_PTR(gpio);
+       }
+       gpiod = gpio_to_desc(gpio);
+
+       parent_name = of_clk_get_parent_name(data->node, 0);
+
+       clk = clk_register_gpio_gate(NULL, clk_name, parent_name, gpiod, 0);
+       if (IS_ERR(clk)) {
+               mutex_unlock(&data->lock);
+               return clk;
+       }
+
+       data->clk = clk;
+       mutex_unlock(&data->lock);
+
+       return clk;
+}
+
+/**
+ * of_gpio_gate_clk_setup() - Setup function for gpio controlled clock
+ */
+void __init of_gpio_gate_clk_setup(struct device_node *node)
+{
+       struct clk_gpio_gate_delayed_register_data *data;
+
+       data = kzalloc(sizeof(struct clk_gpio_gate_delayed_register_data),
+                      GFP_KERNEL);
+       if (!data)
+               return;
+
+       data->node = node;
+       mutex_init(&data->lock);
+
+       of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data);
+}
+EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup);
+CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup);
+#endif
diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c
new file mode 100644 (file)
index 0000000..6505049
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * clk-max-gen.c - Generic clock driver for Maxim PMICs clocks
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electornics
+ * Jonghwa Lee <jonghwa3.lee@samsung.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.
+ *
+ * This driver is based on clk-max77686.c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/export.h>
+
+struct max_gen_clk {
+       struct regmap *regmap;
+       u32 mask;
+       u32 reg;
+       struct clk_hw hw;
+};
+
+static struct max_gen_clk *to_max_gen_clk(struct clk_hw *hw)
+{
+       return container_of(hw, struct max_gen_clk, hw);
+}
+
+static int max_gen_clk_prepare(struct clk_hw *hw)
+{
+       struct max_gen_clk *max_gen = to_max_gen_clk(hw);
+
+       return regmap_update_bits(max_gen->regmap, max_gen->reg,
+                                 max_gen->mask, max_gen->mask);
+}
+
+static void max_gen_clk_unprepare(struct clk_hw *hw)
+{
+       struct max_gen_clk *max_gen = to_max_gen_clk(hw);
+
+       regmap_update_bits(max_gen->regmap, max_gen->reg,
+                          max_gen->mask, ~max_gen->mask);
+}
+
+static int max_gen_clk_is_prepared(struct clk_hw *hw)
+{
+       struct max_gen_clk *max_gen = to_max_gen_clk(hw);
+       int ret;
+       u32 val;
+
+       ret = regmap_read(max_gen->regmap, max_gen->reg, &val);
+
+       if (ret < 0)
+               return -EINVAL;
+
+       return val & max_gen->mask;
+}
+
+static unsigned long max_gen_recalc_rate(struct clk_hw *hw,
+                                        unsigned long parent_rate)
+{
+       return 32768;
+}
+
+struct clk_ops max_gen_clk_ops = {
+       .prepare        = max_gen_clk_prepare,
+       .unprepare      = max_gen_clk_unprepare,
+       .is_prepared    = max_gen_clk_is_prepared,
+       .recalc_rate    = max_gen_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(max_gen_clk_ops);
+
+static struct clk *max_gen_clk_register(struct device *dev,
+                                       struct max_gen_clk *max_gen)
+{
+       struct clk *clk;
+       struct clk_hw *hw = &max_gen->hw;
+       int ret;
+
+       clk = devm_clk_register(dev, hw);
+       if (IS_ERR(clk))
+               return clk;
+
+       ret = clk_register_clkdev(clk, hw->init->name, NULL);
+
+       if (ret)
+               return ERR_PTR(ret);
+
+       return clk;
+}
+
+int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap,
+                     u32 reg, struct clk_init_data *clks_init, int num_init)
+{
+       int i, ret;
+       struct max_gen_clk *max_gen_clks;
+       struct clk **clocks;
+       struct device *dev = pdev->dev.parent;
+       const char *clk_name;
+       struct clk_init_data *init;
+
+       clocks = devm_kzalloc(dev, sizeof(struct clk *) * num_init, GFP_KERNEL);
+       if (!clocks)
+               return -ENOMEM;
+
+       max_gen_clks = devm_kzalloc(dev, sizeof(struct max_gen_clk)
+                                   * num_init, GFP_KERNEL);
+       if (!max_gen_clks)
+               return -ENOMEM;
+
+       for (i = 0; i < num_init; i++) {
+               max_gen_clks[i].regmap = regmap;
+               max_gen_clks[i].mask = 1 << i;
+               max_gen_clks[i].reg = reg;
+
+               init = devm_kzalloc(dev, sizeof(*init), GFP_KERNEL);
+               if (!init)
+                       return -ENOMEM;
+
+               if (dev->of_node &&
+                   !of_property_read_string_index(dev->of_node,
+                                                  "clock-output-names",
+                                                  i, &clk_name))
+                       init->name = clk_name;
+               else
+                       init->name = clks_init[i].name;
+
+               init->ops = clks_init[i].ops;
+               init->flags = clks_init[i].flags;
+
+               max_gen_clks[i].hw.init = init;
+
+               clocks[i] = max_gen_clk_register(dev, &max_gen_clks[i]);
+               if (IS_ERR(clocks[i])) {
+                       ret = PTR_ERR(clocks[i]);
+                       dev_err(dev, "failed to register %s\n",
+                               max_gen_clks[i].hw.init->name);
+                       return ret;
+               }
+       }
+
+       platform_set_drvdata(pdev, clocks);
+
+       if (dev->of_node) {
+               struct clk_onecell_data *of_data;
+
+               of_data = devm_kzalloc(dev, sizeof(*of_data), GFP_KERNEL);
+               if (!of_data)
+                       return -ENOMEM;
+
+               of_data->clks = clocks;
+               of_data->clk_num = num_init;
+               ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
+                                         of_data);
+
+               if (ret) {
+                       dev_err(dev, "failed to register OF clock provider\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(max_gen_clk_probe);
+
+int max_gen_clk_remove(struct platform_device *pdev, int num_init)
+{
+       struct device *dev = pdev->dev.parent;
+
+       if (dev->of_node)
+               of_clk_del_provider(dev->of_node);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(max_gen_clk_remove);
diff --git a/drivers/clk/clk-max-gen.h b/drivers/clk/clk-max-gen.h
new file mode 100644 (file)
index 0000000..997e86f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * clk-max-gen.h - Generic clock driver for Maxim PMICs clocks
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * 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 __CLK_MAX_GEN_H__
+#define __CLK_MAX_GEN_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clkdev.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+
+int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap,
+                     u32 reg, struct clk_init_data *clks_init, int num_init);
+int max_gen_clk_remove(struct platform_device *pdev, int num_init);
+extern struct clk_ops max_gen_clk_ops;
+
+#endif /* __CLK_MAX_GEN_H__ */
index 3d7e8dd8fd58ffba2dba7c39451bfa3f0d09ba0a..86cdb3a28629ab09394292e2b210eb5b689b4504 100644 (file)
 #include <linux/mutex.h>
 #include <linux/clkdev.h>
 
-enum {
-       MAX77686_CLK_AP = 0,
-       MAX77686_CLK_CP,
-       MAX77686_CLK_PMIC,
-       MAX77686_CLKS_NUM,
-};
-
-struct max77686_clk {
-       struct max77686_dev *iodev;
-       u32 mask;
-       struct clk_hw hw;
-       struct clk_lookup *lookup;
-};
-
-static struct max77686_clk *to_max77686_clk(struct clk_hw *hw)
-{
-       return container_of(hw, struct max77686_clk, hw);
-}
-
-static int max77686_clk_prepare(struct clk_hw *hw)
-{
-       struct max77686_clk *max77686 = to_max77686_clk(hw);
-
-       return regmap_update_bits(max77686->iodev->regmap,
-                                 MAX77686_REG_32KHZ, max77686->mask,
-                                 max77686->mask);
-}
-
-static void max77686_clk_unprepare(struct clk_hw *hw)
-{
-       struct max77686_clk *max77686 = to_max77686_clk(hw);
-
-       regmap_update_bits(max77686->iodev->regmap,
-               MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
-}
-
-static int max77686_clk_is_prepared(struct clk_hw *hw)
-{
-       struct max77686_clk *max77686 = to_max77686_clk(hw);
-       int ret;
-       u32 val;
-
-       ret = regmap_read(max77686->iodev->regmap,
-                               MAX77686_REG_32KHZ, &val);
-
-       if (ret < 0)
-               return -EINVAL;
-
-       return val & max77686->mask;
-}
-
-static unsigned long max77686_recalc_rate(struct clk_hw *hw,
-                                         unsigned long parent_rate)
-{
-       return 32768;
-}
-
-static struct clk_ops max77686_clk_ops = {
-       .prepare        = max77686_clk_prepare,
-       .unprepare      = max77686_clk_unprepare,
-       .is_prepared    = max77686_clk_is_prepared,
-       .recalc_rate    = max77686_recalc_rate,
-};
+#include <dt-bindings/clock/maxim,max77686.h>
+#include "clk-max-gen.h"
 
 static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
        [MAX77686_CLK_AP] = {
                .name = "32khz_ap",
-               .ops = &max77686_clk_ops,
+               .ops = &max_gen_clk_ops,
                .flags = CLK_IS_ROOT,
        },
        [MAX77686_CLK_CP] = {
                .name = "32khz_cp",
-               .ops = &max77686_clk_ops,
+               .ops = &max_gen_clk_ops,
                .flags = CLK_IS_ROOT,
        },
        [MAX77686_CLK_PMIC] = {
                .name = "32khz_pmic",
-               .ops = &max77686_clk_ops,
+               .ops = &max_gen_clk_ops,
                .flags = CLK_IS_ROOT,
        },
 };
 
-static struct clk *max77686_clk_register(struct device *dev,
-                               struct max77686_clk *max77686)
-{
-       struct clk *clk;
-       struct clk_hw *hw = &max77686->hw;
-
-       clk = clk_register(dev, hw);
-       if (IS_ERR(clk))
-               return clk;
-
-       max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
-       if (!max77686->lookup)
-               return ERR_PTR(-ENOMEM);
-
-       max77686->lookup->con_id = hw->init->name;
-       max77686->lookup->clk = clk;
-
-       clkdev_add(max77686->lookup);
-
-       return clk;
-}
-
 static int max77686_clk_probe(struct platform_device *pdev)
 {
        struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
-       struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM];
-       struct clk **clocks;
-       int i, ret;
-
-       clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
-                                       * MAX77686_CLKS_NUM, GFP_KERNEL);
-       if (!clocks)
-               return -ENOMEM;
-
-       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
-               max77686_clks[i] = devm_kzalloc(&pdev->dev,
-                                       sizeof(struct max77686_clk), GFP_KERNEL);
-               if (!max77686_clks[i])
-                       return -ENOMEM;
-       }
-
-       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
-               max77686_clks[i]->iodev = iodev;
-               max77686_clks[i]->mask = 1 << i;
-               max77686_clks[i]->hw.init = &max77686_clks_init[i];
-
-               clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]);
-               if (IS_ERR(clocks[i])) {
-                       ret = PTR_ERR(clocks[i]);
-                       dev_err(&pdev->dev, "failed to register %s\n",
-                               max77686_clks[i]->hw.init->name);
-                       goto err_clocks;
-               }
-       }
-
-       platform_set_drvdata(pdev, clocks);
-
-       if (iodev->dev->of_node) {
-               struct clk_onecell_data *of_data;
 
-               of_data = devm_kzalloc(&pdev->dev,
-                                       sizeof(*of_data), GFP_KERNEL);
-               if (!of_data) {
-                       ret = -ENOMEM;
-                       goto err_clocks;
-               }
-
-               of_data->clks = clocks;
-               of_data->clk_num = MAX77686_CLKS_NUM;
-               ret = of_clk_add_provider(iodev->dev->of_node,
-                                       of_clk_src_onecell_get, of_data);
-               if (ret) {
-                       dev_err(&pdev->dev, "failed to register OF clock provider\n");
-                       goto err_clocks;
-               }
-       }
-
-       return 0;
-
-err_clocks:
-       for (--i; i >= 0; --i) {
-               clkdev_drop(max77686_clks[i]->lookup);
-               clk_unregister(max77686_clks[i]->hw.clk);
-       }
-
-       return ret;
+       return max_gen_clk_probe(pdev, iodev->regmap, MAX77686_REG_32KHZ,
+                                max77686_clks_init, MAX77686_CLKS_NUM);
 }
 
 static int max77686_clk_remove(struct platform_device *pdev)
 {
-       struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
-       struct clk **clocks = platform_get_drvdata(pdev);
-       int i;
-
-       if (iodev->dev->of_node)
-               of_clk_del_provider(iodev->dev->of_node);
-
-       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
-               struct clk_hw *hw = __clk_get_hw(clocks[i]);
-               struct max77686_clk *max77686 = to_max77686_clk(hw);
-
-               clkdev_drop(max77686->lookup);
-               clk_unregister(clocks[i]);
-       }
-       return 0;
+       return max_gen_clk_remove(pdev, MAX77686_CLKS_NUM);
 }
 
 static const struct platform_device_id max77686_clk_id[] = {
@@ -228,24 +73,13 @@ MODULE_DEVICE_TABLE(platform, max77686_clk_id);
 static struct platform_driver max77686_clk_driver = {
        .driver = {
                .name  = "max77686-clk",
-               .owner = THIS_MODULE,
        },
        .probe = max77686_clk_probe,
        .remove = max77686_clk_remove,
        .id_table = max77686_clk_id,
 };
 
-static int __init max77686_clk_init(void)
-{
-       return platform_driver_register(&max77686_clk_driver);
-}
-subsys_initcall(max77686_clk_init);
-
-static void __init max77686_clk_cleanup(void)
-{
-       platform_driver_unregister(&max77686_clk_driver);
-}
-module_exit(max77686_clk_cleanup);
+module_platform_driver(max77686_clk_driver);
 
 MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
 MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c
new file mode 100644 (file)
index 0000000..0729dc7
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * clk-max77802.c - Clock driver for Maxim 77802
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electornics
+ * Jonghwa Lee <jonghwa3.lee@samsung.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.
+ *
+ * This driver is based on clk-max77686.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+
+#include <dt-bindings/clock/maxim,max77802.h>
+#include "clk-max-gen.h"
+
+#define MAX77802_CLOCK_OPMODE_MASK     0x1
+#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
+
+static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = {
+       [MAX77802_CLK_32K_AP] = {
+               .name = "32khz_ap",
+               .ops = &max_gen_clk_ops,
+               .flags = CLK_IS_ROOT,
+       },
+       [MAX77802_CLK_32K_CP] = {
+               .name = "32khz_cp",
+               .ops = &max_gen_clk_ops,
+               .flags = CLK_IS_ROOT,
+       },
+};
+
+static int max77802_clk_probe(struct platform_device *pdev)
+{
+       struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+       int ret;
+
+       ret = max_gen_clk_probe(pdev, iodev->regmap, MAX77802_REG_32KHZ,
+                               max77802_clks_init, MAX77802_CLKS_NUM);
+
+       if (ret) {
+               dev_err(&pdev->dev, "generic probe failed %d\n", ret);
+               return ret;
+       }
+
+       /* Enable low-jitter mode on the 32khz clocks. */
+       ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ,
+                                1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
+                                1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
+       if (ret < 0)
+               dev_err(&pdev->dev, "failed to enable low-jitter mode\n");
+
+       return ret;
+}
+
+static int max77802_clk_remove(struct platform_device *pdev)
+{
+       return max_gen_clk_remove(pdev, MAX77802_CLKS_NUM);
+}
+
+static const struct platform_device_id max77802_clk_id[] = {
+       { "max77802-clk", 0},
+       { },
+};
+MODULE_DEVICE_TABLE(platform, max77802_clk_id);
+
+static struct platform_driver max77802_clk_driver = {
+       .driver = {
+               .name  = "max77802-clk",
+       },
+       .probe = max77802_clk_probe,
+       .remove = max77802_clk_remove,
+       .id_table = max77802_clk_id,
+};
+
+module_platform_driver(max77802_clk_driver);
+
+MODULE_DESCRIPTION("MAXIM 77802 Clock Driver");
+MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>");
+MODULE_LICENSE("GPL");
index 781630e1372be6151695d7347c4bf439ea58bda7..8d459923a15fd591c782752f79393bb20f4b4da9 100644 (file)
@@ -292,7 +292,6 @@ static int palmas_clks_remove(struct platform_device *pdev)
 static struct platform_driver palmas_clks_driver = {
        .driver = {
                .name = "palmas-clk",
-               .owner = THIS_MODULE,
                .of_match_table = palmas_clks_of_match,
        },
        .probe = palmas_clks_probe,
index 1ada79a2805297aa4b3a6d921b93159bd951a402..4a755135bcd388433696ee4c651a5bbb03302f5b 100644 (file)
@@ -112,7 +112,6 @@ static int twl6040_clk_remove(struct platform_device *pdev)
 static struct platform_driver twl6040_clk_driver = {
        .driver = {
                .name = "twl6040-clk",
-               .owner = THIS_MODULE,
        },
        .probe = twl6040_clk_probe,
        .remove = twl6040_clk_remove,
index b131041c8f488f29f4a9f9d5161da119f274245e..ef67719f4e52c23cd977844662e3a202be4963bc 100644 (file)
@@ -395,7 +395,6 @@ static struct platform_driver wm831x_clk_driver = {
        .probe = wm831x_clk_probe,
        .driver         = {
                .name   = "wm831x-clk",
-               .owner  = THIS_MODULE,
        },
 };
 
index bacc06ff939b090bffb9ef9759ac9b9058520a98..4896ae9e23da0c23ed52872cf1992f95609e1b59 100644 (file)
@@ -100,6 +100,8 @@ static void clk_enable_unlock(unsigned long flags)
 
 static struct dentry *rootdir;
 static int inited = 0;
+static DEFINE_MUTEX(clk_debug_lock);
+static HLIST_HEAD(clk_debug_list);
 
 static struct hlist_head *all_lists[] = {
        &clk_root_list,
@@ -117,11 +119,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level)
        if (!c)
                return;
 
-       seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu\n",
+       seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n",
                   level * 3 + 1, "",
                   30 - level * 3, c->name,
                   c->enable_count, c->prepare_count, clk_get_rate(c),
-                  clk_get_accuracy(c));
+                  clk_get_accuracy(c), clk_get_phase(c));
 }
 
 static void clk_summary_show_subtree(struct seq_file *s, struct clk *c,
@@ -143,8 +145,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
        struct clk *c;
        struct hlist_head **lists = (struct hlist_head **)s->private;
 
-       seq_puts(s, "   clock                         enable_cnt  prepare_cnt        rate   accuracy\n");
-       seq_puts(s, "--------------------------------------------------------------------------------\n");
+       seq_puts(s, "   clock                         enable_cnt  prepare_cnt        rate   accuracy   phase\n");
+       seq_puts(s, "----------------------------------------------------------------------------------------\n");
 
        clk_prepare_lock();
 
@@ -180,6 +182,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level)
        seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
        seq_printf(s, "\"rate\": %lu", clk_get_rate(c));
        seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c));
+       seq_printf(s, "\"phase\": %d", clk_get_phase(c));
 }
 
 static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level)
@@ -264,6 +267,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
        if (!d)
                goto err_out;
 
+       d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry,
+                       (u32 *)&clk->phase);
+       if (!d)
+               goto err_out;
+
        d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
                        (u32 *)&clk->flags);
        if (!d)
@@ -300,28 +308,6 @@ out:
        return ret;
 }
 
-/* caller must hold prepare_lock */
-static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry)
-{
-       struct clk *child;
-       int ret = -EINVAL;;
-
-       if (!clk || !pdentry)
-               goto out;
-
-       ret = clk_debug_create_one(clk, pdentry);
-
-       if (ret)
-               goto out;
-
-       hlist_for_each_entry(child, &clk->children, child_node)
-               clk_debug_create_subtree(child, pdentry);
-
-       ret = 0;
-out:
-       return ret;
-}
-
 /**
  * clk_debug_register - add a clk node to the debugfs clk tree
  * @clk: the clk being added to the debugfs clk tree
@@ -329,20 +315,21 @@ out:
  * Dynamically adds a clk to 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.  Only clk_init calls this function (so
- * far) so this is taken care.
  */
 static int clk_debug_register(struct clk *clk)
 {
        int ret = 0;
 
+       mutex_lock(&clk_debug_lock);
+       hlist_add_head(&clk->debug_node, &clk_debug_list);
+
        if (!inited)
-               goto out;
+               goto unlock;
 
-       ret = clk_debug_create_subtree(clk, rootdir);
+       ret = clk_debug_create_one(clk, rootdir);
+unlock:
+       mutex_unlock(&clk_debug_lock);
 
-out:
        return ret;
 }
 
@@ -353,12 +340,18 @@ out:
  * Dynamically removes a clk and all it's children clk nodes from the
  * debugfs clk tree if clk->dentry points to debugfs created by
  * clk_debug_register in __clk_init.
- *
- * Caller must hold prepare_lock.
  */
 static void clk_debug_unregister(struct clk *clk)
 {
+       mutex_lock(&clk_debug_lock);
+       if (!clk->dentry)
+               goto out;
+
+       hlist_del_init(&clk->debug_node);
        debugfs_remove_recursive(clk->dentry);
+       clk->dentry = NULL;
+out:
+       mutex_unlock(&clk_debug_lock);
 }
 
 struct dentry *clk_debugfs_add_file(struct clk *clk, char *name, umode_t mode,
@@ -415,17 +408,12 @@ static int __init clk_debug_init(void)
        if (!d)
                return -ENOMEM;
 
-       clk_prepare_lock();
-
-       hlist_for_each_entry(clk, &clk_root_list, child_node)
-               clk_debug_create_subtree(clk, rootdir);
-
-       hlist_for_each_entry(clk, &clk_orphan_list, child_node)
-               clk_debug_create_subtree(clk, rootdir);
+       mutex_lock(&clk_debug_lock);
+       hlist_for_each_entry(clk, &clk_debug_list, debug_node)
+               clk_debug_create_one(clk, rootdir);
 
        inited = 1;
-
-       clk_prepare_unlock();
+       mutex_unlock(&clk_debug_lock);
 
        return 0;
 }
@@ -1743,6 +1731,77 @@ out:
 }
 EXPORT_SYMBOL_GPL(clk_set_parent);
 
+/**
+ * clk_set_phase - adjust the phase shift of a clock signal
+ * @clk: clock signal source
+ * @degrees: number of degrees the signal is shifted
+ *
+ * Shifts the phase of a clock signal by the specified
+ * degrees. Returns 0 on success, -EERROR otherwise.
+ *
+ * This function makes no distinction about the input or reference
+ * signal that we adjust the clock signal phase against. For example
+ * phase locked-loop clock signal generators we may shift phase with
+ * respect to feedback clock signal input, but for other cases the
+ * clock phase may be shifted with respect to some other, unspecified
+ * signal.
+ *
+ * Additionally the concept of phase shift does not propagate through
+ * the clock tree hierarchy, which sets it apart from clock rates and
+ * clock accuracy. A parent clock phase attribute does not have an
+ * impact on the phase attribute of a child clock.
+ */
+int clk_set_phase(struct clk *clk, int degrees)
+{
+       int ret = 0;
+
+       if (!clk)
+               goto out;
+
+       /* sanity check degrees */
+       degrees %= 360;
+       if (degrees < 0)
+               degrees += 360;
+
+       clk_prepare_lock();
+
+       if (!clk->ops->set_phase)
+               goto out_unlock;
+
+       ret = clk->ops->set_phase(clk->hw, degrees);
+
+       if (!ret)
+               clk->phase = degrees;
+
+out_unlock:
+       clk_prepare_unlock();
+
+out:
+       return ret;
+}
+
+/**
+ * clk_get_phase - return the phase shift of a clock signal
+ * @clk: clock signal source
+ *
+ * Returns the phase shift of a clock node in degrees, otherwise returns
+ * -EERROR.
+ */
+int clk_get_phase(struct clk *clk)
+{
+       int ret = 0;
+
+       if (!clk)
+               goto out;
+
+       clk_prepare_lock();
+       ret = clk->phase;
+       clk_prepare_unlock();
+
+out:
+       return ret;
+}
+
 /**
  * __clk_init - initialize the data structures in a struct clk
  * @dev:       device initializing this clk, placeholder for now
@@ -1861,6 +1920,16 @@ int __clk_init(struct device *dev, struct clk *clk)
        else
                clk->accuracy = 0;
 
+       /*
+        * Set clk's phase.
+        * Since a phase is by definition relative to its parent, just
+        * query the current clock phase, or just assume it's in phase.
+        */
+       if (clk->ops->get_phase)
+               clk->phase = clk->ops->get_phase(clk->hw);
+       else
+               clk->phase = 0;
+
        /*
         * Set clk's rate.  The preferred method is to use .recalc_rate.  For
         * simple clocks and lazy developers the default fallback is to use the
@@ -2092,14 +2161,16 @@ void clk_unregister(struct clk *clk)
 {
        unsigned long flags;
 
-       if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
-               return;
+       if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
+               return;
+
+       clk_debug_unregister(clk);
 
        clk_prepare_lock();
 
        if (clk->ops == &clk_nodrv_ops) {
                pr_err("%s: unregistered clock: %s\n", __func__, clk->name);
-               goto out;
+               return;
        }
        /*
         * Assign empty clock ops for consumers that might still hold
@@ -2118,16 +2189,13 @@ void clk_unregister(struct clk *clk)
                        clk_set_parent(child, NULL);
        }
 
-       clk_debug_unregister(clk);
-
        hlist_del_init(&clk->child_node);
 
        if (clk->prepare_count)
                pr_warn("%s: unregistering prepared clock: %s\n",
                                        __func__, clk->name);
-
        kref_put(&clk->ref, __clk_release);
-out:
+
        clk_prepare_unlock();
 }
 EXPORT_SYMBOL_GPL(clk_unregister);
index e5fcfb4e32efa2b8bb24d32bb7ac19fec74b2c55..3f369c60fe56a9f6d449ef82c51ffca141cf8aee 100644 (file)
@@ -9,6 +9,8 @@
 
 #include <linux/of_address.h>
 #include <dt-bindings/clock/hix5hd2-clock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
 #include "clk.h"
 
 static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = {
@@ -48,9 +50,9 @@ static const char *sfc_mux_p[] __initconst = {
                "24m", "150m", "200m", "100m", "75m", };
 static u32 sfc_mux_table[] = {0, 4, 5, 6, 7};
 
-static const char *sdio1_mux_p[] __initconst = {
+static const char *sdio_mux_p[] __initconst = {
                "75m", "100m", "50m", "15m", };
-static u32 sdio1_mux_table[] = {0, 1, 2, 3};
+static u32 sdio_mux_table[] = {0, 1, 2, 3};
 
 static const char *fephy_mux_p[] __initconst = { "25m", "125m"};
 static u32 fephy_mux_table[] = {0, 1};
@@ -59,28 +61,243 @@ static u32 fephy_mux_table[] = {0, 1};
 static struct hisi_mux_clock hix5hd2_mux_clks[] __initdata = {
        { HIX5HD2_SFC_MUX, "sfc_mux", sfc_mux_p, ARRAY_SIZE(sfc_mux_p),
                CLK_SET_RATE_PARENT, 0x5c, 8, 3, 0, sfc_mux_table, },
-       { HIX5HD2_MMC_MUX, "mmc_mux", sdio1_mux_p, ARRAY_SIZE(sdio1_mux_p),
-               CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio1_mux_table, },
+       { HIX5HD2_MMC_MUX, "mmc_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p),
+               CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio_mux_table, },
+       { HIX5HD2_SD_MUX, "sd_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p),
+               CLK_SET_RATE_PARENT, 0x9c, 8, 2, 0, sdio_mux_table, },
        { HIX5HD2_FEPHY_MUX, "fephy_mux",
                fephy_mux_p, ARRAY_SIZE(fephy_mux_p),
                CLK_SET_RATE_PARENT, 0x120, 8, 2, 0, fephy_mux_table, },
 };
 
 static struct hisi_gate_clock hix5hd2_gate_clks[] __initdata = {
-       /*sfc*/
+       /* sfc */
        { HIX5HD2_SFC_CLK, "clk_sfc", "sfc_mux",
                CLK_SET_RATE_PARENT, 0x5c, 0, 0, },
        { HIX5HD2_SFC_RST, "rst_sfc", "clk_sfc",
                CLK_SET_RATE_PARENT, 0x5c, 4, CLK_GATE_SET_TO_DISABLE, },
-       /*sdio1*/
+       /* sdio0 */
+       { HIX5HD2_SD_BIU_CLK, "clk_sd_biu", "200m",
+               CLK_SET_RATE_PARENT, 0x9c, 0, 0, },
+       { HIX5HD2_SD_CIU_CLK, "clk_sd_ciu", "sd_mux",
+               CLK_SET_RATE_PARENT, 0x9c, 1, 0, },
+       { HIX5HD2_SD_CIU_RST, "rst_sd_ciu", "clk_sd_ciu",
+               CLK_SET_RATE_PARENT, 0x9c, 4, CLK_GATE_SET_TO_DISABLE, },
+       /* sdio1 */
        { HIX5HD2_MMC_BIU_CLK, "clk_mmc_biu", "200m",
                CLK_SET_RATE_PARENT, 0xa0, 0, 0, },
        { HIX5HD2_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux",
                CLK_SET_RATE_PARENT, 0xa0, 1, 0, },
        { HIX5HD2_MMC_CIU_RST, "rst_mmc_ciu", "clk_mmc_ciu",
                CLK_SET_RATE_PARENT, 0xa0, 4, CLK_GATE_SET_TO_DISABLE, },
+       /* gsf */
+       { HIX5HD2_FWD_BUS_CLK, "clk_fwd_bus", NULL, 0, 0xcc, 0, 0, },
+       { HIX5HD2_FWD_SYS_CLK, "clk_fwd_sys", "clk_fwd_bus", 0, 0xcc, 5, 0, },
+       { HIX5HD2_MAC0_PHY_CLK, "clk_fephy", "clk_fwd_sys",
+                CLK_SET_RATE_PARENT, 0x120, 0, 0, },
+       /* wdg0 */
+       { HIX5HD2_WDG0_CLK, "clk_wdg0", "24m",
+               CLK_SET_RATE_PARENT, 0x178, 0, 0, },
+       { HIX5HD2_WDG0_RST, "rst_wdg0", "clk_wdg0",
+               CLK_SET_RATE_PARENT, 0x178, 4, CLK_GATE_SET_TO_DISABLE, },
+       /* I2C */
+       {HIX5HD2_I2C0_CLK, "clk_i2c0", "100m",
+                CLK_SET_RATE_PARENT, 0x06c, 4, 0, },
+       {HIX5HD2_I2C0_RST, "rst_i2c0", "clk_i2c0",
+                CLK_SET_RATE_PARENT, 0x06c, 5, CLK_GATE_SET_TO_DISABLE, },
+       {HIX5HD2_I2C1_CLK, "clk_i2c1", "100m",
+                CLK_SET_RATE_PARENT, 0x06c, 8, 0, },
+       {HIX5HD2_I2C1_RST, "rst_i2c1", "clk_i2c1",
+                CLK_SET_RATE_PARENT, 0x06c, 9, CLK_GATE_SET_TO_DISABLE, },
+       {HIX5HD2_I2C2_CLK, "clk_i2c2", "100m",
+                CLK_SET_RATE_PARENT, 0x06c, 12, 0, },
+       {HIX5HD2_I2C2_RST, "rst_i2c2", "clk_i2c2",
+                CLK_SET_RATE_PARENT, 0x06c, 13, CLK_GATE_SET_TO_DISABLE, },
+       {HIX5HD2_I2C3_CLK, "clk_i2c3", "100m",
+                CLK_SET_RATE_PARENT, 0x06c, 16, 0, },
+       {HIX5HD2_I2C3_RST, "rst_i2c3", "clk_i2c3",
+                CLK_SET_RATE_PARENT, 0x06c, 17, CLK_GATE_SET_TO_DISABLE, },
+       {HIX5HD2_I2C4_CLK, "clk_i2c4", "100m",
+                CLK_SET_RATE_PARENT, 0x06c, 20, 0, },
+       {HIX5HD2_I2C4_RST, "rst_i2c4", "clk_i2c4",
+                CLK_SET_RATE_PARENT, 0x06c, 21, CLK_GATE_SET_TO_DISABLE, },
+       {HIX5HD2_I2C5_CLK, "clk_i2c5", "100m",
+                CLK_SET_RATE_PARENT, 0x06c, 0, 0, },
+       {HIX5HD2_I2C5_RST, "rst_i2c5", "clk_i2c5",
+                CLK_SET_RATE_PARENT, 0x06c, 1, CLK_GATE_SET_TO_DISABLE, },
 };
 
+enum hix5hd2_clk_type {
+       TYPE_COMPLEX,
+       TYPE_ETHER,
+};
+
+struct hix5hd2_complex_clock {
+       const char      *name;
+       const char      *parent_name;
+       u32             id;
+       u32             ctrl_reg;
+       u32             ctrl_clk_mask;
+       u32             ctrl_rst_mask;
+       u32             phy_reg;
+       u32             phy_clk_mask;
+       u32             phy_rst_mask;
+       enum hix5hd2_clk_type type;
+};
+
+struct hix5hd2_clk_complex {
+       struct clk_hw   hw;
+       u32             id;
+       void __iomem    *ctrl_reg;
+       u32             ctrl_clk_mask;
+       u32             ctrl_rst_mask;
+       void __iomem    *phy_reg;
+       u32             phy_clk_mask;
+       u32             phy_rst_mask;
+};
+
+static struct hix5hd2_complex_clock hix5hd2_complex_clks[] __initdata = {
+       {"clk_mac0", "clk_fephy", HIX5HD2_MAC0_CLK,
+               0xcc, 0xa, 0x500, 0x120, 0, 0x10, TYPE_ETHER},
+       {"clk_mac1", "clk_fwd_sys", HIX5HD2_MAC1_CLK,
+               0xcc, 0x14, 0xa00, 0x168, 0x2, 0, TYPE_ETHER},
+       {"clk_sata", NULL, HIX5HD2_SATA_CLK,
+               0xa8, 0x1f, 0x300, 0xac, 0x1, 0x0, TYPE_COMPLEX},
+       {"clk_usb", NULL, HIX5HD2_USB_CLK,
+               0xb8, 0xff, 0x3f000, 0xbc, 0x7, 0x3f00, TYPE_COMPLEX},
+};
+
+#define to_complex_clk(_hw) container_of(_hw, struct hix5hd2_clk_complex, hw)
+
+static int clk_ether_prepare(struct clk_hw *hw)
+{
+       struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+       u32 val;
+
+       val = readl_relaxed(clk->ctrl_reg);
+       val |= clk->ctrl_clk_mask | clk->ctrl_rst_mask;
+       writel_relaxed(val, clk->ctrl_reg);
+       val &= ~(clk->ctrl_rst_mask);
+       writel_relaxed(val, clk->ctrl_reg);
+
+       val = readl_relaxed(clk->phy_reg);
+       val |= clk->phy_clk_mask;
+       val &= ~(clk->phy_rst_mask);
+       writel_relaxed(val, clk->phy_reg);
+       mdelay(10);
+
+       val &= ~(clk->phy_clk_mask);
+       val |= clk->phy_rst_mask;
+       writel_relaxed(val, clk->phy_reg);
+       mdelay(10);
+
+       val |= clk->phy_clk_mask;
+       val &= ~(clk->phy_rst_mask);
+       writel_relaxed(val, clk->phy_reg);
+       mdelay(30);
+       return 0;
+}
+
+static void clk_ether_unprepare(struct clk_hw *hw)
+{
+       struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+       u32 val;
+
+       val = readl_relaxed(clk->ctrl_reg);
+       val &= ~(clk->ctrl_clk_mask);
+       writel_relaxed(val, clk->ctrl_reg);
+}
+
+static struct clk_ops clk_ether_ops = {
+       .prepare = clk_ether_prepare,
+       .unprepare = clk_ether_unprepare,
+};
+
+static int clk_complex_enable(struct clk_hw *hw)
+{
+       struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+       u32 val;
+
+       val = readl_relaxed(clk->ctrl_reg);
+       val |= clk->ctrl_clk_mask;
+       val &= ~(clk->ctrl_rst_mask);
+       writel_relaxed(val, clk->ctrl_reg);
+
+       val = readl_relaxed(clk->phy_reg);
+       val |= clk->phy_clk_mask;
+       val &= ~(clk->phy_rst_mask);
+       writel_relaxed(val, clk->phy_reg);
+
+       return 0;
+}
+
+static void clk_complex_disable(struct clk_hw *hw)
+{
+       struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+       u32 val;
+
+       val = readl_relaxed(clk->ctrl_reg);
+       val |= clk->ctrl_rst_mask;
+       val &= ~(clk->ctrl_clk_mask);
+       writel_relaxed(val, clk->ctrl_reg);
+
+       val = readl_relaxed(clk->phy_reg);
+       val |= clk->phy_rst_mask;
+       val &= ~(clk->phy_clk_mask);
+       writel_relaxed(val, clk->phy_reg);
+}
+
+static struct clk_ops clk_complex_ops = {
+       .enable = clk_complex_enable,
+       .disable = clk_complex_disable,
+};
+
+void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks,
+                                        int nums, struct hisi_clock_data *data)
+{
+       void __iomem *base = data->base;
+       int i;
+
+       for (i = 0; i < nums; i++) {
+               struct hix5hd2_clk_complex *p_clk;
+               struct clk *clk;
+               struct clk_init_data init;
+
+               p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL);
+               if (!p_clk)
+                       return;
+
+               init.name = clks[i].name;
+               if (clks[i].type == TYPE_ETHER)
+                       init.ops = &clk_ether_ops;
+               else
+                       init.ops = &clk_complex_ops;
+
+               init.flags = CLK_IS_BASIC;
+               init.parent_names =
+                       (clks[i].parent_name ? &clks[i].parent_name : NULL);
+               init.num_parents = (clks[i].parent_name ? 1 : 0);
+
+               p_clk->ctrl_reg = base + clks[i].ctrl_reg;
+               p_clk->ctrl_clk_mask = clks[i].ctrl_clk_mask;
+               p_clk->ctrl_rst_mask = clks[i].ctrl_rst_mask;
+               p_clk->phy_reg = base + clks[i].phy_reg;
+               p_clk->phy_clk_mask = clks[i].phy_clk_mask;
+               p_clk->phy_rst_mask = clks[i].phy_rst_mask;
+               p_clk->hw.init = &init;
+
+               clk = clk_register(NULL, &p_clk->hw);
+               if (IS_ERR(clk)) {
+                       kfree(p_clk);
+                       pr_err("%s: failed to register clock %s\n",
+                              __func__, clks[i].name);
+                       continue;
+               }
+
+               data->clk_data.clks[clks[i].id] = clk;
+       }
+}
+
 static void __init hix5hd2_clk_init(struct device_node *np)
 {
        struct hisi_clock_data *clk_data;
@@ -96,6 +313,9 @@ static void __init hix5hd2_clk_init(struct device_node *np)
                                        clk_data);
        hisi_clk_register_gate(hix5hd2_gate_clks,
                        ARRAY_SIZE(hix5hd2_gate_clks), clk_data);
+       hix5hd2_clk_register_complex(hix5hd2_complex_clks,
+                                    ARRAY_SIZE(hix5hd2_complex_clks),
+                                    clk_data);
 }
 
 CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init);
index bef198a83863aaa79e21ca1c4e1401238cae31b7..756f0f39d6a3d9f4d3ffcd19a9eae6166c7ba3e3 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 #define SARL                           0       /* Low part [0:31] */
+#define         SARL_A370_SSCG_ENABLE          BIT(10)
 #define         SARL_A370_PCLK_FREQ_OPT        11
 #define         SARL_A370_PCLK_FREQ_OPT_MASK   0xF
 #define         SARL_A370_FAB_FREQ_OPT         15
@@ -133,10 +134,17 @@ static void __init a370_get_clk_ratio(
        }
 }
 
+static bool a370_is_sscg_enabled(void __iomem *sar)
+{
+       return !(readl(sar) & SARL_A370_SSCG_ENABLE);
+}
+
 static const struct coreclk_soc_desc a370_coreclks = {
        .get_tclk_freq = a370_get_tclk_freq,
        .get_cpu_freq = a370_get_cpu_freq,
        .get_clk_ratio = a370_get_clk_ratio,
+       .is_sscg_enabled = a370_is_sscg_enabled,
+       .fix_sscg_deviation = kirkwood_fix_sscg_deviation,
        .ratios = a370_coreclk_ratios,
        .num_ratios = ARRAY_SIZE(a370_coreclk_ratios),
 };
index c991a4d95e108d594f7ab0930a202e7eb7ea7fe3..c7af2242b796866a9367b02c0a74827499901a1f 100644 (file)
  * all modified at the same time, and not separately as for the Armada
  * 370 or the Armada XP SoCs.
  *
- * SAR0[21:17]   : CPU frequency    DDR frequency   L2 frequency
+ * SAR1[21:17]   : CPU frequency    DDR frequency   L2 frequency
  *              6   =  400 MHz     400 MHz         200 MHz
  *              15  =  600 MHz     600 MHz         300 MHz
  *              21  =  800 MHz     534 MHz         400 MHz
  *              25  = 1000 MHz     500 MHz         500 MHz
  *              others reserved.
  *
- * SAR0[22]   : TCLK frequency
+ * SAR1[22]   : TCLK frequency
  *              0 = 166 MHz
  *              1 = 200 MHz
  */
index 25ceccf939ad2f33cdc71907d91e68b8e69a64be..b7fcb469c87af73e6c6f45dcb1b98e188d99c0f8 100644 (file)
  * Core Clocks
  */
 
+#define SSCG_CONF_MODE(reg)    (((reg) >> 16) & 0x3)
+#define SSCG_SPREAD_DOWN       0x0
+#define SSCG_SPREAD_UP         0x1
+#define SSCG_SPREAD_CENTRAL    0x2
+#define SSCG_CONF_LOW(reg)     (((reg) >> 8) & 0xFF)
+#define SSCG_CONF_HIGH(reg)    ((reg) & 0xFF)
+
 static struct clk_onecell_data clk_data;
 
+/*
+ * This function can be used by the Kirkwood, the Armada 370, the
+ * Armada XP and the Armada 375 SoC. The name of the function was
+ * chosen following the dt convention: using the first known SoC
+ * compatible with it.
+ */
+u32 kirkwood_fix_sscg_deviation(u32 system_clk)
+{
+       struct device_node *sscg_np = NULL;
+       void __iomem *sscg_map;
+       u32 sscg_reg;
+       s32 low_bound, high_bound;
+       u64 freq_swing_half;
+
+       sscg_np = of_find_node_by_name(NULL, "sscg");
+       if (sscg_np == NULL) {
+               pr_err("cannot get SSCG register node\n");
+               return system_clk;
+       }
+
+       sscg_map = of_iomap(sscg_np, 0);
+       if (sscg_map == NULL) {
+               pr_err("cannot map SSCG register\n");
+               goto out;
+       }
+
+       sscg_reg = readl(sscg_map);
+       high_bound = SSCG_CONF_HIGH(sscg_reg);
+       low_bound = SSCG_CONF_LOW(sscg_reg);
+
+       if ((high_bound - low_bound) <= 0)
+               goto out;
+       /*
+        * From Marvell engineer we got the following formula (when
+        * this code was written, the datasheet was erroneous)
+        * Spread percentage = 1/96 * (H - L) / H
+        * H = SSCG_High_Boundary
+        * L = SSCG_Low_Boundary
+        *
+        * As the deviation is half of spread then it lead to the
+        * following formula in the code.
+        *
+        * To avoid an overflow and not lose any significant digit in
+        * the same time we have to use a 64 bit integer.
+        */
+
+       freq_swing_half = (((u64)high_bound - (u64)low_bound)
+                       * (u64)system_clk);
+       do_div(freq_swing_half, (2 * 96 * high_bound));
+
+       switch (SSCG_CONF_MODE(sscg_reg)) {
+       case SSCG_SPREAD_DOWN:
+               system_clk -= freq_swing_half;
+               break;
+       case SSCG_SPREAD_UP:
+               system_clk += freq_swing_half;
+               break;
+       case SSCG_SPREAD_CENTRAL:
+       default:
+               break;
+       }
+
+       iounmap(sscg_map);
+
+out:
+       of_node_put(sscg_np);
+
+       return system_clk;
+}
+
 void __init mvebu_coreclk_setup(struct device_node *np,
                                const struct coreclk_soc_desc *desc)
 {
@@ -62,6 +139,11 @@ void __init mvebu_coreclk_setup(struct device_node *np,
        of_property_read_string_index(np, "clock-output-names", 1,
                                      &cpuclk_name);
        rate = desc->get_cpu_freq(base);
+
+       if (desc->is_sscg_enabled && desc->fix_sscg_deviation
+               && desc->is_sscg_enabled(base))
+               rate = desc->fix_sscg_deviation(rate);
+
        clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
                                                   CLK_IS_ROOT, rate);
        WARN_ON(IS_ERR(clk_data.clks[1]));
@@ -89,8 +171,10 @@ void __init mvebu_coreclk_setup(struct device_node *np,
  * Clock Gating Control
  */
 
+DEFINE_SPINLOCK(ctrl_gating_lock);
+
 struct clk_gating_ctrl {
-       spinlock_t lock;
+       spinlock_t *lock;
        struct clk **gates;
        int num_gates;
 };
@@ -138,7 +222,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
        if (WARN_ON(!ctrl))
                goto ctrl_out;
 
-       spin_lock_init(&ctrl->lock);
+       /* lock must already be initialized */
+       ctrl->lock = &ctrl_gating_lock;
 
        /* Count, allocate, and register clock gates */
        for (n = 0; desc[n].name;)
@@ -155,7 +240,7 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
                        (desc[n].parent) ? desc[n].parent : default_parent;
                ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent,
                                        desc[n].flags, base, desc[n].bit_idx,
-                                       0, &ctrl->lock);
+                                       0, ctrl->lock);
                WARN_ON(IS_ERR(ctrl->gates[n]));
        }
 
index f968b4d9df922cff17e9f404ca211c3aa56e53f1..783b5631a453fe614e070a51e760e1004d2580e1 100644 (file)
@@ -17,6 +17,8 @@
 
 #include <linux/kernel.h>
 
+extern spinlock_t ctrl_gating_lock;
+
 struct device_node;
 
 struct coreclk_ratio {
@@ -28,6 +30,8 @@ struct coreclk_soc_desc {
        u32 (*get_tclk_freq)(void __iomem *sar);
        u32 (*get_cpu_freq)(void __iomem *sar);
        void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
+       bool (*is_sscg_enabled)(void __iomem *sar);
+       u32 (*fix_sscg_deviation)(u32 system_clk);
        const struct coreclk_ratio *ratios;
        int num_ratios;
 };
@@ -45,4 +49,9 @@ void __init mvebu_coreclk_setup(struct device_node *np,
 void __init mvebu_clk_gating_setup(struct device_node *np,
                                   const struct clk_gating_soc_desc *desc);
 
+/*
+ * This function is shared among the Kirkwood, Armada 370, Armada XP
+ * and Armada 375 SoC
+ */
+u32 kirkwood_fix_sscg_deviation(u32 system_clk);
 #endif
index ddb666a86500964201db0ad6d3a3724511302b59..99550f25975ea74b8963bb77910b5cf1614f3594 100644 (file)
  */
 
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/clk-provider.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include "common.h"
 
 /*
@@ -214,7 +216,6 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = {
        { "runit", NULL, 7, 0 },
        { "xor0", NULL, 8, 0 },
        { "audio", NULL, 9, 0 },
-       { "powersave", "cpuclk", 11, 0 },
        { "sata0", NULL, 14, 0 },
        { "sata1", NULL, 15, 0 },
        { "xor1", NULL, 16, 0 },
@@ -225,6 +226,101 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = {
        { }
 };
 
+
+/*
+ * Clock Muxing Control
+ */
+
+struct clk_muxing_soc_desc {
+       const char *name;
+       const char **parents;
+       int num_parents;
+       int shift;
+       int width;
+       unsigned long flags;
+};
+
+struct clk_muxing_ctrl {
+       spinlock_t *lock;
+       struct clk **muxes;
+       int num_muxes;
+};
+
+static const char *powersave_parents[] = {
+       "cpuclk",
+       "ddrclk",
+};
+
+static const struct clk_muxing_soc_desc kirkwood_mux_desc[] __initconst = {
+       { "powersave", powersave_parents, ARRAY_SIZE(powersave_parents),
+               11, 1, 0 },
+};
+
+#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
+
+static struct clk *clk_muxing_get_src(
+       struct of_phandle_args *clkspec, void *data)
+{
+       struct clk_muxing_ctrl *ctrl = (struct clk_muxing_ctrl *)data;
+       int n;
+
+       if (clkspec->args_count < 1)
+               return ERR_PTR(-EINVAL);
+
+       for (n = 0; n < ctrl->num_muxes; n++) {
+               struct clk_mux *mux =
+                       to_clk_mux(__clk_get_hw(ctrl->muxes[n]));
+               if (clkspec->args[0] == mux->shift)
+                       return ctrl->muxes[n];
+       }
+       return ERR_PTR(-ENODEV);
+}
+
+static void __init kirkwood_clk_muxing_setup(struct device_node *np,
+                                  const struct clk_muxing_soc_desc *desc)
+{
+       struct clk_muxing_ctrl *ctrl;
+       void __iomem *base;
+       int n;
+
+       base = of_iomap(np, 0);
+       if (WARN_ON(!base))
+               return;
+
+       ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+       if (WARN_ON(!ctrl))
+               goto ctrl_out;
+
+       /* lock must already be initialized */
+       ctrl->lock = &ctrl_gating_lock;
+
+       /* Count, allocate, and register clock muxes */
+       for (n = 0; desc[n].name;)
+               n++;
+
+       ctrl->num_muxes = n;
+       ctrl->muxes = kcalloc(ctrl->num_muxes, sizeof(struct clk *),
+                       GFP_KERNEL);
+       if (WARN_ON(!ctrl->muxes))
+               goto muxes_out;
+
+       for (n = 0; n < ctrl->num_muxes; n++) {
+               ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name,
+                               desc[n].parents, desc[n].num_parents,
+                               desc[n].flags, base, desc[n].shift,
+                               desc[n].width, desc[n].flags, ctrl->lock);
+               WARN_ON(IS_ERR(ctrl->muxes[n]));
+       }
+
+       of_clk_add_provider(np, clk_muxing_get_src, ctrl);
+
+       return;
+muxes_out:
+       kfree(ctrl);
+ctrl_out:
+       iounmap(base);
+}
+
 static void __init kirkwood_clk_init(struct device_node *np)
 {
        struct device_node *cgnp =
@@ -236,8 +332,10 @@ static void __init kirkwood_clk_init(struct device_node *np)
        else
                mvebu_coreclk_setup(np, &kirkwood_coreclks);
 
-       if (cgnp)
+       if (cgnp) {
                mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc);
+               kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc);
+       }
 }
 CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock",
               kirkwood_clk_init);
diff --git a/drivers/clk/pxa/Makefile b/drivers/clk/pxa/Makefile
new file mode 100644 (file)
index 0000000..4ff2abc
--- /dev/null
@@ -0,0 +1,2 @@
+obj-y                          += clk-pxa.o
+obj-$(CONFIG_PXA27x)           += clk-pxa27x.o
diff --git a/drivers/clk/pxa/clk-pxa.c b/drivers/clk/pxa/clk-pxa.c
new file mode 100644 (file)
index 0000000..ef3c053
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Marvell PXA family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Common clock code for PXA clocks ("CKEN" type clocks + DT)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/pxa-clock.h>
+#include "clk-pxa.h"
+
+DEFINE_SPINLOCK(lock);
+
+static struct clk *pxa_clocks[CLK_MAX];
+static struct clk_onecell_data onecell_data = {
+       .clks = pxa_clocks,
+       .clk_num = CLK_MAX,
+};
+
+#define to_pxa_clk(_hw) container_of(_hw, struct pxa_clk_cken, hw)
+
+static unsigned long cken_recalc_rate(struct clk_hw *hw,
+                                     unsigned long parent_rate)
+{
+       struct pxa_clk_cken *pclk = to_pxa_clk(hw);
+       struct clk_fixed_factor *fix;
+
+       if (!pclk->is_in_low_power || pclk->is_in_low_power())
+               fix = &pclk->lp;
+       else
+               fix = &pclk->hp;
+       fix->hw.clk = hw->clk;
+       return clk_fixed_factor_ops.recalc_rate(&fix->hw, parent_rate);
+}
+
+static struct clk_ops cken_rate_ops = {
+       .recalc_rate = cken_recalc_rate,
+};
+
+static u8 cken_get_parent(struct clk_hw *hw)
+{
+       struct pxa_clk_cken *pclk = to_pxa_clk(hw);
+
+       if (!pclk->is_in_low_power)
+               return 0;
+       return pclk->is_in_low_power() ? 0 : 1;
+}
+
+static struct clk_ops cken_mux_ops = {
+       .get_parent = cken_get_parent,
+       .set_parent = dummy_clk_set_parent,
+};
+
+void __init clkdev_pxa_register(int ckid, const char *con_id,
+                               const char *dev_id, struct clk *clk)
+{
+       if (!IS_ERR(clk) && (ckid != CLK_NONE))
+               pxa_clocks[ckid] = clk;
+       if (!IS_ERR(clk))
+               clk_register_clkdev(clk, con_id, dev_id);
+}
+
+int __init clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks)
+{
+       int i;
+       struct pxa_clk_cken *pclk;
+       struct clk *clk;
+
+       for (i = 0; i < nb_clks; i++) {
+               pclk = clks + i;
+               pclk->gate.lock = &lock;
+               clk = clk_register_composite(NULL, pclk->name,
+                                            pclk->parent_names, 2,
+                                            &pclk->hw, &cken_mux_ops,
+                                            &pclk->hw, &cken_rate_ops,
+                                            &pclk->gate.hw, &clk_gate_ops,
+                                            pclk->flags);
+               clkdev_pxa_register(pclk->ckid, pclk->con_id, pclk->dev_id,
+                                   clk);
+       }
+       return 0;
+}
+
+static void __init pxa_dt_clocks_init(struct device_node *np)
+{
+       of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data);
+}
+CLK_OF_DECLARE(pxa_clks, "marvell,pxa-clocks", pxa_dt_clocks_init);
diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h
new file mode 100644 (file)
index 0000000..5fe219d
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Marvell PXA family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Common clock code for PXA clocks ("CKEN" type clocks + DT)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#ifndef _CLK_PXA_
+#define _CLK_PXA_
+
+#define PARENTS(name) \
+       static const char *name ## _parents[] __initconst
+#define MUX_RO_RATE_RO_OPS(name, clk_name)                     \
+       static struct clk_hw name ## _mux_hw;                   \
+       static struct clk_hw name ## _rate_hw;                  \
+       static struct clk_ops name ## _mux_ops = {              \
+               .get_parent = name ## _get_parent,              \
+               .set_parent = dummy_clk_set_parent,             \
+       };                                                      \
+       static struct clk_ops name ## _rate_ops = {             \
+               .recalc_rate = name ## _get_rate,               \
+       };                                                      \
+       static struct clk *clk_register_ ## name(void)          \
+       {                                                       \
+               return clk_register_composite(NULL, clk_name,   \
+                       name ## _parents,                       \
+                       ARRAY_SIZE(name ## _parents),           \
+                       &name ## _mux_hw, &name ## _mux_ops,    \
+                       &name ## _rate_hw, &name ## _rate_ops,  \
+                       NULL, NULL, CLK_GET_RATE_NOCACHE);      \
+       }
+
+#define RATE_RO_OPS(name, clk_name)                    \
+       static struct clk_hw name ## _rate_hw;                  \
+       static struct clk_ops name ## _rate_ops = {             \
+               .recalc_rate = name ## _get_rate,               \
+       };                                                      \
+       static struct clk *clk_register_ ## name(void)          \
+       {                                                       \
+               return clk_register_composite(NULL, clk_name,   \
+                       name ## _parents,                       \
+                       ARRAY_SIZE(name ## _parents),           \
+                       NULL, NULL,                             \
+                       &name ## _rate_hw, &name ## _rate_ops,  \
+                       NULL, NULL, CLK_GET_RATE_NOCACHE);      \
+       }
+
+/*
+ * CKEN clock type
+ * This clock takes it source from 2 possible parents :
+ *  - a low power parent
+ *  - a normal parent
+ *
+ *  +------------+     +-----------+
+ *  |  Low Power | --- | x mult_lp |
+ *  |    Clock   |     | / div_lp  |\
+ *  +------------+     +-----------+ \+-----+   +-----------+
+ *                                    | Mux |---| CKEN gate |
+ *  +------------+     +-----------+ /+-----+   +-----------+
+ *  | High Power |     | x mult_hp |/
+ *  |    Clock   | --- | / div_hp  |
+ *  +------------+     +-----------+
+ */
+struct pxa_clk_cken {
+       struct clk_hw hw;
+       int ckid;
+       const char *name;
+       const char *dev_id;
+       const char *con_id;
+       const char **parent_names;
+       struct clk_fixed_factor lp;
+       struct clk_fixed_factor hp;
+       struct clk_gate gate;
+       bool (*is_in_low_power)(void);
+       const unsigned long flags;
+};
+
+#define PXA_CKEN(_dev_id, _con_id, _name, parents, _mult_lp, _div_lp,  \
+                _mult_hp, _div_hp, is_lp, _cken_reg, _cken_bit, flag)  \
+       { .ckid = CLK_ ## _name, .name = #_name,                        \
+         .dev_id = _dev_id, .con_id = _con_id, .parent_names = parents,\
+         .lp = { .mult = _mult_lp, .div = _div_lp },                   \
+         .hp = { .mult = _mult_hp, .div = _div_hp },                   \
+         .is_in_low_power = is_lp,                                     \
+         .gate = { .reg = (void __iomem *)_cken_reg, .bit_idx = _cken_bit }, \
+         .flags = flag,                                                \
+       }
+#define PXA_CKEN_1RATE(dev_id, con_id, name, parents, cken_reg,                \
+                           cken_bit, flag)                             \
+       PXA_CKEN(dev_id, con_id, name, parents, 1, 1, 1, 1,             \
+                NULL, cken_reg, cken_bit, flag)
+
+static int dummy_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+       return 0;
+}
+
+extern void clkdev_pxa_register(int ckid, const char *con_id,
+                               const char *dev_id, struct clk *clk);
+extern int clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks);
+
+#endif
diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c
new file mode 100644 (file)
index 0000000..b345cc7
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Marvell PXA27x family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Heavily inspired from former arch/arm/mach-pxa/clock.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#include <linux/clk-provider.h>
+#include <mach/pxa2xx-regs.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/pxa-clock.h>
+#include "clk-pxa.h"
+
+#define KHz 1000
+#define MHz (1000 * 1000)
+
+enum {
+       PXA_CORE_13Mhz = 0,
+       PXA_CORE_RUN,
+       PXA_CORE_TURBO,
+};
+
+enum {
+       PXA_BUS_13Mhz = 0,
+       PXA_BUS_RUN,
+};
+
+enum {
+       PXA_LCD_13Mhz = 0,
+       PXA_LCD_RUN,
+};
+
+enum {
+       PXA_MEM_13Mhz = 0,
+       PXA_MEM_SYSTEM_BUS,
+       PXA_MEM_RUN,
+};
+
+static const char * const get_freq_khz[] = {
+       "core", "run", "cpll", "memory",
+       "system_bus"
+};
+
+/*
+ * Get the clock frequency as reflected by CCSR and the turbo flag.
+ * We assume these values have been applied via a fcs.
+ * If info is not 0 we also display the current settings.
+ */
+unsigned int pxa27x_get_clk_frequency_khz(int info)
+{
+       struct clk *clk;
+       unsigned long clks[5];
+       int i;
+
+       for (i = 0; i < 5; i++) {
+               clk = clk_get(NULL, get_freq_khz[i]);
+               if (IS_ERR(clk)) {
+                       clks[i] = 0;
+               } else {
+                       clks[i] = clk_get_rate(clk);
+                       clk_put(clk);
+               }
+       }
+       if (info) {
+               pr_info("Run Mode clock: %ld.%02ldMHz\n",
+                       clks[1] / 1000000, (clks[1] % 1000000) / 10000);
+               pr_info("Turbo Mode clock: %ld.%02ldMHz\n",
+                       clks[2] / 1000000, (clks[2] % 1000000) / 10000);
+               pr_info("Memory clock: %ld.%02ldMHz\n",
+                       clks[3] / 1000000, (clks[3] % 1000000) / 10000);
+               pr_info("System bus clock: %ld.%02ldMHz\n",
+                       clks[4] / 1000000, (clks[4] % 1000000) / 10000);
+       }
+       return (unsigned int)clks[0];
+}
+
+bool pxa27x_is_ppll_disabled(void)
+{
+       unsigned long ccsr = CCSR;
+
+       return ccsr & (1 << CCCR_PPDIS_BIT);
+}
+
+#define PXA27X_CKEN(dev_id, con_id, parents, mult_hp, div_hp,          \
+                   bit, is_lp, flags)                                  \
+       PXA_CKEN(dev_id, con_id, bit, parents, 1, 1, mult_hp, div_hp,   \
+                is_lp,  &CKEN, CKEN_ ## bit, flags)
+#define PXA27X_PBUS_CKEN(dev_id, con_id, bit, mult_hp, div_hp, delay)  \
+       PXA27X_CKEN(dev_id, con_id, pxa27x_pbus_parents, mult_hp,       \
+                   div_hp, bit, pxa27x_is_ppll_disabled, 0)
+
+PARENTS(pxa27x_pbus) = { "osc_13mhz", "ppll_312mhz" };
+PARENTS(pxa27x_sbus) = { "system_bus", "system_bus" };
+PARENTS(pxa27x_32Mhz_bus) = { "osc_32_768khz", "osc_32_768khz" };
+PARENTS(pxa27x_lcd_bus) = { "lcd_base", "lcd_base" };
+PARENTS(pxa27x_membus) = { "lcd_base", "lcd_base" };
+
+#define PXA27X_CKEN_1RATE(dev_id, con_id, bit, parents, delay)         \
+       PXA_CKEN_1RATE(dev_id, con_id, bit, parents,                    \
+                      &CKEN, CKEN_ ## bit, 0)
+#define PXA27X_CKEN_1RATE_AO(dev_id, con_id, bit, parents, delay)      \
+       PXA_CKEN_1RATE(dev_id, con_id, bit, parents,                    \
+                      &CKEN, CKEN_ ## bit, CLK_IGNORE_UNUSED)
+
+static struct pxa_clk_cken pxa27x_clocks[] = {
+       PXA27X_PBUS_CKEN("pxa2xx-uart.0", NULL, FFUART, 2, 42, 1),
+       PXA27X_PBUS_CKEN("pxa2xx-uart.1", NULL, BTUART, 2, 42, 1),
+       PXA27X_PBUS_CKEN("pxa2xx-uart.2", NULL, STUART, 2, 42, 1),
+       PXA27X_PBUS_CKEN("pxa2xx-i2s", NULL, I2S, 2, 51, 0),
+       PXA27X_PBUS_CKEN("pxa2xx-i2c.0", NULL, I2C, 2, 19, 0),
+       PXA27X_PBUS_CKEN("pxa27x-udc", NULL, USB, 2, 13, 5),
+       PXA27X_PBUS_CKEN("pxa2xx-mci.0", NULL, MMC, 2, 32, 0),
+       PXA27X_PBUS_CKEN("pxa2xx-ir", "FICPCLK", FICP, 2, 13, 0),
+       PXA27X_PBUS_CKEN("pxa27x-ohci", NULL, USBHOST, 2, 13, 0),
+       PXA27X_PBUS_CKEN("pxa2xx-i2c.1", NULL, PWRI2C, 1, 24, 0),
+       PXA27X_PBUS_CKEN("pxa27x-ssp.0", NULL, SSP1, 1, 24, 0),
+       PXA27X_PBUS_CKEN("pxa27x-ssp.1", NULL, SSP2, 1, 24, 0),
+       PXA27X_PBUS_CKEN("pxa27x-ssp.2", NULL, SSP3, 1, 24, 0),
+       PXA27X_PBUS_CKEN("pxa27x-pwm.0", NULL, PWM0, 1, 24, 0),
+       PXA27X_PBUS_CKEN("pxa27x-pwm.1", NULL, PWM1, 1, 24, 0),
+       PXA27X_PBUS_CKEN(NULL, "MSLCLK", MSL, 2, 13, 0),
+       PXA27X_PBUS_CKEN(NULL, "USIMCLK", USIM, 2, 13, 0),
+       PXA27X_PBUS_CKEN(NULL, "MSTKCLK", MEMSTK, 2, 32, 0),
+       PXA27X_PBUS_CKEN(NULL, "AC97CLK", AC97, 1, 1, 0),
+       PXA27X_PBUS_CKEN(NULL, "AC97CONFCLK", AC97CONF, 1, 1, 0),
+       PXA27X_PBUS_CKEN(NULL, "OSTIMER0", OSTIMER, 1, 96, 0),
+
+       PXA27X_CKEN_1RATE("pxa27x-keypad", NULL, KEYPAD,
+                         pxa27x_32Mhz_bus_parents, 0),
+       PXA27X_CKEN_1RATE(NULL, "IMCLK", IM, pxa27x_sbus_parents, 0),
+       PXA27X_CKEN_1RATE("pxa2xx-fb", NULL, LCD, pxa27x_lcd_bus_parents, 0),
+       PXA27X_CKEN_1RATE("pxa27x-camera.0", NULL, CAMERA,
+                         pxa27x_lcd_bus_parents, 0),
+       PXA27X_CKEN_1RATE_AO("pxa2xx-pcmcia", NULL, MEMC,
+                            pxa27x_membus_parents, 0),
+
+};
+
+static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw,
+       unsigned long parent_rate)
+{
+       unsigned long clkcfg;
+       unsigned int t, ht;
+       unsigned int l, L, n2, N;
+       unsigned long ccsr = CCSR;
+
+       asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+       t  = clkcfg & (1 << 0);
+       ht = clkcfg & (1 << 2);
+
+       l  = ccsr & CCSR_L_MASK;
+       n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT;
+       L  = l * parent_rate;
+       N  = (L * n2) / 2;
+
+       return t ? N : L;
+}
+PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" };
+RATE_RO_OPS(clk_pxa27x_cpll, "cpll");
+
+static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw,
+                                                 unsigned long parent_rate)
+{
+       unsigned int l, osc_forced;
+       unsigned long ccsr = CCSR;
+       unsigned long cccr = CCCR;
+
+       l  = ccsr & CCSR_L_MASK;
+       osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+       if (osc_forced) {
+               if (cccr & (1 << CCCR_LCD_26_BIT))
+                       return parent_rate * 2;
+               else
+                       return parent_rate;
+       }
+
+       if (l <= 7)
+               return parent_rate;
+       if (l <= 16)
+               return parent_rate / 2;
+       return parent_rate / 4;
+}
+
+static u8 clk_pxa27x_lcd_base_get_parent(struct clk_hw *hw)
+{
+       unsigned int osc_forced;
+       unsigned long ccsr = CCSR;
+
+       osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+       if (osc_forced)
+               return PXA_LCD_13Mhz;
+       else
+               return PXA_LCD_RUN;
+}
+
+PARENTS(clk_pxa27x_lcd_base) = { "osc_13mhz", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_lcd_base, "lcd_base");
+
+static void __init pxa27x_register_plls(void)
+{
+       clk_register_fixed_rate(NULL, "osc_13mhz", NULL,
+                               CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
+                               13 * MHz);
+       clk_register_fixed_rate(NULL, "osc_32_768khz", NULL,
+                               CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
+                               32768 * KHz);
+       clk_register_fixed_rate(NULL, "clk_dummy", NULL, CLK_IS_ROOT, 0);
+       clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1);
+}
+
+static unsigned long clk_pxa27x_core_get_rate(struct clk_hw *hw,
+                                             unsigned long parent_rate)
+{
+       unsigned long clkcfg;
+       unsigned int t, ht, b, osc_forced;
+       unsigned long ccsr = CCSR;
+
+       osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+       asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+       t  = clkcfg & (1 << 0);
+       ht = clkcfg & (1 << 2);
+       b  = clkcfg & (1 << 3);
+
+       if (osc_forced)
+               return parent_rate;
+       if (ht)
+               return parent_rate / 2;
+       else
+               return parent_rate;
+}
+
+static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw)
+{
+       unsigned long clkcfg;
+       unsigned int t, ht, b, osc_forced;
+       unsigned long ccsr = CCSR;
+
+       osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+       if (osc_forced)
+               return PXA_CORE_13Mhz;
+
+       asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+       t  = clkcfg & (1 << 0);
+       ht = clkcfg & (1 << 2);
+       b  = clkcfg & (1 << 3);
+
+       if (ht || t)
+               return PXA_CORE_TURBO;
+       return PXA_CORE_RUN;
+}
+PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core");
+
+static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
+{
+       unsigned long ccsr = CCSR;
+       unsigned int n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT;
+
+       return (parent_rate / n2) * 2;
+}
+PARENTS(clk_pxa27x_run) = { "cpll" };
+RATE_RO_OPS(clk_pxa27x_run, "run");
+
+static void __init pxa27x_register_core(void)
+{
+       clk_register_clk_pxa27x_cpll();
+       clk_register_clk_pxa27x_run();
+
+       clkdev_pxa_register(CLK_CORE, "core", NULL,
+                           clk_register_clk_pxa27x_core());
+}
+
+static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw,
+                                                   unsigned long parent_rate)
+{
+       unsigned long clkcfg;
+       unsigned int b, osc_forced;
+       unsigned long ccsr = CCSR;
+
+       osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+       asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+       b  = clkcfg & (1 << 3);
+
+       if (osc_forced)
+               return parent_rate;
+       if (b)
+               return parent_rate / 2;
+       else
+               return parent_rate;
+}
+
+static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw)
+{
+       unsigned int osc_forced;
+       unsigned long ccsr = CCSR;
+
+       osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+       if (osc_forced)
+               return PXA_BUS_13Mhz;
+       else
+               return PXA_BUS_RUN;
+}
+
+PARENTS(clk_pxa27x_system_bus) = { "osc_13mhz", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_system_bus, "system_bus");
+
+static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw,
+                                               unsigned long parent_rate)
+{
+       unsigned int a, l, osc_forced;
+       unsigned long cccr = CCCR;
+       unsigned long ccsr = CCSR;
+
+       osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+       a = cccr & CCCR_A_BIT;
+       l  = ccsr & CCSR_L_MASK;
+
+       if (osc_forced || a)
+               return parent_rate;
+       if (l <= 10)
+               return parent_rate;
+       if (l <= 20)
+               return parent_rate / 2;
+       return parent_rate / 4;
+}
+
+static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw)
+{
+       unsigned int osc_forced, a;
+       unsigned long cccr = CCCR;
+       unsigned long ccsr = CCSR;
+
+       osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+       a = cccr & CCCR_A_BIT;
+       if (osc_forced)
+               return PXA_MEM_13Mhz;
+       if (a)
+               return PXA_MEM_SYSTEM_BUS;
+       else
+               return PXA_MEM_RUN;
+}
+
+PARENTS(clk_pxa27x_memory) = { "osc_13mhz", "system_bus", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_memory, "memory");
+
+static void __init pxa27x_base_clocks_init(void)
+{
+       pxa27x_register_plls();
+       pxa27x_register_core();
+       clk_register_clk_pxa27x_system_bus();
+       clk_register_clk_pxa27x_memory();
+       clk_register_clk_pxa27x_lcd_base();
+}
+
+static int __init pxa27x_clocks_init(void)
+{
+       pxa27x_base_clocks_init();
+       return clk_pxa_cken_init(pxa27x_clocks, ARRAY_SIZE(pxa27x_clocks));
+}
+postcore_initcall(pxa27x_clocks_init);
index 9db03d3b1657302b5b4ef8e5cca4b19a87edd7e3..b823bc3b625067c3b64b32b79a0b8b3e17541096 100644 (file)
@@ -97,7 +97,7 @@ static unsigned long
 clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 {
        struct clk_pll *pll = to_clk_pll(hw);
-       u32 l, m, n;
+       u32 l, m, n, config;
        unsigned long rate;
        u64 tmp;
 
@@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
                do_div(tmp, n);
                rate += tmp;
        }
+       if (pll->post_div_width) {
+               regmap_read(pll->clkr.regmap, pll->config_reg, &config);
+               config >>= pll->post_div_shift;
+               config &= BIT(pll->post_div_width) - 1;
+               rate /= config + 1;
+       }
+
        return rate;
 }
 
+static const
+struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
+{
+       if (!f)
+               return NULL;
+
+       for (; f->freq; f++)
+               if (rate <= f->freq)
+                       return f;
+
+       return NULL;
+}
+
+static long
+clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate,
+                      unsigned long *p_rate, struct clk **p)
+{
+       struct clk_pll *pll = to_clk_pll(hw);
+       const struct pll_freq_tbl *f;
+
+       f = find_freq(pll->freq_tbl, rate);
+       if (!f)
+               return clk_pll_recalc_rate(hw, *p_rate);
+
+       return f->freq;
+}
+
+static int
+clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
+{
+       struct clk_pll *pll = to_clk_pll(hw);
+       const struct pll_freq_tbl *f;
+       bool enabled;
+       u32 mode;
+       u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;
+
+       f = find_freq(pll->freq_tbl, rate);
+       if (!f)
+               return -EINVAL;
+
+       regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
+       enabled = (mode & enable_mask) == enable_mask;
+
+       if (enabled)
+               clk_pll_disable(hw);
+
+       regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
+       regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
+       regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
+       regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits);
+
+       if (enabled)
+               clk_pll_enable(hw);
+
+       return 0;
+}
+
 const struct clk_ops clk_pll_ops = {
        .enable = clk_pll_enable,
        .disable = clk_pll_disable,
        .recalc_rate = clk_pll_recalc_rate,
+       .determine_rate = clk_pll_determine_rate,
+       .set_rate = clk_pll_set_rate,
 };
 EXPORT_SYMBOL_GPL(clk_pll_ops);
 
index 3003e9962472f3ae98ae55709d2d4f347d2ccf40..c9c0cda306d04e45548daa0d5b50f802ab2c04a9 100644 (file)
 #include <linux/clk-provider.h>
 #include "clk-regmap.h"
 
+/**
+ * struct pll_freq_tbl - PLL frequency table
+ * @l: L value
+ * @m: M value
+ * @n: N value
+ * @ibits: internal values
+ */
+struct pll_freq_tbl {
+       unsigned long freq;
+       u16 l;
+       u16 m;
+       u16 n;
+       u32 ibits;
+};
+
 /**
  * struct clk_pll - phase locked loop (PLL)
  * @l_reg: L register
@@ -26,6 +41,7 @@
  * @mode_reg: mode register
  * @status_reg: status register
  * @status_bit: ANDed with @status_reg to determine if PLL is enabled
+ * @freq_tbl: PLL frequency table
  * @hw: handle between common and hardware-specific interfaces
  */
 struct clk_pll {
@@ -36,6 +52,10 @@ struct clk_pll {
        u32     mode_reg;
        u32     status_reg;
        u8      status_bit;
+       u8      post_div_width;
+       u8      post_div_shift;
+
+       const struct pll_freq_tbl *freq_tbl;
 
        struct clk_regmap clkr;
 };
index b638c5846dbfb20e2cc20046afe9823927713921..b6e6959e89aafed9248ba6ddd882e0ece7a4deb1 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/div64.h>
 
 #include "clk-rcg.h"
+#include "common.h"
 
 static u32 ns_to_src(struct src_sel *s, u32 ns)
 {
@@ -67,16 +68,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw)
 {
        struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
        int num_parents = __clk_get_num_parents(hw->clk);
-       u32 ns, ctl;
+       u32 ns, reg;
        int bank;
        int i;
        struct src_sel *s;
 
-       regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
-       bank = reg_to_bank(rcg, ctl);
+       regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+       bank = reg_to_bank(rcg, reg);
        s = &rcg->s[bank];
 
-       regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
+       regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
        ns = ns_to_src(s, ns);
 
        for (i = 0; i < num_parents; i++)
@@ -192,90 +193,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val)
 
 static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
 {
-       u32 ns, md, ctl, *regp;
+       u32 ns, md, reg;
        int bank, new_bank;
        struct mn *mn;
        struct pre_div *p;
        struct src_sel *s;
        bool enabled;
-       u32 md_reg;
-       u32 bank_reg;
+       u32 md_reg, ns_reg;
        bool banked_mn = !!rcg->mn[1].width;
+       bool banked_p = !!rcg->p[1].pre_div_width;
        struct clk_hw *hw = &rcg->clkr.hw;
 
        enabled = __clk_is_enabled(hw->clk);
 
-       regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
-       regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
-
-       if (banked_mn) {
-               regp = &ctl;
-               bank_reg = rcg->clkr.enable_reg;
-       } else {
-               regp = &ns;
-               bank_reg = rcg->ns_reg;
-       }
-
-       bank = reg_to_bank(rcg, *regp);
+       regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+       bank = reg_to_bank(rcg, reg);
        new_bank = enabled ? !bank : bank;
 
+       ns_reg = rcg->ns_reg[new_bank];
+       regmap_read(rcg->clkr.regmap, ns_reg, &ns);
+
        if (banked_mn) {
                mn = &rcg->mn[new_bank];
                md_reg = rcg->md_reg[new_bank];
 
                ns |= BIT(mn->mnctr_reset_bit);
-               regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+               regmap_write(rcg->clkr.regmap, ns_reg, ns);
 
                regmap_read(rcg->clkr.regmap, md_reg, &md);
                md = mn_to_md(mn, f->m, f->n, md);
                regmap_write(rcg->clkr.regmap, md_reg, md);
 
                ns = mn_to_ns(mn, f->m, f->n, ns);
-               regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+               regmap_write(rcg->clkr.regmap, ns_reg, ns);
 
-               ctl = mn_to_reg(mn, f->m, f->n, ctl);
-               regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl);
+               /* Two NS registers means mode control is in NS register */
+               if (rcg->ns_reg[0] != rcg->ns_reg[1]) {
+                       ns = mn_to_reg(mn, f->m, f->n, ns);
+                       regmap_write(rcg->clkr.regmap, ns_reg, ns);
+               } else {
+                       reg = mn_to_reg(mn, f->m, f->n, reg);
+                       regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
+               }
 
                ns &= ~BIT(mn->mnctr_reset_bit);
-               regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
-       } else {
+               regmap_write(rcg->clkr.regmap, ns_reg, ns);
+       }
+
+       if (banked_p) {
                p = &rcg->p[new_bank];
                ns = pre_div_to_ns(p, f->pre_div - 1, ns);
        }
 
        s = &rcg->s[new_bank];
        ns = src_to_ns(s, s->parent_map[f->src], ns);
-       regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+       regmap_write(rcg->clkr.regmap, ns_reg, ns);
 
        if (enabled) {
-               *regp ^= BIT(rcg->mux_sel_bit);
-               regmap_write(rcg->clkr.regmap, bank_reg, *regp);
+               regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+               reg ^= BIT(rcg->mux_sel_bit);
+               regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
        }
 }
 
 static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
 {
        struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
-       u32 ns, ctl, md, reg;
+       u32 ns, md, reg;
        int bank;
        struct freq_tbl f = { 0 };
        bool banked_mn = !!rcg->mn[1].width;
+       bool banked_p = !!rcg->p[1].pre_div_width;
 
-       regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
-       regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
-       reg = banked_mn ? ctl : ns;
-
+       regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
        bank = reg_to_bank(rcg, reg);
 
+       regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
+
        if (banked_mn) {
                regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
                f.m = md_to_m(&rcg->mn[bank], md);
                f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m);
-       } else {
-               f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
        }
-       f.src = index;
 
+       if (banked_p)
+               f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
+
+       f.src = index;
        configure_bank(rcg, &f);
 
        return 0;
@@ -336,41 +340,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
        u32 m, n, pre_div, ns, md, mode, reg;
        int bank;
        struct mn *mn;
+       bool banked_p = !!rcg->p[1].pre_div_width;
        bool banked_mn = !!rcg->mn[1].width;
 
-       regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
-
-       if (banked_mn)
-               regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &reg);
-       else
-               reg = ns;
-
+       regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
        bank = reg_to_bank(rcg, reg);
 
+       regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
+       m = n = pre_div = mode = 0;
+
        if (banked_mn) {
                mn = &rcg->mn[bank];
                regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
                m = md_to_m(mn, md);
                n = ns_m_to_n(mn, ns, m);
+               /* Two NS registers means mode control is in NS register */
+               if (rcg->ns_reg[0] != rcg->ns_reg[1])
+                       reg = ns;
                mode = reg_to_mnctr_mode(mn, reg);
-               return calc_rate(parent_rate, m, n, mode, 0);
-       } else {
-               pre_div = ns_to_pre_div(&rcg->p[bank], ns);
-               return calc_rate(parent_rate, 0, 0, 0, pre_div);
        }
-}
 
-static const
-struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
-{
-       if (!f)
-               return NULL;
-
-       for (; f->freq; f++)
-               if (rate <= f->freq)
-                       return f;
+       if (banked_p)
+               pre_div = ns_to_pre_div(&rcg->p[bank], ns);
 
-       return NULL;
+       return calc_rate(parent_rate, m, n, mode, pre_div);
 }
 
 static long _freq_tbl_determine_rate(struct clk_hw *hw,
@@ -379,7 +372,7 @@ static long _freq_tbl_determine_rate(struct clk_hw *hw,
 {
        unsigned long clk_flags;
 
-       f = find_freq(f, rate);
+       f = qcom_find_freq(f, rate);
        if (!f)
                return -EINVAL;
 
@@ -477,7 +470,7 @@ static int clk_rcg_set_rate(struct clk_hw *hw, unsigned long rate,
        struct clk_rcg *rcg = to_clk_rcg(hw);
        const struct freq_tbl *f;
 
-       f = find_freq(rcg->freq_tbl, rate);
+       f = qcom_find_freq(rcg->freq_tbl, rate);
        if (!f)
                return -EINVAL;
 
@@ -497,7 +490,7 @@ static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
        struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
        const struct freq_tbl *f;
 
-       f = find_freq(rcg->freq_tbl, rate);
+       f = qcom_find_freq(rcg->freq_tbl, rate);
        if (!f)
                return -EINVAL;
 
index ba0523cefd2e4ad04fcb6a853e727263011333ee..687e41f91d7c9f90d99eb80acbd98086d237e4e5 100644 (file)
@@ -103,8 +103,9 @@ extern const struct clk_ops clk_rcg_bypass_ops;
  * struct clk_dyn_rcg - root clock generator with glitch free mux
  *
  * @mux_sel_bit: bit to switch glitch free mux
- * @ns_reg: NS register
+ * @ns_reg: NS0 and NS1 register
  * @md_reg: MD0 and MD1 register
+ * @bank_reg: register to XOR @mux_sel_bit into to switch glitch free mux
  * @mn: mn counter (banked)
  * @s: source selector (banked)
  * @freq_tbl: frequency table
@@ -113,8 +114,9 @@ extern const struct clk_ops clk_rcg_bypass_ops;
  *
  */
 struct clk_dyn_rcg {
-       u32     ns_reg;
+       u32     ns_reg[2];
        u32     md_reg[2];
+       u32     bank_reg;
 
        u8      mux_sel_bit;
 
index cd185d5cc67a3710f4a825c7ffd8bf4f66ae7f45..cfa9eb4fe9ca60c1420e7821f7d927e1fc82fb10 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/div64.h>
 
 #include "clk-rcg.h"
+#include "common.h"
 
 #define CMD_REG                        0x0
 #define CMD_UPDATE             BIT(0)
@@ -172,27 +173,13 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
        return calc_rate(parent_rate, m, n, mode, hid_div);
 }
 
-static const
-struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
-{
-       if (!f)
-               return NULL;
-
-       for (; f->freq; f++)
-               if (rate <= f->freq)
-                       return f;
-
-       /* Default to our fastest rate */
-       return f - 1;
-}
-
 static long _freq_tbl_determine_rate(struct clk_hw *hw,
                const struct freq_tbl *f, unsigned long rate,
                unsigned long *p_rate, struct clk **p)
 {
        unsigned long clk_flags;
 
-       f = find_freq(f, rate);
+       f = qcom_find_freq(f, rate);
        if (!f)
                return -EINVAL;
 
@@ -268,7 +255,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
        struct clk_rcg2 *rcg = to_clk_rcg2(hw);
        const struct freq_tbl *f;
 
-       f = find_freq(rcg->freq_tbl, rate);
+       f = qcom_find_freq(rcg->freq_tbl, rate);
        if (!f)
                return -EINVAL;
 
index eeb3eea01f4ca751944df67da5d4df65cabbb048..e20d947db3e50516a132f9e6cc17717da0abd23f 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/reset-controller.h>
 
 #include "common.h"
+#include "clk-rcg.h"
 #include "clk-regmap.h"
 #include "reset.h"
 
@@ -27,6 +28,21 @@ struct qcom_cc {
        struct clk *clks[];
 };
 
+const
+struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
+{
+       if (!f)
+               return NULL;
+
+       for (; f->freq; f++)
+               if (rate <= f->freq)
+                       return f;
+
+       /* Default to our fastest rate */
+       return f - 1;
+}
+EXPORT_SYMBOL_GPL(qcom_find_freq);
+
 struct regmap *
 qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
 {
index 2765e9d3da97ef5c23f976dd0e62fa4c34edf1d5..f519322acdf3be21d4ada6d014a517b459f956a6 100644 (file)
@@ -18,6 +18,7 @@ struct regmap_config;
 struct clk_regmap;
 struct qcom_reset_map;
 struct regmap;
+struct freq_tbl;
 
 struct qcom_cc_desc {
        const struct regmap_config *config;
@@ -27,6 +28,9 @@ struct qcom_cc_desc {
        size_t num_resets;
 };
 
+extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
+                                            unsigned long rate);
+
 extern struct regmap *qcom_cc_map(struct platform_device *pdev,
                                  const struct qcom_cc_desc *desc);
 extern int qcom_cc_really_probe(struct platform_device *pdev,
index 3b83b7dd78c7297b612c2c3720e8c86568ce447e..5cd62a709ac7d5baa494865b6a63aa2d533eb516 100644 (file)
 #include "clk-branch.h"
 #include "reset.h"
 
+static struct clk_pll pll0 = {
+       .l_reg = 0x30c4,
+       .m_reg = 0x30c8,
+       .n_reg = 0x30cc,
+       .config_reg = 0x30d4,
+       .mode_reg = 0x30c0,
+       .status_reg = 0x30d8,
+       .status_bit = 16,
+       .clkr.hw.init = &(struct clk_init_data){
+               .name = "pll0",
+               .parent_names = (const char *[]){ "pxo" },
+               .num_parents = 1,
+               .ops = &clk_pll_ops,
+       },
+};
+
+static struct clk_regmap pll0_vote = {
+       .enable_reg = 0x34c0,
+       .enable_mask = BIT(0),
+       .hw.init = &(struct clk_init_data){
+               .name = "pll0_vote",
+               .parent_names = (const char *[]){ "pll0" },
+               .num_parents = 1,
+               .ops = &clk_pll_vote_ops,
+       },
+};
+
 static struct clk_pll pll3 = {
        .l_reg = 0x3164,
        .m_reg = 0x3168,
@@ -154,7 +181,7 @@ static const u8 gcc_pxo_pll8_pll0[] = {
 static const char *gcc_pxo_pll8_pll0_map[] = {
        "pxo",
        "pll8_vote",
-       "pll0",
+       "pll0_vote",
 };
 
 static struct freq_tbl clk_tbl_gsbi_uart[] = {
@@ -2133,6 +2160,8 @@ static struct clk_branch usb_fs1_h_clk = {
 };
 
 static struct clk_regmap *gcc_ipq806x_clks[] = {
+       [PLL0] = &pll0.clkr,
+       [PLL0_VOTE] = &pll0_vote,
        [PLL3] = &pll3.clkr,
        [PLL8] = &pll8.clkr,
        [PLL8_VOTE] = &pll8_vote,
index 751eea376a2b078a7b8221608a31cf95b92d3a96..dab988ab8cf12740ac931c5f5efaa39b90887ec3 100644 (file)
@@ -3341,7 +3341,6 @@ static struct platform_driver mmcc_apq8084_driver = {
        .remove         = mmcc_apq8084_remove,
        .driver         = {
                .name   = "mmcc-apq8084",
-               .owner  = THIS_MODULE,
                .of_match_table = mmcc_apq8084_match_table,
        },
 };
index 2e80a219b8ead315a053676f1776c39363f12fda..e8b33bbc362f9aa552959fbd56d231dba86ee1f0 100644 (file)
@@ -773,9 +773,11 @@ static struct freq_tbl clk_tbl_gfx2d[] = {
 };
 
 static struct clk_dyn_rcg gfx2d0_src = {
-       .ns_reg = 0x0070,
+       .ns_reg[0] = 0x0070,
+       .ns_reg[1] = 0x0070,
        .md_reg[0] = 0x0064,
        .md_reg[1] = 0x0068,
+       .bank_reg = 0x0060,
        .mn[0] = {
                .mnctr_en_bit = 8,
                .mnctr_reset_bit = 25,
@@ -831,9 +833,11 @@ static struct clk_branch gfx2d0_clk = {
 };
 
 static struct clk_dyn_rcg gfx2d1_src = {
-       .ns_reg = 0x007c,
+       .ns_reg[0] = 0x007c,
+       .ns_reg[1] = 0x007c,
        .md_reg[0] = 0x0078,
        .md_reg[1] = 0x006c,
+       .bank_reg = 0x0074,
        .mn[0] = {
                .mnctr_en_bit = 8,
                .mnctr_reset_bit = 25,
@@ -930,9 +934,11 @@ static struct freq_tbl clk_tbl_gfx3d_8064[] = {
 };
 
 static struct clk_dyn_rcg gfx3d_src = {
-       .ns_reg = 0x008c,
+       .ns_reg[0] = 0x008c,
+       .ns_reg[1] = 0x008c,
        .md_reg[0] = 0x0084,
        .md_reg[1] = 0x0088,
+       .bank_reg = 0x0080,
        .mn[0] = {
                .mnctr_en_bit = 8,
                .mnctr_reset_bit = 25,
@@ -1006,9 +1012,11 @@ static struct freq_tbl clk_tbl_vcap[] = {
 };
 
 static struct clk_dyn_rcg vcap_src = {
-       .ns_reg = 0x021c,
+       .ns_reg[0] = 0x021c,
+       .ns_reg[1] = 0x021c,
        .md_reg[0] = 0x01ec,
        .md_reg[1] = 0x0218,
+       .bank_reg = 0x0178,
        .mn[0] = {
                .mnctr_en_bit = 8,
                .mnctr_reset_bit = 23,
@@ -1211,9 +1219,11 @@ static struct freq_tbl clk_tbl_mdp[] = {
 };
 
 static struct clk_dyn_rcg mdp_src = {
-       .ns_reg = 0x00d0,
+       .ns_reg[0] = 0x00d0,
+       .ns_reg[1] = 0x00d0,
        .md_reg[0] = 0x00c4,
        .md_reg[1] = 0x00c8,
+       .bank_reg = 0x00c0,
        .mn[0] = {
                .mnctr_en_bit = 8,
                .mnctr_reset_bit = 31,
@@ -1318,7 +1328,9 @@ static struct freq_tbl clk_tbl_rot[] = {
 };
 
 static struct clk_dyn_rcg rot_src = {
-       .ns_reg = 0x00e8,
+       .ns_reg[0] = 0x00e8,
+       .ns_reg[1] = 0x00e8,
+       .bank_reg = 0x00e8,
        .p[0] = {
                .pre_div_shift = 22,
                .pre_div_width = 4,
@@ -1542,9 +1554,11 @@ static struct freq_tbl clk_tbl_vcodec[] = {
 };
 
 static struct clk_dyn_rcg vcodec_src = {
-       .ns_reg = 0x0100,
+       .ns_reg[0] = 0x0100,
+       .ns_reg[1] = 0x0100,
        .md_reg[0] = 0x00fc,
        .md_reg[1] = 0x0128,
+       .bank_reg = 0x00f8,
        .mn[0] = {
                .mnctr_en_bit = 5,
                .mnctr_reset_bit = 31,
@@ -2679,7 +2693,6 @@ static struct platform_driver mmcc_msm8960_driver = {
        .remove         = mmcc_msm8960_remove,
        .driver         = {
                .name   = "mmcc-msm8960",
-               .owner  = THIS_MODULE,
                .of_match_table = mmcc_msm8960_match_table,
        },
 };
index bc8f519c47aad0054f675f6595f436f9a1363f6c..be94c54a9a4f72db338c07f260a69da0b706b561 100644 (file)
@@ -2570,7 +2570,6 @@ static struct platform_driver mmcc_msm8974_driver = {
        .remove         = mmcc_msm8974_remove,
        .driver         = {
                .name   = "mmcc-msm8974",
-               .owner  = THIS_MODULE,
                .of_match_table = mmcc_msm8974_match_table,
        },
 };
index ee6b077381e11193840e848ec822a86602c42413..bd8514d63634bdb78ae0e75f801be478265f5f91 100644 (file)
@@ -5,6 +5,7 @@
 obj-y  += clk-rockchip.o
 obj-y  += clk.o
 obj-y  += clk-pll.o
+obj-y  += clk-cpu.o
 obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
 
 obj-y  += clk-rk3188.o
diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c
new file mode 100644 (file)
index 0000000..75c8c45
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on clk/samsung/clk-cpu.c
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * A CPU clock is defined as a clock supplied to a CPU or a group of CPUs.
+ * The CPU clock is typically derived from a hierarchy of clock
+ * blocks which includes mux and divider blocks. There are a number of other
+ * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
+ * clock for CPU domain. The rates of these auxiliary clocks are related to the
+ * CPU clock rate and this relation is usually specified in the hardware manual
+ * of the SoC or supplied after the SoC characterization.
+ *
+ * The below implementation of the CPU clock allows the rate changes of the CPU
+ * clock and the corresponding rate changes of the auxillary clocks of the CPU
+ * domain. The platform clock driver provides a clock register configuration
+ * for each configurable rate which is then used to program the clock hardware
+ * registers to acheive a fast co-oridinated rate change for all the CPU domain
+ * clocks.
+ *
+ * On a rate change request for the CPU clock, the rate change is propagated
+ * upto the PLL supplying the clock to the CPU domain clock blocks. While the
+ * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
+ * alternate clock source. If required, the alternate clock source is divided
+ * down in order to keep the output clock rate within the previous OPP limits.
+ */
+
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include "clk.h"
+
+/**
+ * struct rockchip_cpuclk: information about clock supplied to a CPU core.
+ * @hw:                handle between ccf and cpu clock.
+ * @alt_parent:        alternate parent clock to use when switching the speed
+ *             of the primary parent clock.
+ * @reg_base:  base register for cpu-clock values.
+ * @clk_nb:    clock notifier registered for changes in clock speed of the
+ *             primary parent clock.
+ * @rate_count:        number of rates in the rate_table
+ * @rate_table:        pll-rates and their associated dividers
+ * @reg_data:  cpu-specific register settings
+ * @lock:      clock lock
+ */
+struct rockchip_cpuclk {
+       struct clk_hw                           hw;
+
+       struct clk_mux                          cpu_mux;
+       const struct clk_ops                    *cpu_mux_ops;
+
+       struct clk                              *alt_parent;
+       void __iomem                            *reg_base;
+       struct notifier_block                   clk_nb;
+       unsigned int                            rate_count;
+       struct rockchip_cpuclk_rate_table       *rate_table;
+       const struct rockchip_cpuclk_reg_data   *reg_data;
+       spinlock_t                              *lock;
+};
+
+#define to_rockchip_cpuclk_hw(hw) container_of(hw, struct rockchip_cpuclk, hw)
+#define to_rockchip_cpuclk_nb(nb) \
+                       container_of(nb, struct rockchip_cpuclk, clk_nb)
+
+static const struct rockchip_cpuclk_rate_table *rockchip_get_cpuclk_settings(
+                           struct rockchip_cpuclk *cpuclk, unsigned long rate)
+{
+       const struct rockchip_cpuclk_rate_table *rate_table =
+                                                       cpuclk->rate_table;
+       int i;
+
+       for (i = 0; i < cpuclk->rate_count; i++) {
+               if (rate == rate_table[i].prate)
+                       return &rate_table[i];
+       }
+
+       return NULL;
+}
+
+static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw);
+       const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
+       u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg);
+
+       clksel0 >>= reg_data->div_core_shift;
+       clksel0 &= reg_data->div_core_mask;
+       return parent_rate / (clksel0 + 1);
+}
+
+static const struct clk_ops rockchip_cpuclk_ops = {
+       .recalc_rate = rockchip_cpuclk_recalc_rate,
+};
+
+static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk,
+                               const struct rockchip_cpuclk_rate_table *rate)
+{
+       int i;
+
+       /* alternate parent is active now. set the dividers */
+       for (i = 0; i < ARRAY_SIZE(rate->divs); i++) {
+               const struct rockchip_cpuclk_clksel *clksel = &rate->divs[i];
+
+               if (!clksel->reg)
+                       continue;
+
+               pr_debug("%s: setting reg 0x%x to 0x%x\n",
+                        __func__, clksel->reg, clksel->val);
+               writel(clksel->val , cpuclk->reg_base + clksel->reg);
+       }
+}
+
+static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
+                                          struct clk_notifier_data *ndata)
+{
+       const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
+       unsigned long alt_prate, alt_div;
+
+       alt_prate = clk_get_rate(cpuclk->alt_parent);
+
+       spin_lock(cpuclk->lock);
+
+       /*
+        * If the old parent clock speed is less than the clock speed
+        * of the alternate parent, then it should be ensured that at no point
+        * the armclk speed is more than the old_rate until the dividers are
+        * set.
+        */
+       if (alt_prate > ndata->old_rate) {
+               /* calculate dividers */
+               alt_div =  DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1;
+               if (alt_div > reg_data->div_core_mask) {
+                       pr_warn("%s: limiting alt-divider %lu to %d\n",
+                               __func__, alt_div, reg_data->div_core_mask);
+                       alt_div = reg_data->div_core_mask;
+               }
+
+               /*
+                * Change parents and add dividers in a single transaction.
+                *
+                * NOTE: we do this in a single transaction so we're never
+                * dividing the primary parent by the extra dividers that were
+                * needed for the alt.
+                */
+               pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n",
+                        __func__, alt_div, alt_prate, ndata->old_rate);
+
+               writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask,
+                                             reg_data->div_core_shift) |
+                      HIWORD_UPDATE(1, 1, reg_data->mux_core_shift),
+                      cpuclk->reg_base + reg_data->core_reg);
+       } else {
+               /* select alternate parent */
+               writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift),
+                       cpuclk->reg_base + reg_data->core_reg);
+       }
+
+       spin_unlock(cpuclk->lock);
+       return 0;
+}
+
+static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
+                                           struct clk_notifier_data *ndata)
+{
+       const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
+       const struct rockchip_cpuclk_rate_table *rate;
+
+       rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate);
+       if (!rate) {
+               pr_err("%s: Invalid rate : %lu for cpuclk\n",
+                      __func__, ndata->new_rate);
+               return -EINVAL;
+       }
+
+       spin_lock(cpuclk->lock);
+
+       if (ndata->old_rate < ndata->new_rate)
+               rockchip_cpuclk_set_dividers(cpuclk, rate);
+
+       /*
+        * post-rate change event, re-mux to primary parent and remove dividers.
+        *
+        * NOTE: we do this in a single transaction so we're never dividing the
+        * primary parent by the extra dividers that were needed for the alt.
+        */
+
+       writel(HIWORD_UPDATE(0, reg_data->div_core_mask,
+                               reg_data->div_core_shift) |
+              HIWORD_UPDATE(0, 1, reg_data->mux_core_shift),
+              cpuclk->reg_base + reg_data->core_reg);
+
+       if (ndata->old_rate > ndata->new_rate)
+               rockchip_cpuclk_set_dividers(cpuclk, rate);
+
+       spin_unlock(cpuclk->lock);
+       return 0;
+}
+
+/*
+ * This clock notifier is called when the frequency of the parent clock
+ * of cpuclk is to be changed. This notifier handles the setting up all
+ * the divider clocks, remux to temporary parent and handling the safe
+ * frequency levels when using temporary parent.
+ */
+static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb,
+                                       unsigned long event, void *data)
+{
+       struct clk_notifier_data *ndata = data;
+       struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb);
+       int ret = 0;
+
+       pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
+                __func__, event, ndata->old_rate, ndata->new_rate);
+       if (event == PRE_RATE_CHANGE)
+               ret = rockchip_cpuclk_pre_rate_change(cpuclk, ndata);
+       else if (event == POST_RATE_CHANGE)
+               ret = rockchip_cpuclk_post_rate_change(cpuclk, ndata);
+
+       return notifier_from_errno(ret);
+}
+
+struct clk *rockchip_clk_register_cpuclk(const char *name,
+                       const char **parent_names, u8 num_parents,
+                       const struct rockchip_cpuclk_reg_data *reg_data,
+                       const struct rockchip_cpuclk_rate_table *rates,
+                       int nrates, void __iomem *reg_base, spinlock_t *lock)
+{
+       struct rockchip_cpuclk *cpuclk;
+       struct clk_init_data init;
+       struct clk *clk, *cclk;
+       int ret;
+
+       if (num_parents != 2) {
+               pr_err("%s: needs two parent clocks\n", __func__);
+               return ERR_PTR(-EINVAL);
+       }
+
+       cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
+       if (!cpuclk)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.parent_names = &parent_names[0];
+       init.num_parents = 1;
+       init.ops = &rockchip_cpuclk_ops;
+
+       /* only allow rate changes when we have a rate table */
+       init.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0;
+
+       /* disallow automatic parent changes by ccf */
+       init.flags |= CLK_SET_RATE_NO_REPARENT;
+
+       init.flags |= CLK_GET_RATE_NOCACHE;
+
+       cpuclk->reg_base = reg_base;
+       cpuclk->lock = lock;
+       cpuclk->reg_data = reg_data;
+       cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb;
+       cpuclk->hw.init = &init;
+
+       cpuclk->alt_parent = __clk_lookup(parent_names[1]);
+       if (!cpuclk->alt_parent) {
+               pr_err("%s: could not lookup alternate parent\n",
+                      __func__);
+               ret = -EINVAL;
+               goto free_cpuclk;
+       }
+
+       ret = clk_prepare_enable(cpuclk->alt_parent);
+       if (ret) {
+               pr_err("%s: could not enable alternate parent\n",
+                      __func__);
+               goto free_cpuclk;
+       }
+
+       clk = __clk_lookup(parent_names[0]);
+       if (!clk) {
+               pr_err("%s: could not lookup parent clock %s\n",
+                      __func__, parent_names[0]);
+               ret = -EINVAL;
+               goto free_cpuclk;
+       }
+
+       ret = clk_notifier_register(clk, &cpuclk->clk_nb);
+       if (ret) {
+               pr_err("%s: failed to register clock notifier for %s\n",
+                               __func__, name);
+               goto free_cpuclk;
+       }
+
+       if (nrates > 0) {
+               cpuclk->rate_count = nrates;
+               cpuclk->rate_table = kmemdup(rates,
+                                            sizeof(*rates) * nrates,
+                                            GFP_KERNEL);
+               if (!cpuclk->rate_table) {
+                       pr_err("%s: could not allocate memory for cpuclk rates\n",
+                              __func__);
+                       ret = -ENOMEM;
+                       goto unregister_notifier;
+               }
+       }
+
+       cclk = clk_register(NULL, &cpuclk->hw);
+       if (IS_ERR(clk)) {
+               pr_err("%s: could not register cpuclk %s\n", __func__,  name);
+               ret = PTR_ERR(clk);
+               goto free_rate_table;
+       }
+
+       return cclk;
+
+free_rate_table:
+       kfree(cpuclk->rate_table);
+unregister_notifier:
+       clk_notifier_unregister(clk, &cpuclk->clk_nb);
+free_cpuclk:
+       kfree(cpuclk);
+       return ERR_PTR(ret);
+}
index f2a1c7abf4d952c008c7e65c45e92fb53f47ff3e..a3e886a38480a75060d310bc128944e54169c80d 100644 (file)
@@ -34,7 +34,6 @@ struct rockchip_clk_pll {
        const struct clk_ops    *pll_mux_ops;
 
        struct notifier_block   clk_nb;
-       bool                    rate_change_remuxed;
 
        void __iomem            *reg_base;
        int                     lock_offset;
@@ -108,38 +107,6 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
        return -ETIMEDOUT;
 }
 
-/**
- * Set pll mux when changing the pll rate.
- * This makes sure to move the pll mux away from the actual pll before
- * changing its rate and back to the original parent after the change.
- */
-static int rockchip_pll_notifier_cb(struct notifier_block *nb,
-                                       unsigned long event, void *data)
-{
-       struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb);
-       struct clk_mux *pll_mux = &pll->pll_mux;
-       const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
-       int cur_parent;
-
-       switch (event) {
-       case PRE_RATE_CHANGE:
-               cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
-               if (cur_parent == PLL_MODE_NORM) {
-                       pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
-                       pll->rate_change_remuxed = 1;
-               }
-               break;
-       case POST_RATE_CHANGE:
-               if (pll->rate_change_remuxed) {
-                       pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
-                       pll->rate_change_remuxed = 0;
-               }
-               break;
-       }
-
-       return NOTIFY_OK;
-}
-
 /**
  * PLL used in RK3066, RK3188 and RK3288
  */
@@ -194,6 +161,10 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
        const struct rockchip_pll_rate_table *rate;
        unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
        struct regmap *grf = rockchip_clk_get_grf();
+       struct clk_mux *pll_mux = &pll->pll_mux;
+       const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
+       int rate_change_remuxed = 0;
+       int cur_parent;
        int ret;
 
        if (IS_ERR(grf)) {
@@ -216,6 +187,12 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
        pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n",
                 __func__, rate->rate, rate->nr, rate->no, rate->nf);
 
+       cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
+       if (cur_parent == PLL_MODE_NORM) {
+               pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
+               rate_change_remuxed = 1;
+       }
+
        /* enter reset mode */
        writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0),
               pll->reg_base + RK3066_PLLCON(3));
@@ -247,6 +224,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
                rockchip_rk3066_pll_set_rate(hw, old_rate, prate);
        }
 
+       if (rate_change_remuxed)
+               pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
+
        return ret;
 }
 
@@ -310,7 +290,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
        struct clk_mux *pll_mux;
        struct clk *pll_clk, *mux_clk;
        char pll_name[20];
-       int ret;
 
        if (num_parents != 2) {
                pr_err("%s: needs two parent clocks\n", __func__);
@@ -367,7 +346,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
        pll->lock_offset = grf_lock_offset;
        pll->lock_shift = lock_shift;
        pll->lock = lock;
-       pll->clk_nb.notifier_call = rockchip_pll_notifier_cb;
 
        pll_clk = clk_register(NULL, &pll->hw);
        if (IS_ERR(pll_clk)) {
@@ -377,14 +355,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
                goto err_pll;
        }
 
-       ret = clk_notifier_register(pll_clk, &pll->clk_nb);
-       if (ret) {
-               pr_err("%s: failed to register clock notifier for %s : %d\n",
-                               __func__, name, ret);
-               mux_clk = ERR_PTR(ret);
-               goto err_pll_notifier;
-       }
-
        /* create the mux on top of the real pll */
        pll->pll_mux_ops = &clk_mux_ops;
        pll_mux = &pll->pll_mux;
@@ -417,13 +387,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
        return mux_clk;
 
 err_mux:
-       ret = clk_notifier_unregister(pll_clk, &pll->clk_nb);
-       if (ret) {
-               pr_err("%s: could not unregister clock notifier in error path : %d\n",
-                      __func__, ret);
-               return mux_clk;
-       }
-err_pll_notifier:
        clk_unregister(pll_clk);
 err_pll:
        kfree(pll);
index a83a6d8d0fb6479b4592c672e059fe6e813bac11..beed49c79126bb1412cf7b217586aa8c0b6e532d 100644 (file)
@@ -19,6 +19,7 @@
 #include <dt-bindings/clock/rk3188-cru-common.h>
 #include "clk.h"
 
+#define RK3066_GRF_SOC_STATUS  0x15c
 #define RK3188_GRF_SOC_STATUS  0xac
 
 enum rk3188_plls {
@@ -100,6 +101,98 @@ struct rockchip_pll_rate_table rk3188_pll_rates[] = {
        { /* sentinel */ },
 };
 
+#define RK3066_DIV_CORE_PERIPH_MASK    0x3
+#define RK3066_DIV_CORE_PERIPH_SHIFT   6
+#define RK3066_DIV_ACLK_CORE_MASK      0x7
+#define RK3066_DIV_ACLK_CORE_SHIFT     0
+#define RK3066_DIV_ACLK_HCLK_MASK      0x3
+#define RK3066_DIV_ACLK_HCLK_SHIFT     8
+#define RK3066_DIV_ACLK_PCLK_MASK      0x3
+#define RK3066_DIV_ACLK_PCLK_SHIFT     12
+#define RK3066_DIV_AHB2APB_MASK                0x3
+#define RK3066_DIV_AHB2APB_SHIFT       14
+
+#define RK3066_CLKSEL0(_core_peri)                                     \
+       {                                                               \
+               .reg = RK2928_CLKSEL_CON(0),                            \
+               .val = HIWORD_UPDATE(_core_peri, RK3066_DIV_CORE_PERIPH_MASK, \
+                               RK3066_DIV_CORE_PERIPH_SHIFT)           \
+       }
+#define RK3066_CLKSEL1(_aclk_core, _aclk_hclk, _aclk_pclk, _ahb2apb)   \
+       {                                                               \
+               .reg = RK2928_CLKSEL_CON(1),                            \
+               .val = HIWORD_UPDATE(_aclk_core, RK3066_DIV_ACLK_CORE_MASK, \
+                               RK3066_DIV_ACLK_CORE_SHIFT) |           \
+                      HIWORD_UPDATE(_aclk_hclk, RK3066_DIV_ACLK_HCLK_MASK, \
+                               RK3066_DIV_ACLK_HCLK_SHIFT) |           \
+                      HIWORD_UPDATE(_aclk_pclk, RK3066_DIV_ACLK_PCLK_MASK, \
+                               RK3066_DIV_ACLK_PCLK_SHIFT) |           \
+                      HIWORD_UPDATE(_ahb2apb, RK3066_DIV_AHB2APB_MASK, \
+                               RK3066_DIV_AHB2APB_SHIFT),              \
+       }
+
+#define RK3066_CPUCLK_RATE(_prate, _core_peri, _acore, _ahclk, _apclk, _h2p) \
+       {                                                               \
+               .prate = _prate,                                        \
+               .divs = {                                               \
+                       RK3066_CLKSEL0(_core_peri),                     \
+                       RK3066_CLKSEL1(_acore, _ahclk, _apclk, _h2p),   \
+               },                                                      \
+       }
+
+static struct rockchip_cpuclk_rate_table rk3066_cpuclk_rates[] __initdata = {
+       RK3066_CPUCLK_RATE(1416000000, 2, 3, 1, 2, 1),
+       RK3066_CPUCLK_RATE(1200000000, 2, 3, 1, 2, 1),
+       RK3066_CPUCLK_RATE(1008000000, 2, 2, 1, 2, 1),
+       RK3066_CPUCLK_RATE( 816000000, 2, 2, 1, 2, 1),
+       RK3066_CPUCLK_RATE( 600000000, 1, 2, 1, 2, 1),
+       RK3066_CPUCLK_RATE( 504000000, 1, 1, 1, 2, 1),
+       RK3066_CPUCLK_RATE( 312000000, 0, 1, 1, 1, 0),
+};
+
+static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = {
+       .core_reg = RK2928_CLKSEL_CON(0),
+       .div_core_shift = 0,
+       .div_core_mask = 0x1f,
+       .mux_core_shift = 8,
+};
+
+#define RK3188_DIV_ACLK_CORE_MASK      0x7
+#define RK3188_DIV_ACLK_CORE_SHIFT     3
+
+#define RK3188_CLKSEL1(_aclk_core)             \
+       {                                       \
+&nbs