Commit 7e21774d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'clk-for-linus-3.14-part1' of git://git.linaro.org/people/mike.turquette/linux

Pull clk framework changes from Mike Turquette:
 "The first half of the clk framework pull request is made up almost
  entirely of new platform/driver support.  There are some conversions
  of existing drivers to the common-clock Device Tree binding, and a few
  non-critical fixes to the framework.

  Due to an entirely unnecessary cyclical dependency with the arm-soc
  tree this pull request is broken into two pieces.  The second piece
  will be sent out after arm-soc sends you the pull request that merged
  in core support for the HiSilicon 3620 platform.  That same pull
  request from arm-soc depends on this pull request to merge in those
  HiSilicon bits without causing build failures"

[ Just did the ARM SoC merges, so getting ready for the second clk tree
  pull request   - Linus ]

* tag 'clk-for-linus-3.14-part1' of git://git.linaro.org/people/mike.turquette/linux: (97 commits)
  devicetree: bindings: Document qcom,mmcc
  devicetree: bindings: Document qcom,gcc
  clk: qcom: Add support for MSM8660's global clock controller (GCC)
  clk: qcom: Add support for MSM8974's multimedia clock controller (MMCC)
  clk: qcom: Add support for MSM8974's global clock controller (GCC)
  clk: qcom: Add support for MSM8960's multimedia clock controller (MMCC)
  clk: qcom: Add support for MSM8960's global clock controller (GCC)
  clk: qcom: Add reset controller support
  clk: qcom: Add support for branches/gate clocks
  clk: qcom: Add support for root clock generators (RCGs)
  clk: qcom: Add support for phase locked loops (PLLs)
  clk: qcom: Add a regmap type clock struct
  clk: Add set_rate_and_parent() op
  reset: Silence warning in reset-controller.h
  clk: sirf: re-arch to make the codes support both prima2 and atlas6
  clk: composite: pass mux_hw into determine_rate
  clk: shmobile: Fix MSTP clock array initialization
  clk: shmobile: Fix MSTP clock index
  ARM: dts: Add clock provider specific properties to max77686 node
  clk: max77686: Register OF clock provider
  ...
parents 0ba3307a 2e84d751
......@@ -77,6 +77,11 @@ the operations defined in clk.h:
int (*set_parent)(struct clk_hw *hw, u8 index);
u8 (*get_parent)(struct clk_hw *hw);
int (*set_rate)(struct clk_hw *hw, unsigned long);
int (*set_rate_and_parent)(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate, u8 index);
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
unsigned long parent_accuracy);
void (*init)(struct clk_hw *hw);
};
......@@ -202,6 +207,8 @@ optional or must be evaluated on a case-by-case basis.
.set_parent | | | n | y | n |
.get_parent | | | n | y | n |
| | | | | |
.recalc_accuracy| | | | | |
| | | | | |
.init | | | | | |
-----------------------------------------------------------
[1] either one of round_rate or determine_rate is required.
......
......@@ -8,12 +8,29 @@ Required Properties:
- compatible: should be one of the following:
- "samsung,exynos4210-audss-clock" - controller compatible with all Exynos4 SoCs.
- "samsung,exynos5250-audss-clock" - controller compatible with all Exynos5 SoCs.
- "samsung,exynos5250-audss-clock" - controller compatible with Exynos5250
SoCs.
- "samsung,exynos5420-audss-clock" - controller compatible with Exynos5420
SoCs.
- reg: physical base address and length of the controller's register set.
- #clock-cells: should be 1.
- clocks:
- pll_ref: Fixed rate PLL reference clock, parent of mout_audss. "fin_pll"
is used if not specified.
- pll_in: Input PLL to the AudioSS block, parent of mout_audss. "fout_epll"
is used if not specified.
- cdclk: External i2s clock, parent of mout_i2s. "cdclk0" is used if not
specified.
- sclk_audio: Audio bus clock, parent of mout_i2s. "sclk_audio0" is used if
not specified.
- sclk_pcm_in: PCM clock, parent of sclk_pcm. "sclk_pcm0" is used if not
specified.
- clock-names: Aliases for the above clocks. They should be "pll_ref",
"pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively.
The following is the list of clocks generated by the controller. Each clock is
assigned an identifier and client nodes use this identifier to specify the
clock which they consume. Some of the clocks are available only on a particular
......@@ -34,16 +51,30 @@ i2s_bus 6
sclk_i2s 7
pcm_bus 8
sclk_pcm 9
adma 10 Exynos5420
Example 1: An example of a clock controller node using the default input
clock names is listed below.
clock_audss: audss-clock-controller@3810000 {
compatible = "samsung,exynos5250-audss-clock";
reg = <0x03810000 0x0C>;
#clock-cells = <1>;
};
Example 1: An example of a clock controller node is listed below.
Example 2: An example of a clock controller node with the input clocks
specified.
clock_audss: audss-clock-controller@3810000 {
compatible = "samsung,exynos5250-audss-clock";
reg = <0x03810000 0x0C>;
#clock-cells = <1>;
clocks = <&clock 1>, <&clock 7>, <&clock 138>, <&clock 160>,
<&ext_i2s_clk>;
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in", "cdclk";
};
Example 2: I2S controller node that consumes the clock generated by the clock
Example 3: I2S 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.
......
Device tree Clock bindings for Renesas EMMA Mobile EV2
This binding uses the common clock binding.
* SMU
System Management Unit described in user's manual R19UH0037EJ1000_SMU.
This is not a clock provider, but clocks under SMU depend on it.
Required properties:
- compatible: Should be "renesas,emev2-smu"
- reg: Address and Size of SMU registers
* SMU_CLKDIV
Function block with an input mux and a divider, which corresponds to
"Serial clock generator" in fig."Clock System Overview" of the manual,
and "xxx frequency division setting register" (XXXCLKDIV) registers.
This makes internal (neither input nor output) clock that is provided
to input of xxxGCLK block.
Required properties:
- compatible: Should be "renesas,emev2-smu-clkdiv"
- reg: Byte offset from SMU base and Bit position in the register
- clocks: Parent clocks. Input clocks as described in clock-bindings.txt
- #clock-cells: Should be <0>
* SMU_GCLK
Clock gating node shown as "Clock stop processing block" in the
fig."Clock System Overview" of the manual.
Registers are "xxx clock gate control register" (XXXGCLKCTRL).
Required properties:
- compatible: Should be "renesas,emev2-smu-gclk"
- reg: Byte offset from SMU base and Bit position in the register
- clocks: Input clock as described in clock-bindings.txt
- #clock-cells: Should be <0>
Example of provider:
usia_u0_sclkdiv: usia_u0_sclkdiv {
compatible = "renesas,emev2-smu-clkdiv";
reg = <0x610 0>;
clocks = <&pll3_fo>, <&pll4_fo>, <&pll1_fo>, <&osc1_fo>;
#clock-cells = <0>;
};
usia_u0_sclk: usia_u0_sclk {
compatible = "renesas,emev2-smu-gclk";
reg = <0x4a0 1>;
clocks = <&usia_u0_sclkdiv>;
#clock-cells = <0>;
};
Example of consumer:
uart@e1020000 {
compatible = "renesas,em-uart";
reg = <0xe1020000 0x38>;
interrupts = <0 8 0>;
clocks = <&usia_u0_sclk>;
clock-names = "sclk";
};
Example of clock-tree description:
This describes a clock path in the clock tree
c32ki -> pll3_fo -> usia_u0_sclkdiv -> usia_u0_sclk
smu@e0110000 {
compatible = "renesas,emev2-smu";
reg = <0xe0110000 0x10000>;
#address-cells = <2>;
#size-cells = <0>;
c32ki: c32ki {
compatible = "fixed-clock";
clock-frequency = <32768>;
#clock-cells = <0>;
};
pll3_fo: pll3_fo {
compatible = "fixed-factor-clock";
clocks = <&c32ki>;
clock-div = <1>;
clock-mult = <7000>;
#clock-cells = <0>;
};
usia_u0_sclkdiv: usia_u0_sclkdiv {
compatible = "renesas,emev2-smu-clkdiv";
reg = <0x610 0>;
clocks = <&pll3_fo>;
#clock-cells = <0>;
};
usia_u0_sclk: usia_u0_sclk {
compatible = "renesas,emev2-smu-gclk";
reg = <0x4a0 1>;
clocks = <&usia_u0_sclkdiv>;
#clock-cells = <0>;
};
};
......@@ -62,6 +62,7 @@ clock which they consume.
div_i2s1 157
div_i2s2 158
sclk_hdmiphy 159
div_pcm0 160
[Peripheral Clock Gates]
......
......@@ -10,6 +10,8 @@ Required properties:
- clock-frequency : frequency of clock in Hz. Should be a single cell.
Optional properties:
- clock-accuracy : accuracy of clock in ppb (parts per billion).
Should be a single cell.
- gpios : From common gpio binding; gpio connection to clock enable pin.
- clock-output-names : From common clock binding.
......@@ -18,4 +20,5 @@ Example:
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <1000000000>;
clock-accuracy = <100>;
};
......@@ -19,6 +19,6 @@ Example:
compatible = "fixed-factor-clock";
clocks = <&parentclk>;
#clock-cells = <0>;
div = <2>;
mult = <1>;
clock-div = <2>;
clock-mult = <1>;
};
* Hisilicon Hi3620 Clock Controller
The Hi3620 clock controller generates and supplies clock to various
controllers within the Hi3620 SoC.
Required Properties:
- compatible: should be one of the following.
- "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
- reg: physical base address of the controller and length of memory mapped
region.
- #clock-cells: should be 1.
Each clock is assigned an identifier and client nodes use this identifier
to specify the clock which they consume.
All these identifier could be found in <dt-bindings/clock/hi3620-clock.h>.
......@@ -17,13 +17,14 @@ Required properties:
- reg - pll control0 and pll multipler registers
- reg-names : control and multiplier. The multiplier is applicable only for
main pll clock
- fixed-postdiv : fixed post divider value
- fixed-postdiv : fixed post divider value. If absent, use clkod register bits
for postdiv
Example:
mainpllclk: mainpllclk@2310110 {
#clock-cells = <0>;
compatible = "ti,keystone,main-pll-clock";
clocks = <&refclkmain>;
clocks = <&refclksys>;
reg = <0x02620350 4>, <0x02310110 4>;
reg-names = "control", "multiplier";
fixed-postdiv = <2>;
......@@ -32,11 +33,10 @@ Example:
papllclk: papllclk@2620358 {
#clock-cells = <0>;
compatible = "ti,keystone,pll-clock";
clocks = <&refclkmain>;
clocks = <&refclkpass>;
clock-output-names = "pa-pll-clk";
reg = <0x02620358 4>;
reg-names = "control";
fixed-postdiv = <6>;
};
Required properties:
......
Binding for Maxim MAX77686 32k clock generator block
This is a part of device tree bindings of MAX77686 multi-function device.
More information can be found in bindings/mfd/max77686.txt file.
The MAX77686 contains three 32.768khz clock outputs that can be controlled
(gated/ungated) over I2C.
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:
- 0: 32khz_ap clock,
- 1: 32khz_cp clock,
- 2: 32khz_pmic clock.
Example: Node of the MFD chip
max77686: max77686@09 {
compatible = "maxim,max77686";
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 = <&max77686 2>;
};
Qualcomm Global Clock & Reset Controller Binding
------------------------------------------------
Required properties :
- compatible : shall contain only one of the following:
"qcom,gcc-msm8660"
"qcom,gcc-msm8960"
"qcom,gcc-msm8974"
- reg : shall contain base register location and length
- #clock-cells : shall contain 1
- #reset-cells : shall contain 1
Example:
clock-controller@900000 {
compatible = "qcom,gcc-msm8960";
reg = <0x900000 0x4000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
Qualcomm Multimedia Clock & Reset Controller Binding
----------------------------------------------------
Required properties :
- compatible : shall contain only one of the following:
"qcom,mmcc-msm8660"
"qcom,mmcc-msm8960"
"qcom,mmcc-msm8974"
- reg : shall contain base register location and length
- #clock-cells : shall contain 1
- #reset-cells : shall contain 1
Example:
clock-controller@4000000 {
compatible = "qcom,mmcc-msm8960";
reg = <0x4000000 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
Binding for Silicon Labs 570, 571, 598 and 599 programmable
I2C clock generators.
Reference
This binding uses the common clock binding[1]. Details about the devices can be
found in the data sheets[2][3].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Si570/571 Data Sheet
http://www.silabs.com/Support%20Documents/TechnicalDocs/si570.pdf
[3] Si598/599 Data Sheet
http://www.silabs.com/Support%20Documents/TechnicalDocs/si598-99.pdf
Required properties:
- compatible: Shall be one of "silabs,si570", "silabs,si571",
"silabs,si598", "silabs,si599"
- reg: I2C device address.
- #clock-cells: From common clock bindings: Shall be 0.
- factory-fout: Factory set default frequency. This frequency is part specific.
The correct frequency for the part used has to be provided in
order to generate the correct output frequencies. For more
details, please refer to the data sheet.
- temperature-stability: Temperature stability of the device in PPM. Should be
one of: 7, 20, 50 or 100.
Optional properties:
- clock-output-names: From common clock bindings. Recommended to be "si570".
- clock-frequency: Output frequency to generate. This defines the output
frequency set during boot. It can be reprogrammed during
runtime through the common clock framework.
Example:
si570: clock-generator@5d {
#clock-cells = <0>;
compatible = "silabs,si570";
temperature-stability = <50>;
reg = <0x5d>;
factory-fout = <156250000>;
};
......@@ -7,8 +7,10 @@ This binding uses the common clock binding[1].
Required properties:
- compatible : shall be one of the following:
"allwinner,sun4i-osc-clk" - for a gatable oscillator
"allwinner,sun4i-pll1-clk" - for the main PLL clock
"allwinner,sun4i-pll1-clk" - for the main PLL clock and PLL4
"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
"allwinner,sun4i-pll5-clk" - for the PLL5 clock
"allwinner,sun4i-pll6-clk" - for the PLL6 clock
"allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock
"allwinner,sun4i-axi-clk" - for the AXI clock
"allwinner,sun4i-axi-gates-clk" - for the AXI gates
......@@ -33,10 +35,14 @@ Required properties:
"allwinner,sun7i-a20-apb1-gates-clk" - for the APB1 gates on A20
"allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31
"allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
"allwinner,sun4i-mod0-clk" - for the module 0 family of clocks
"allwinner,sun7i-a20-out-clk" - for the external output clocks
Required properties for all clocks:
- reg : shall be the control register address for the clock.
- clocks : shall be the input parent clock(s) phandle for the clock
- clocks : shall be the input parent clock(s) phandle for the clock. For
multiplexed clocks, the list order must match the hardware
programming order.
- #clock-cells : from common clock binding; shall be set to 0 except for
"allwinner,*-gates-clk" where it shall be set to 1
......
......@@ -22,6 +22,10 @@ Required properties:
Optional properties:
- clocks : as described in the clock bindings
- clock-names : as described in the clock bindings
- fclk-enable : Bit mask to enable FCLKs statically at boot time.
Bit [0..3] correspond to FCLK0..FCLK3. The corresponding
FCLK will only be enabled if it is actually running at
boot time.
Clock inputs:
The following strings are optional parameters to the 'clock-names' property in
......
......@@ -7,6 +7,9 @@ different i2c slave address,presently for which we are statically creating i2c
client while probing.This document describes the binding for mfd device and
PMIC submodule.
Binding for the built-in 32k clock generator block is defined separately
in bindings/clk/maxim,max77686.txt file.
Required properties:
- compatible : Must be "maxim,max77686";
- reg : Specifies the i2c slave address of PMIC block.
......
......@@ -1345,6 +1345,14 @@ F: drivers/rtc/rtc-ab8500.c
F: drivers/rtc/rtc-pl031.c
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
ARM/Ux500 CLOCK FRAMEWORK SUPPORT
M: Ulf Hansson <ulf.hansson@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
T: git git://git.linaro.org/people/ulfh/clk.git
S: Maintained
F: drivers/clk/ux500/
F: include/linux/platform_data/clk-ux500.h
ARM/VFP SUPPORT
M: Russell King <linux@arm.linux.org.uk>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
......@@ -7431,6 +7439,12 @@ L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/i2c/s5c73m3/*
SAMSUNG SOC CLOCK DRIVERS
M: Tomasz Figa <t.figa@samsung.com>
S: Supported
L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
F: drivers/clk/samsung/
SERIAL DRIVERS
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
L: linux-serial@vger.kernel.org
......
......@@ -116,6 +116,7 @@ i2c@13860000 {
max77686: pmic@09 {
compatible = "maxim,max77686";
reg = <0x09>;
#clock-cells = <1>;
voltage-regulators {
ldo1_reg: LDO1 {
......
......@@ -139,6 +139,7 @@ max77686_pmic@09 {
interrupt-parent = <&gpx0>;
interrupts = <7 0>;
reg = <0x09>;
#clock-cells = <1>;
voltage-regulators {
ldo1_reg: ldo1 {
......
......@@ -49,6 +49,7 @@ max77686@09 {
pinctrl-0 = <&max77686_irq>;
wakeup-source;
reg = <0x09>;
#clock-cells = <1>;
voltage-regulators {
ldo1_reg: LDO1 {
......
......@@ -90,6 +90,8 @@ clock_audss: audss-clock-controller@3810000 {
compatible = "samsung,exynos5250-audss-clock";
reg = <0x03810000 0x0C>;
#clock-cells = <1>;
clocks = <&clock 1>, <&clock 7>, <&clock 138>, <&clock 160>;
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
};
timer {
......
......@@ -119,8 +119,8 @@ clock_audss: audss-clock-controller@3810000 {
compatible = "samsung,exynos5420-audss-clock";
reg = <0x03810000 0x0C>;
#clock-cells = <1>;
clocks = <&clock 148>;
clock-names = "sclk_audio";
clocks = <&clock 1>, <&clock 5>, <&clock 148>, <&clock 149>;
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
};
codec@11000000 {
......
......@@ -14,12 +14,14 @@
#include <linux/slab.h>
#ifndef CONFIG_COMMON_CLK
#ifdef CONFIG_HAVE_MACH_CLKDEV
#include <mach/clkdev.h>
#else
#define __clk_get(clk) ({ 1; })
#define __clk_put(clk) do { } while (0)
#endif
#endif
static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
{
......
......@@ -8,7 +8,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
return kzalloc(size, GFP_KERNEL);
}
#ifndef CONFIG_COMMON_CLK
#define __clk_put(clk)
#define __clk_get(clk) ({ 1; })
#endif
#endif
......@@ -14,8 +14,10 @@
#include <linux/slab.h>
#ifndef CONFIG_COMMON_CLK
#define __clk_get(clk) ({ 1; })
#define __clk_put(clk) do { } while (0)
#endif
static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
{
......
......@@ -25,7 +25,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
return kzalloc(size, GFP_KERNEL);
}
#ifndef CONFIG_COMMON_CLK
#define __clk_put(clk)
#define __clk_get(clk) ({ 1; })
#endif
#endif /* __CLKDEV_H__ */
......@@ -23,16 +23,6 @@ config COMMON_CLK
menu "Common Clock Framework"
depends on COMMON_CLK
config COMMON_CLK_DEBUG
bool "DebugFS representation of clock tree"
select DEBUG_FS
---help---
Creates a directory hierarchy in debugfs for visualizing the clk
tree structure. Each directory contains read-only members
that export information specific to that clk node: clk_rate,
clk_flags, clk_prepare_count, clk_enable_count &
clk_notifier_count.
config COMMON_CLK_WM831X
tristate "Clock driver for WM831x/2x PMICs"
depends on MFD_WM831X
......@@ -64,6 +54,16 @@ config COMMON_CLK_SI5351
This driver supports Silicon Labs 5351A/B/C programmable clock
generators.
config COMMON_CLK_SI570
tristate "Clock driver for SiLabs 570 and compatible devices"
depends on I2C
depends on OF
select REGMAP_I2C
help
---help---
This driver supports Silicon Labs 570/571/598/599 programmable
clock generators.
config COMMON_CLK_S2MPS11
tristate "Clock driver for S2MPS11 MFD"
depends on MFD_SEC_CORE
......@@ -107,6 +107,8 @@ config COMMON_CLK_KEYSTONE
Supports clock drivers for Keystone based SOCs. These SOCs have local
a power sleep control module that gate the clock to the IPs and PLLs.
source "drivers/clk/qcom/Kconfig"
endmenu
source "drivers/clk/mvebu/Kconfig"
......@@ -14,13 +14,14 @@ obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
obj-$(CONFIG_ARCH_MXS) += mxs/
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-$(CONFIG_ARCH_U300) += clk-u300.o
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-$(CONFIG_PLAT_ORION) += mvebu/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
......@@ -30,6 +31,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_SIRF) += sirf/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
......@@ -45,6 +47,7 @@ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
......@@ -55,6 +55,30 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
return rate_ops->recalc_rate(rate_hw, parent_rate);
}
static long clk_composite_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate,
struct clk **best_parent_p)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *rate_ops = composite->rate_ops;
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk_hw *rate_hw = composite->rate_hw;
struct clk_hw *mux_hw = composite->mux_hw;
if (rate_hw && rate_ops && rate_ops->determine_rate) {
rate_hw->clk = hw->clk;
return rate_ops->determine_rate(rate_hw, rate, best_parent_rate,
best_parent_p);
} else if (mux_hw && mux_ops && mux_ops->determine_rate) {
mux_hw->clk = hw->clk;
return mux_ops->determine_rate(mux_hw, rate, best_parent_rate,
best_parent_p);
} else {
pr_err("clk: clk_composite_determine_rate function called, but no mux or rate callback set!\n");
return 0;
}
}
static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
......@@ -147,6 +171,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
composite->mux_ops = mux_ops;
clk_composite_ops->get_parent = clk_composite_get_parent;
clk_composite_ops->set_parent = clk_composite_set_parent;
if (mux_ops->determine_rate)
clk_composite_ops->determine_rate = clk_composite_determine_rate;
}
if (rate_hw && rate_ops) {
......@@ -170,6 +196,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
composite->rate_hw = rate_hw;
composite->rate_ops = rate_ops;
clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
if (rate_ops->determine_rate)
clk_composite_ops->determine_rate = clk_composite_determine_rate;
}
if (gate_hw && gate_ops) {
......
......@@ -34,22 +34,31 @@ static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
return to_clk_fixed_rate(hw)->fixed_rate;
}
static unsigned long clk_fixed_rate_recalc_accuracy(struct clk_hw *hw,
unsigned long parent_accuracy)
{
return to_clk_fixed_rate(hw)->fixed_accuracy;
}
const struct clk_ops clk_fixed_rate_ops = {
.recalc_rate = clk_fixed_rate_recalc_rate,
.recalc_accuracy = clk_fixed_rate_recalc_accuracy,
};
EXPORT_SYMBOL_GPL(clk_fixed_rate_ops);
/**
* clk_register_fixed_rate - register fixed-rate clock with the clock framework
* clk_register_fixed_rate_with_accuracy - register fixed-rate clock with the
* clock framework
* @dev: device that is registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @fixed_rate: non-adjustable clock rate
* @fixed_accuracy: non-adjustable clock rate
*/
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
unsigned long fixed_rate)
struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned long fixed_rate, unsigned long fixed_accuracy)
{
struct clk_fixed_rate *fixed;
struct clk *clk;
......@@ -70,16 +79,33 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
/* struct clk_fixed_rate assignments */
fixed->fixed_rate = fixed_rate;
fixed->fixed_accuracy = fixed_accuracy;
fixed->hw.init = &init;
/* register the clock */
clk = clk_register(dev, &fixed->hw);
if (IS_ERR(clk))
kfree(fixed);
return clk;
}
EXPORT_SYMBOL_GPL(clk_register_fixed_rate_with_accuracy);
/**
* clk_register_fixed_rate - register fixed-rate clock with the clock framework
* @dev: device that is registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @fixed_rate: non-adjustable clock rate
*/
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
unsigned long fixed_rate)
{
return clk_register_fixed_rate_with_accuracy(dev, name, parent_name,
flags, fixed_rate, 0);
}
EXPORT_SYMBOL_GPL(clk_register_fixed_rate);
#ifdef CONFIG_OF
......@@ -91,13 +117,18 @@ void of_fixed_clk_setup(struct device_node *node)
struct clk *clk;
const char *clk_name = node->name;
u32 rate;
u32 accuracy = 0;
if (of_property_read_u32(node, "clock-frequency", &rate))
return;
of_property_read_u32(node, "clock-accuracy", &accuracy);
of_property_read_string(node, "clock-output-names", &clk_name);
clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate);
clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,
CLK_IS_ROOT, rate,
accuracy);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
......
......@@ -66,7 +66,7 @@ static void max77686_clk_unprepare(struct clk_hw *hw)
MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
}
static int max77686_clk_is_enabled(struct clk_hw *hw)
static int max77686_clk_is_prepared(struct clk_hw *hw)
{
struct max77686_clk *max77686 = to_max77686_clk(hw);
int ret;
......@@ -81,10 +81,17 @@ static int max77686_clk_is_enabled(struct clk_hw *hw)
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_enabled = max77686_clk_is_enabled,
.is_prepared = max77686_clk_is_prepared,
.recalc_rate = max77686_recalc_rate,
};
static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
......@@ -105,38 +112,38 @@ static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
},
};
static int max77686_clk_register(struct device *dev,
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 -ENOMEM;
return clk;
max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
if (!max77686->lookup)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
max77686->lookup->con_id = hw->init->name;
max77686->lookup->clk = clk;
clkdev_add(max77686->lookup);
return 0;
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;
struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM];
struct clk **clocks;
int i, ret;
max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
* MAX77686_CLKS_NUM, GFP_KERNEL);
if (!max77686_clks)
if (!clocks)
return -ENOMEM;
for (i = 0; i < MAX77686_CLKS_NUM; i++) {
......@@ -151,47 +158,63 @@ static int max77686_clk_probe(struct platform_device *pdev)
max77686_clks[i]->mask = 1 << i;
max77686_clks[i]->hw.init = &max77686_clks_init[i];
ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
if (ret) {
switch (i) {
case MAX77686_CLK_AP:
dev_err(&pdev->dev, "Fail to register CLK_AP\n");
goto err_clk_ap;
break;
case MAX77686_CLK_CP:
dev_err(&pdev->dev, "Fail to register CLK_CP\n");
goto err_clk_cp;
break;
case MAX77686_CLK_PMIC:
dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
goto err_clk_pmic;
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;
}
platform_set_drvdata(pdev, max77686_clks);
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;
goto out;
err_clocks:
for (--i; i >= 0; --i) {
clkdev_drop(max77686_clks[i]->lookup);
clk_unregister(max77686_clks[i]->hw.clk);
}
err_clk_pmic:
clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
err_clk_cp:
clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
err_clk_ap:
out:
return ret;
}
static int max77686_clk_remove(struct platform_device *pdev)
{
struct max77686_clk **max77686_clks = platform_get_drvdata(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++) {
clkdev_drop(max77686_clks[i]->lookup);
kfree(max77686_clks[i]->hw.clk);
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;
}
......
This diff is collapsed.
......@@ -641,7 +641,7 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
return pll_freq;
}
const struct clk_ops vtwm_pll_ops = {
static const struct clk_ops vtwm_pll_ops = {
.round_rate = vtwm_pll_round_rate,
.set_rate = vtwm_pll_set_rate,
.recalc_rate = vtwm_pll_recalc_rate,
......
This diff is collapsed.
/*
* linux/drivers/clk/clk.h
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Sylwester Nawrocki <s.nawrocki@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.
*/
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec);
void of_clk_lock(void);
void of_clk_unlock(void);
#endif
......@@ -21,6 +21,8 @@
#include <linux/clkdev.h>
#include <linux/of.h>
#include "clk.h"
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
......@@ -39,7 +41,13 @@ struct clk *of_clk_get(struct device_node *np, int index)
if (rc)
return ERR_PTR(rc);
clk = of_clk_get_from_provider(&clkspec);
of_clk_lock();
clk = __of_clk_get_from_provider(&clkspec);
if (!IS_ERR(clk) && !__clk_get(clk))
clk = ERR_PTR(-ENOENT);
of_clk_unlock();
of_node_put(clkspec.np);
return clk;
}
......@@ -157,7 +165,7 @@ struct clk *clk_get(struct device *dev, const char *con_id)
if (dev) {
clk = of_clk_get_by_name(dev->of_node, con_id);
if (!IS_ERR(clk) && __clk_get(clk))
if (!IS_ERR(clk))
return clk;
}
......
#
# Hisilicon Clock specific Makefile
#
obj-y += clk.o clkgate-separated.o clk-hi3620.o
This diff is collapsed.
/*
* Hisilicon clock driver
*
* Copyright (c) 2012-2013 Hisilicon Limited.
* Copyright (c) 2012-2013 Linaro Limited.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
* Xin Li <li.xin@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include "clk.h"
static DEFINE_SPINLOCK(hisi_clk_lock);
static struct clk **clk_table;
static struct clk_onecell_data clk_data;
void __init hisi_clk_init(struct device_node *np, int nr_clks)
{
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table) {
pr_err("%s: could not allocate clock lookup table\n", __func__);
return;
}
clk_data.clks = clk_table;
clk_data.clk_num = nr_clks;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = clk_register_fixed_rate(NULL, clks[i].name,
clks[i].parent_name,
clks[i].flags,
clks[i].fixed_rate);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
}
}
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = clk_register_fixed_factor(NULL, clks[i].name,
clks[i].parent_name,
clks[i].flags, clks[i].mult,
clks[i].div);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
}
}
void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = clk_register_mux(NULL, clks[i].name, clks[i].parent_names,
clks[i].num_parents, clks[i].flags,
base + clks[i].offset, clks[i].shift,
clks[i].width, clks[i].mux_flags,
&hisi_clk_lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
clk_table[clks[i].id] = clk;
}
}
void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = clk_register_divider_table(NULL, clks[i].name,
clks[i].parent_name,
clks[i].flags,
base + clks[i].offset,
clks[i].shift, clks[i].width,
clks[i].div_flags,
clks[i].table,
&hisi_clk_lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
clk_table[clks[i].id] = clk;
}
}
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = hisi_register_clkgate_sep(NULL, clks[i].name,
clks[i].parent_name,
clks[i].flags,
base + clks[i].offset,
clks[i].bit_idx,
clks[i].gate_flags,
&hisi_clk_lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
clk_table[clks[i].id] = clk;
}
}
/*
* Hisilicon Hi3620 clock gate driver
*
* Copyright (c) 2012-2013 Hisilicon Limited.
* Copyright (c) 2012-2013 Linaro Limited.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
* Xin Li <li.xin@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef __HISI_CLK_H
#define __HISI_CLK_H
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/spinlock.h>
struct hisi_fixed_rate_clock {
unsigned int id;
char *name;
const char *parent_name;
unsigned long flags;
unsigned long fixed_rate;
};
struct hisi_fixed_factor_clock {
unsigned int id;
char *name;
const char *parent_name;
unsigned long mult;
unsigned long div;
unsigned long flags;
};
struct hisi_mux_clock {
unsigned int id;
const char *name;
const char **parent_names;
u8 num_parents;
unsigned long flags;
unsigned long offset;
u8 shift;
u8 width;
u8 mux_flags;
const char *alias;
};
struct hisi_divider_clock {
unsigned int id;
const char *name;
const char *parent_name;
unsigned long flags;
unsigned long offset;
u8 shift;
u8 width;
u8 div_flags;
struct clk_div_table *table;
const char *alias;
};
struct hisi_gate_clock {
unsigned int id;
const char *name;
const char *parent_name;
unsigned long flags;
unsigned long offset;
u8 bit_idx;
u8 gate_flags;
const char *alias;
};
struct clk *hisi_register_clkgate_sep(struct device *, const char *,
const char *, unsigned long,
void __iomem *, u8,
u8, spinlock_t *);
void __init hisi_clk_init(struct device_node *, int);
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *,
int, void __iomem *);
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *,
int, void __iomem *);
void __init hisi_clk_register_mux(struct hisi_mux_clock *, int,
void __iomem *);
void __init hisi_clk_register_divider(struct hisi_divider_clock *,
int, void __iomem *);
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *,
int, void __iomem *);
#endif /* __HISI_CLK_H */
/*
* Hisilicon clock separated gate driver
*
* Copyright (c) 2012-2013 Hisilicon Limited.
* Copyright (c) 2012-2013 Linaro Limited.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
* Xin Li <li.xin@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include "clk.h"
/* clock separated gate register offset */
#define CLKGATE_SEPERATED_ENABLE 0x0
#define CLKGATE_SEPERATED_DISABLE 0x4
#define CLKGATE_SEPERATED_STATUS 0x8
struct clkgate_separated {
struct clk_hw hw;
void __iomem *enable; /* enable register */
u8 bit_idx; /* bits in enable/disable register */
u8 flags;
spinlock_t *lock;
};
static int clkgate_separated_enable(struct clk_hw *hw)
{
struct clkgate_separated *sclk;
unsigned long flags = 0;
u32 reg;
sclk = container_of(hw, struct clkgate_separated, hw);
if (sclk->lock)
spin_lock_irqsave(sclk->lock, flags);
reg = BIT(sclk->bit_idx);
writel_relaxed(reg, sclk->enable);
readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
if (sclk->lock)
spin_unlock_irqrestore(sclk->lock, flags);
return 0;
}
static void clkgate_separated_disable(struct clk_hw *hw)
{
struct clkgate_separated *sclk;
unsigned long flags = 0;
u32 reg;
sclk = container_of(hw, struct clkgate_separated, hw);
if (sclk->lock)
spin_lock_irqsave(sclk->lock, flags);
reg = BIT(sclk->bit_idx);
writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE);
readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
if (sclk->lock)
spin_unlock_irqrestore(sclk->lock, flags);
}
static int clkgate_separated_is_enabled(struct clk_hw *hw)
{
struct clkgate_separated *sclk;
u32 reg;
sclk = container_of(hw, struct clkgate_separated, hw);
reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
reg &= BIT(sclk->bit_idx);
return reg ? 1 : 0;
}
static struct clk_ops clkgate_separated_ops = {
.enable = clkgate_separated_enable,
.disable = clkgate_separated_disable,
.is_enabled = clkgate_separated_is_enabled,
};
struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock)
{
struct clkgate_separated *sclk;
struct clk *clk;
struct clk_init_data init;
sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
if (!sclk) {
pr_err("%s: fail to allocate separated gated clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clkgate_separated_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
sclk->enable = reg + CLKGATE_SEPERATED_ENABLE;
sclk->bit_idx = bit_idx;
sclk->flags = clk_gate_flags;
sclk->hw.init = &init;
clk = clk_register(dev, &sclk->hw);
if (IS_ERR(clk))
kfree(sclk);
return clk;
}
......@@ -223,8 +223,7 @@ static void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock)
data->domain_base = of_iomap(node, i);
if (!data->domain_base) {
pr_err("%s: domain ioremap failed\n", __func__);
iounmap(data->control_base);
goto out;
goto unmap_ctrl;
}
of_property_read_u32(node, "domain-id", &data->domain_id);
......@@ -237,16 +236,21 @@ static void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock)
parent_name = of_clk_get_parent_name(node, 0);
if (!parent_name) {
pr_err("%s: Parent clock not found\n", __func__);
goto out;
goto unmap_domain;
}
clk = clk_register_psc(NULL, clk_name, parent_name, data, lock);
if (clk) {
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
return;
}
pr_err("%s: error registering clk %s\n", __func__, node->name);
unmap_domain:
iounmap(data->domain_base);
unmap_ctrl:
iounmap(data->control_base);
out:
kfree(data);
return;
......
......@@ -24,6 +24,8 @@
#define MAIN_PLLM_HIGH_MASK 0x7f000
#define PLLM_HIGH_SHIFT 6
#define PLLD_MASK 0x3f
#define CLKOD_MASK 0x780000
#define CLKOD_SHIFT 19
/**
* struct clk_pll_data - pll data structure
......@@ -41,7 +43,10 @@
* @pllm_upper_mask: multiplier upper mask
* @pllm_upper_shift: multiplier upper shift
* @plld_mask: divider mask
* @postdiv: Post divider
* @clkod_mask: output divider mask
* @clkod_shift: output divider shift
* @plld_mask: divider mask
* @postdiv: Fixed post divider
*/
struct clk_pll_data {
bool has_pllctrl;
......@@ -53,6 +58,8 @@ struct clk_pll_data {
u32 pllm_upper_mask;
u32 pllm_upper_shift;
u32 plld_mask;
u32 clkod_mask;
u32 clkod_shift;
u32 postdiv;
};
......@@ -90,6 +97,12 @@ static unsigned long clk_pllclk_recalc(struct clk_hw *hw,
mult |= ((val & pll_data->pllm_upper_mask)
>> pll_data->pllm_upper_shift);
prediv = (val & pll_data->plld_mask);
if (!pll_data->has_pllctrl)
/* read post divider from od bits*/
postdiv = ((val & pll_data->clkod_mask) >>
pll_data->clkod_shift) + 1;
else
postdiv = pll_data->postdiv;
rate /= (prediv + 1);
......@@ -155,8 +168,11 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl)
}
parent_name = of_clk_get_parent_name(node, 0);
if (of_property_read_u32(node, "fixed-postdiv", &pll_data->postdiv))
goto out;
if (of_property_read_u32(node, "fixed-postdiv", &pll_data->postdiv)) {
/* assume the PLL has output divider register bits */
pll_data->clkod_mask = CLKOD_MASK;
pll_data->clkod_shift = CLKOD_SHIFT;
}
i = of_property_match_string(node, "reg-names", "control");
pll_data->pll_ctl0 = of_iomap(node, i);
......
......@@ -4,15 +4,20 @@ config MVEBU_CLK_COMMON
config MVEBU_CLK_CPU
bool
config MVEBU_CLK_COREDIV
bool
config ARMADA_370_CLK
bool
select MVEBU_CLK_COMMON
select MVEBU_CLK_CPU
select MVEBU_CLK_COREDIV
config ARMADA_XP_CLK
bool
select MVEBU_CLK_COMMON
select MVEBU_CLK_CPU
select MVEBU_CLK_COREDIV
config DOVE_CLK
bool
......
obj-$(CONFIG_MVEBU_CLK_COMMON) += common.o
obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o
obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o
obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o
obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
......
/*
* MVEBU Core divider clock
*
* Copyright (C) 2013 Marvell
*
* Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include "common.h"
#define CORE_CLK_DIV_RATIO_MASK 0xff
#define CORE_CLK_DIV_RATIO_RELOAD BIT(8)
#define CORE_CLK_DIV_ENABLE_OFFSET 24
#define CORE_CLK_DIV_RATIO_OFFSET 0x8
struct clk_corediv_desc {
unsigned int mask;
unsigned int offset;
unsigned int fieldbit;
};
struct clk_corediv {
struct clk_hw hw;
void __iomem *reg;
struct clk_corediv_desc desc;
spinlock_t lock;
};
static struct clk_onecell_data clk_data;
static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = {
{ .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */
};
#define to_corediv_clk(p) container_of(p, struct clk_corediv, hw)
static int clk_corediv_is_enabled(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
struct clk_corediv_desc *desc = &corediv->desc;
u32 enable_mask = BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET;
return !!(readl(corediv->reg) & enable_mask);
}
static int clk_corediv_enable(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
struct clk_corediv_desc *desc = &corediv->desc;
unsigned long flags = 0;
u32 reg;
spin_lock_irqsave(&corediv->lock, flags);
reg = readl(corediv->reg);
reg |= (BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET);
writel(reg, corediv->reg);
spin_unlock_irqrestore(&corediv->lock, flags);
return 0;
}
static void clk_corediv_disable(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
struct clk_corediv_desc *desc = &corediv->desc;
unsigned long flags = 0;
u32 reg;
spin_lock_irqsave(&corediv->lock, flags);
reg = readl(corediv->reg);
reg &= ~(BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET);
writel(reg, corediv->reg);
spin_unlock_irqrestore(&corediv->lock, flags);
}
static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
struct clk_corediv_desc *desc = &corediv->desc;
u32 reg, div;
reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
div = (reg >> desc->offset) & desc->mask;
return parent_rate / div;
}
static long clk_corediv_round_rate(struct clk_hw *hwclk, unsigned long rate,
unsigned long *parent_rate)
{
/* Valid ratio are 1:4, 1:5, 1:6 and 1:8 */
u32 div;
div = *parent_rate / rate;
if (div < 4)
div = 4;
else if (div > 6)
div = 8;
return *parent_rate / div;
}
static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
unsigned long parent_rate)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
struct clk_corediv_desc *desc = &corediv->desc;
unsigned long flags = 0;
u32 reg, div;
div = parent_rate / rate;
spin_lock_irqsave(&corediv->lock, flags);
/* Write new divider to the divider ratio register */
reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
reg &= ~(desc->mask << desc->offset);
reg |= (div & desc->mask) << desc->offset;
writel(reg, corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
/* Set reload-force for this clock */
reg = readl(corediv->reg) | BIT(desc->fieldbit);
writel(reg, corediv->reg);
/* Now trigger the clock update */
reg = readl(corediv->reg) | CORE_CLK_DIV_RATIO_RELOAD;
writel(reg, corediv->reg);
/*
* Wait for clocks to settle down, and then clear all the
* ratios request and the reload request.
*/
udelay(1000);
reg &= ~(CORE_CLK_DIV_RATIO_MASK | CORE_CLK_DIV_RATIO_RELOAD);
writel(reg, corediv->reg);
udelay(1000);
spin_unlock_irqrestore(&corediv->lock, flags);
return 0;
}
static const struct clk_ops corediv_ops = {
.enable = clk_corediv_enable,
.disable = clk_corediv_disable,
.is_enabled = clk_corediv_is_enabled,
.recalc_rate = clk_corediv_recalc_rate,
.round_rate = clk_corediv_round_rate,
.set_rate = clk_corediv_set_rate,
};
static void __init mvebu_corediv_clk_init(struct device_node *node)
{
struct clk_init_data init;
struct clk_corediv *corediv;
struct clk **clks;
void __iomem *base;
const char *parent_name;
const char *clk_name;
int i;
base = of_iomap(node, 0);
if (WARN_ON(!base))
return;
parent_name = of_clk_get_parent_name(node, 0);
clk_data.clk_num = ARRAY_SIZE(mvebu_corediv_desc);
/* clks holds the clock array */
clks = kcalloc(clk_data.clk_num, sizeof(struct clk *),
GFP_KERNEL);
if (WARN_ON(!clks))
goto err_unmap;
/* corediv holds the clock specific array */
corediv = kcalloc(clk_data.clk_num, sizeof(struct clk_corediv),
GFP_KERNEL);
if (WARN_ON(!corediv))
goto err_free_clks;
spin_lock_init(&corediv->lock);
for (i = 0; i < clk_data.clk_num; i++) {
of_property_read_string_index(node, "clock-output-names",
i, &clk_name);
init.num_parents = 1;
init.parent_names = &parent_name;
init.name = clk_name;
init.ops = &corediv_ops;
init.flags = 0;
corediv[i].desc = mvebu_corediv_desc[i];
corediv[i].reg = base;
corediv[i].hw.init = &init;
clks[i] = clk_register(NULL, &corediv[i].hw);
WARN_ON(IS_ERR(clks[i]));
}
clk_data.clks = clks;
of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
return;
err_free_clks:
kfree(clks);
err_unmap:
iounmap(base);
}
CLK_OF_DECLARE(mvebu_corediv_clk, "marvell,armada-370-corediv-clock",
mvebu_corediv_clk_init);
......@@ -101,7 +101,7 @@ static const struct clk_ops cpu_ops = {
.set_rate = clk_cpu_set_rate,
};
void __init of_cpu_clk_setup(struct device_node *node)
static void __init of_cpu_clk_setup(struct device_node *node)
{
struct cpu_clk *cpuclk;
void __iomem *clock_complex_base = of_iomap(node, 0);
......
config COMMON_CLK_QCOM
tristate "Support for Qualcomm's clock controllers"
depends on OF
select REGMAP_MMIO
select RESET_CONTROLLER
config MSM_GCC_8660
tristate "MSM8660 Global Clock Controller"
depends on COMMON_CLK_QCOM
help
Support for the global clock controller on msm8660 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, etc.
config MSM_GCC_8960
tristate "MSM8960 Global Clock Controller"
depends on COMMON_CLK_QCOM
help
Support for the global clock controller on msm8960 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, SATA, PCIe, etc.
config MSM_MMCC_8960
tristate "MSM8960 Multimedia Clock Controller"
select MSM_GCC_8960
depends on COMMON_CLK_QCOM
help
Support for the multimedia clock controller on msm8960 devices.
Say Y if you want to support multimedia devices such as display,
graphics, video encode/decode, camera, etc.
config MSM_GCC_8974
tristate "MSM8974 Global Clock Controller"
depends on COMMON_CLK_QCOM
help
Support for the global clock controller on msm8974 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, SATA, PCIe, etc.
config MSM_MMCC_8974
tristate "MSM8974 Multimedia Clock Controller"
select MSM_GCC_8974
depends on COMMON_CLK_QCOM
help
Support for the multimedia clock controller on msm8974 devices.
Say Y if you want to support multimedia devices such as display,
graphics, video encode/decode, camera, etc.
obj-$(CONFIG_COMMON_CLK_QCOM) += clk-qcom.o
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-regmap.o
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-pll.o
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-rcg.o
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-rcg2.o
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-branch.o
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += reset.o
obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include "clk-branch.h"
static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
{
u32 val;
if (!br->hwcg_reg)
return 0;
regmap_read(br->clkr.regmap, br->hwcg_reg, &val);
return !!(val & BIT(br->hwcg_bit));
}
static bool clk_branch_check_halt(const struct clk_branch *br, bool enabling)
{
bool invert = (br->halt_check == BRANCH_HALT_ENABLE);
u32 val;
regmap_read(br->clkr.regmap, br->halt_reg, &val);
val &= BIT(br->halt_bit);
if (invert)
val = !val;
return !!val == !enabling;
}
#define BRANCH_CLK_OFF BIT(31)
#define BRANCH_NOC_FSM_STATUS_SHIFT 28
#define BRANCH_NOC_FSM_STATUS_MASK 0x7
#define BRANCH_NOC_FSM_STATUS_ON (0x2 << BRANCH_NOC_FSM_STATUS_SHIFT)
static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling)
{
u32 val;
u32 mask;
mask = BRANCH_NOC_FSM_STATUS_MASK << BRANCH_NOC_FSM_STATUS_SHIFT;
mask |= BRANCH_CLK_OFF;
regmap_read(br->clkr.regmap, br->halt_reg, &val);
if (enabling) {
val &= mask;
return (val & BRANCH_CLK_OFF) == 0 ||
val == BRANCH_NOC_FSM_STATUS_ON;
} else {
return val & BRANCH_CLK_OFF;
}
}
static int clk_branch_wait(const struct clk_branch *br, bool enabling,
bool (check_halt)(const struct clk_branch *, bool))
{
bool voted = br->halt_check & BRANCH_VOTED;
const char *name = __clk_get_name(br->clkr.hw.clk);
/* Skip checking halt bit if the clock is in hardware gated mode */
if (clk_branch_in_hwcg_mode(br))
return 0;
if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) {
udelay(10);
} else if (br->halt_check == BRANCH_HALT_ENABLE ||
br->halt_check == BRANCH_HALT ||
(enabling && voted)) {
int count = 200;
while (count-- > 0) {
if (check_halt(br, enabling))
return 0;
udelay(1);
}
WARN(1, "%s status stuck at 'o%s'", name,
enabling ? "ff" : "n");
return -EBUSY;
}
return 0;
}
static int clk_branch_toggle(struct clk_hw *hw, bool en,
bool (check_halt)(const struct clk_branch *, bool))
{
struct clk_branch *br = to_clk_branch(hw);
int ret;
if (en) {
ret = clk_enable_regmap(hw);
if (ret)
return ret;
} else {
clk_disable_regmap(hw);
}
return clk_branch_wait(br, en, check_halt);
}
static int clk_branch_enable(struct clk_hw *hw)
{
return clk_branch_toggle(hw, true, clk_branch_check_halt);
}
static void clk_branch_disable(struct clk_hw *hw)
{
clk_branch_toggle(hw, false, clk_branch_check_halt);
}
const struct clk_ops clk_branch_ops = {
.enable = clk_branch_enable,
.disable = clk_branch_disable,
.is_enabled = clk_is_enabled_regmap,
};
EXPORT_SYMBOL_GPL(clk_branch_ops);
static int clk_branch2_enable(struct clk_hw *hw)
{
return clk_branch_toggle(hw, true, clk_branch2_check_halt);
}
static void clk_branch2_disable(struct clk_hw *hw)
{
clk_branch_toggle(hw, false, clk_branch2_check_halt);
}
const struct clk_ops clk_branch2_ops = {
.enable = clk_branch2_enable,
.disable = clk_branch2_disable,
.is_enabled = clk_is_enabled_regmap,
};
EXPORT_SYMBOL_GPL(clk_branch2_ops);
const struct clk_ops clk_branch_simple_ops = {
.enable = clk_enable_regmap,
.disable = clk_disable_regmap,
.is_enabled = clk_is_enabled_regmap,
};
EXPORT_SYMBOL_GPL(clk_branch_simple_ops);
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 __QCOM_CLK_BRANCH_H__
#define __QCOM_CLK_BRANCH_H__
#include <linux/clk-provider.h>
#include "clk-regmap.h"
/**
* struct clk_branch - gating clock with status bit and dynamic hardware gating
*
* @hwcg_reg: dynamic hardware clock gating register
* @hwcg_bit: ORed with @hwcg_reg to enable dynamic hardware clock gating
* @halt_reg: halt register
* @halt_bit: ANDed with @halt_reg to test for clock halted
* @halt_check: type of halt checking to perform
* @clkr: handle between common and hardware-specific interfaces
*
* Clock which can gate its output.
*/
struct clk_branch {
u32 hwcg_reg;
u32 halt_reg;
u8 hwcg_bit;
u8 halt_bit;
u8 halt_check;
#define BRANCH_VOTED BIT(7) /* Delay on disable */
#define BRANCH_HALT 0 /* pol: 1 = halt */
#define BRANCH_HALT_VOTED (BRANCH_HALT | BRANCH_VOTED)
#define BRANCH_HALT_ENABLE 1 /* pol: 0 = halt */
#define BRANCH_HALT_ENABLE_VOTED (BRANCH_HALT_ENABLE | BRANCH_VOTED)
#define BRANCH_HALT_DELAY 2 /* No bit to check; just delay */
struct clk_regmap clkr;
};
extern const struct clk_ops clk_branch_ops;
extern const struct clk_ops clk_branch2_ops;
extern const struct clk_ops clk_branch_simple_ops;
#define to_clk_branch(_hw) \
container_of(to_clk_regmap(_hw), struct clk_branch, clkr)
#endif
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <asm/div64.h>
#include "clk-pll.h"
#define PLL_OUTCTRL BIT(0)
#define PLL_BYPASSNL BIT(1)
#define PLL_RESET_N BIT(2)
#define PLL_LOCK_COUNT_SHIFT 8
#define PLL_LOCK_COUNT_MASK 0x3f
#define PLL_BIAS_COUNT_SHIFT 14
#define PLL_BIAS_COUNT_MASK 0x3f
#define PLL_VOTE_FSM_ENA BIT(20)
#define PLL_VOTE_FSM_RESET BIT(21)
static int clk_pll_enable(struct clk_hw *hw)
{
struct clk_pll *pll = to_clk_pll(hw);
int ret;
u32 mask, val;
mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
ret = regmap_read(pll->clkr.regmap, pll->mode_reg, &val);
if (ret)
return ret;
/* Skip if already enabled or in FSM mode */
if ((val & mask) == mask || val & PLL_VOTE_FSM_ENA)
return 0;
/* Disable PLL bypass mode. */
ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_BYPASSNL,
PLL_BYPASSNL);
if (ret)
return ret;
/*
* H/W requires a 5us delay between disabling the bypass and
* de-asserting the reset. Delay 10us just to be safe.
*/
udelay(10);
/* De-assert active-low PLL reset. */
ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_RESET_N,
PLL_RESET_N);
if (ret)
return ret;
/* Wait until PLL is locked. */
udelay(50);
/* Enable PLL output. */
ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_OUTCTRL,
PLL_OUTCTRL);
if (ret)
return ret;
return 0;
}
static void clk_pll_disable(struct clk_hw *hw)
{
struct clk_pll *pll = to_clk_pll(hw);
u32 mask;
u32 val;
regmap_read(pll->clkr.regmap, pll->mode_reg, &val);
/* Skip if in FSM mode */
if (val & PLL_VOTE_FSM_ENA)
return;
mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
regmap_update_bits(pll->clkr.regmap, pll->mode_reg, mask, 0);
}
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;
unsigned long rate;
u64 tmp;
regmap_read(pll->clkr.regmap, pll->l_reg, &l);
regmap_read(pll->clkr.regmap, pll->m_reg, &m);
regmap_read(pll->clkr.regmap, pll->n_reg, &n);
l &= 0x3ff;
m &= 0x7ffff;
n &= 0x7ffff;
rate = parent_rate * l;
if (n) {
tmp = parent_rate;
tmp *= m;
do_div(tmp, n);
rate += tmp;
}
return rate;
}
const struct clk_ops clk_pll_ops = {
.enable = clk_pll_enable,
.disable = clk_pll_disable,
.recalc_rate = clk_pll_recalc_rate,
};
EXPORT_SYMBOL_GPL(clk_pll_ops);
static int wait_for_pll(struct clk_pll *pll)
{
u32 val;
int count;
int ret;
const char *name = __clk_get_name(pll->clkr.hw.clk);
/* Wait for pll to enable. */
for (count = 200; count > 0; count--) {
ret = regmap_read(pll->clkr.regmap, pll->status_reg, &val);
if (ret)
return ret;
if (val & BIT(pll->status_bit))
return 0;
udelay(1);
}
WARN(1, "%s didn't enable after voting for it!\n", name);
return -ETIMEDOUT;
}
static int clk_pll_vote_enable(struct clk_hw *hw)
{
int ret;
struct clk_pll *p = to_clk_pll(__clk_get_hw(__clk_get_parent(hw->clk)));
ret = clk_enable_regmap(hw);
if (ret)
return ret;
return wait_for_pll(p);
}
const struct clk_ops clk_pll_vote_ops = {
.enable = clk_pll_vote_enable,
.disable = clk_disable_regmap,
};
EXPORT_SYMBOL_GPL(clk_pll_vote_ops);
static void
clk_pll_set_fsm_mode(struct clk_pll *pll, struct regmap *regmap)
{
u32 val;
u32 mask;
/* De-assert reset to FSM */
regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_RESET, 0);
/* Program bias count and lock count */
val = 1 << PLL_BIAS_COUNT_SHIFT;
mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
regmap_update_bits(regmap, pll->mode_reg, mask, val);
/* Enable PLL FSM voting */
regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_ENA,
PLL_VOTE_FSM_ENA);
}
static void clk_pll_configure(struct clk_pll *pll, struct regmap *regmap,
const struct pll_config *config)
{
u32 val;
u32 mask;
regmap_write(regmap, pll->l_reg, config->l);
regmap_write(regmap, pll->m_reg, config->m);
regmap_write(regmap, pll->n_reg, config->n);
val = config->vco_val;
val |= config->pre_div_val;
val |= config->post_div_val;
val |= config->mn_ena_mask;
val |= config->main_output_mask;
val |= config->aux_output_mask;
mask = config->vco_mask;
mask |= config->pre_div_mask;
mask |= config->post_div_mask;
mask |= config->mn_ena_mask;
mask |= config->main_output_mask;
mask |= config->aux_output_mask;
regmap_update_bits(regmap, pll->config_reg, mask, val);
}
void clk_pll_configure_sr_hpm_lp(struct clk_pll *pll, struct regmap *regmap,
const struct pll_config *config, bool fsm_mode)
{
clk_pll_configure(pll, regmap, config);
if (fsm_mode)
clk_pll_set_fsm_mode(pll, regmap);
}
EXPORT_SYMBOL_GPL(clk_pll_configure_sr_hpm_lp);
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 __QCOM_CLK_PLL_H__
#define __QCOM_CLK_PLL_H__
#include <linux/clk-provider.h>
#include "clk-regmap.h"
/**
* struct clk_pll - phase locked loop (PLL)
* @l_reg: L register
* @m_reg: M register
* @n_reg: N register
* @config_reg: config register
* @mode_reg: mode register
* @status_reg: status register
* @status_bit: ANDed with @status_reg to determine if PLL is enabled
* @hw: handle between common and hardware-specific interfaces
*/
struct clk_pll {
u32 l_reg;
u32 m_reg;
u32 n_reg;
u32 config_reg;
u32 mode_reg;
u32 status_reg;
u8 status_bit;
struct clk_regmap clkr;
};
extern const struct clk_ops clk_pll_ops;
extern const struct clk_ops clk_pll_vote_ops;
#define to_clk_pll(_hw) container_of(to_clk_regmap(_hw), struct clk_pll, clkr)
struct pll_config {
u16 l;
u32 m;
u32 n;
u32 vco_val;
u32 vco_mask;
u32 pre_div_val;
u32 pre_div_mask;
u32 post_div_val;
u32 post_div_mask;
u32 mn_ena_mask;
u32 main_output_mask;
u32 aux_output_mask;
};
void clk_pll_configure_sr_hpm_lp(struct clk_pll *pll, struct regmap *regmap,
const struct pll_config *config, bool fsm_mode);
#endif
This diff is collapsed.
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 __QCOM_CLK_RCG_H__
#define __QCOM_CLK_RCG_H__
#include <linux/clk-provider.h>
#include "clk-regmap.h"
struct freq_tbl {
unsigned long freq;
u8 src;
u8 pre_div;
u16 m;
u16 n;
};
/**
* struct mn - M/N:D counter
* @mnctr_en_bit: bit to enable mn counter
* @mnctr_reset_bit: bit to assert mn counter reset
* @mnctr_mode_shift: lowest bit of mn counter mode field
* @n_val_shift: lowest bit of n value field
* @m_val_shift: lowest bit of m value field
* @width: number of bits in m/n/d values
* @reset_in_cc: true if the mnctr_reset_bit is in the CC register
*/
struct mn {
u8 mnctr_en_bit;
u8 mnctr_reset_bit;
u8 mnctr_mode_shift;
#define MNCTR_MODE_DUAL 0x2
#define MNCTR_MODE_MASK 0x3
u8 n_val_shift;
u8 m_val_shift;
u8 width;
bool reset_in_cc;
};
/**
* struct pre_div - pre-divider
* @pre_div_shift: lowest bit of pre divider field
* @pre_div_width: number of bits in predivider
*/
struct pre_div {
u8 pre_div_shift;
u8 pre_div_width;
};
/**
* struct src_sel - source selector
* @src_sel_shift: lowest bit of source selection field
* @parent_map: map from software's parent index to hardware's src_sel field
*/
struct src_sel {
u8 src_sel_shift;
#define SRC_SEL_MASK 0x7
const u8 *parent_map;
};
/**
* struct clk_rcg - root clock generator
*
* @ns_reg: NS register
* @md_reg: MD register
* @mn: mn counter
* @p: pre divider
* @s: source selector
* @freq_tbl: frequency table
* @clkr: regmap clock handle
* @lock: register lock
*
*/
struct clk_rcg {
u32 ns_reg;
u32 md_reg;
struct mn mn;
struct pre_div p;
struct src_sel s;
const struct freq_tbl *freq_tbl;
struct clk_regmap clkr;
};
extern const struct clk_ops clk_rcg_ops;
#define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr)
/**
* struct clk_dyn_rcg - root clock generator with glitch free mux
*
* @mux_sel_bit: bit to switch glitch free mux
* @ns_reg: NS register
* @md_reg: MD0 and MD1 register
* @mn: mn counter (banked)
* @s: source selector (banked)
* @freq_tbl: frequency table
* @clkr: regmap clock handle
* @lock: register lock
*
*/
struct clk_dyn_rcg {
u32 ns_reg;
u32 md_reg[2];
u8 mux_sel_bit;
struct mn mn[2];
struct pre_div p[2];
struct src_sel s[2];
const struct freq_tbl *freq_tbl;
struct clk_regmap clkr;
};
extern const struct clk_ops clk_dyn_rcg_ops;
#define to_clk_dyn_rcg(_hw) \
container_of(to_clk_regmap(_hw), struct clk_dyn_rcg, clkr)
/**
* struct clk_rcg2 - root clock generator
*
* @cmd_rcgr: corresponds to *_CMD_RCGR
* @mnd_width: number of bits in m/n/d values
* @hid_width: number of bits in half integer divider
* @parent_map: map from software's parent index to hardware's src_sel field
* @freq_tbl: frequency table
* @clkr: regmap clock handle
* @lock: register lock
*
*/
struct clk_rcg2 {
u32 cmd_rcgr;
u8 mnd_width;
u8 hid_width;
const u8 *parent_map;
const struct freq_tbl *freq_tbl;
struct clk_regmap clkr;
};
#define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
extern const struct clk_ops clk_rcg2_ops;
#endif
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/export.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <asm/div64.h>
#include "clk-rcg.h"
#define CMD_REG 0x0
#define CMD_UPDATE BIT(0)
#define CMD_ROOT_EN BIT(1)
#define CMD_DIRTY_CFG BIT(4)
#define CMD_DIRTY_N BIT(5)
#define CMD_DIRTY_M BIT(6)
#define CMD_DIRTY_D BIT(7)
#define CMD_ROOT_OFF BIT(31)
#define CFG_REG 0x4
#define CFG_SRC_DIV_SHIFT 0
#define CFG_SRC_SEL_SHIFT 8
#define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT)
#define CFG_MODE_SHIFT 12
#define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT)
#define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT)
#define M_REG 0x8
#define N_REG 0xc
#define D_REG 0x10
static int clk_rcg2_is_enabled(struct clk_hw *hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
u32 cmd;
int ret;
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
if (ret)
return ret;
return (cmd & CMD_ROOT_OFF) != 0;
}
static u8 clk_rcg2_get_parent(struct clk_hw *hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
int num_parents = __clk_get_num_parents(hw->clk);
u32 cfg;
int i, ret;
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
if (ret)
return ret;
cfg &= CFG_SRC_SEL_MASK;
cfg >>= CFG_SRC_SEL_SHIFT;
for (i = 0; i < num_parents; i++)
if (cfg == rcg->parent_map[i])
return i;
return -EINVAL;
}
static int update_config(struct clk_rcg2 *rcg)
{
int count, ret;
u32 cmd;
struct clk_hw *hw = &rcg->clkr.hw;
const char *name = __clk_get_name(hw->clk);
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
CMD_UPDATE, CMD_UPDATE);
if (ret)
return ret;
/* Wait for update to take effect */
for (count = 500; count > 0; count--) {
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
if (ret)
return ret;
if (!(cmd & CMD_UPDATE))
return 0;
udelay(1);
}
WARN(1, "%s: rcg didn't update its configuration.", name);
return 0;
}
static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
int ret;
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
CFG_SRC_SEL_MASK,
rcg->parent_map[index] << CFG_SRC_SEL_SHIFT);
if (ret)
return ret;
return update_config(rcg);
}
/*
* Calculate m/n:d rate
*
* parent_rate m
* rate = ----------- x ---
* hid_div n
*/
static unsigned long
calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div)
{
if (hid_div) {
rate *= 2;
rate /= hid_div + 1;
}
if (mode) {
u64 tmp = rate;
tmp *= m;
do_div(tmp, n);
rate = tmp;
}
return rate;
}
static unsigned long
clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
if (rcg->mnd_width) {
mask = BIT(rcg->mnd_width) - 1;
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, &m);
m &= mask;
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, &n);
n = ~n;
n &= mask;
n += m;
mode = cfg & CFG_MODE_MASK;
mode >>= CFG_MODE_SHIFT;
}
mask = BIT(rcg->hid_width) - 1;
hid_div = cfg >> CFG_SRC_DIV_SHIFT;
hid_div &= mask;
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;
return NULL;
}
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);
if (!f)
return -EINVAL;
clk_flags = __clk_get_flags(hw->clk);
*p = clk_get_parent_by_index(hw->clk, f->src);
if (clk_flags & CLK_SET_RATE_PARENT) {
if (f->pre_div) {
rate /= 2;
rate *= f->pre_div + 1;
}
if (f->n) {
u64 tmp = rate;
tmp = tmp * f->n;
do_div(tmp, f->m);
rate = tmp;
}
} else {
rate = __clk_get_rate(*p);
}
*p_rate = rate;
return f->freq;
}
static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *p_rate, struct clk **p)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
}
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;
u32 cfg, mask;
int ret;
f = find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
if (rcg->mnd_width && f->n) {
mask = BIT(rcg->mnd_width) - 1;
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG,
mask, f->m);
if (ret)
return ret;
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG,
mask, ~(f->n - f->m));
if (ret)
return ret;
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + D_REG,
mask, ~f->n);
if (ret)
return ret;
}
mask = BIT(rcg->hid_width) - 1;
mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK;
cfg = f->pre_div << CFG_SRC_DIV_SHIFT;
cfg |= rcg->parent_map[f->src] << CFG_SRC_SEL_SHIFT;
if (rcg->mnd_width && f->n)
cfg |= CFG_MODE_DUAL_EDGE;
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, mask,
cfg);
if (ret)
return ret;
return update_config(rcg);
}
static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return __clk_rcg2_set_rate(hw, rate);
}
static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate, u8 index)
{
return __clk_rcg2_set_rate(hw, rate);
}
const struct clk_ops clk_rcg2_ops = {
.is_enabled = clk_rcg2_is_enabled,
.get_parent = clk_rcg2_get_parent,
.set_parent = clk_rcg2_set_parent,
.recalc_rate = clk_rcg2_recalc_rate,
.determine_rate = clk_rcg2_determine_rate,
.set_rate = clk_rcg2_set_rate,
.set_rate_and_parent = clk_rcg2_set_rate_and_parent,
};
EXPORT_SYMBOL_GPL(clk_rcg2_ops);
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/export.h>
#include "clk-regmap.h"
/**
* clk_is_enabled_regmap - standard is_enabled() for regmap users
*
* @hw: clk to operate on
*
* Clocks that use regmap for their register I/O can set the
* enable_reg and enable_mask fields in their struct clk_regmap and then use
* this as their is_enabled operation, saving some code.
*/
int clk_is_enabled_regmap(struct clk_hw *hw)
{
struct clk_regmap *rclk = to_clk_regmap(hw);
unsigned int val;
int ret;
ret = regmap_read(rclk->regmap, rclk->enable_reg, &val);
if (ret != 0)
return ret;
if (rclk->enable_is_inverted)
return (val & rclk->enable_mask) == 0;
else
return (val & rclk->enable_mask) != 0;
}
EXPORT_SYMBOL_GPL(clk_is_enabled_regmap);
/**
* clk_enable_regmap - standard enable() for regmap users
*
* @hw: clk to operate on
*
* Clocks that use regmap for their register I/O can set the
* enable_reg and enable_mask fields in their struct clk_regmap and then use
* this as their enable() operation, saving some code.
*/
int clk_enable_regmap(struct clk_hw *hw)
{
struct clk_regmap *rclk = to_clk_regmap(hw);
unsigned int val;
if (rclk->enable_is_inverted)
val = 0;
else
val = rclk->enable_mask;
return regmap_update_bits(rclk->regmap, rclk->enable_reg,
rclk->enable_mask, val);
}
EXPORT_SYMBOL_GPL(clk_enable_regmap);
/**
* clk_disable_regmap - standard disable() for regmap users
*
* @hw: clk to operate on
*
* Clocks that use regmap for their register I/O can set the
* enable_reg and enable_mask fields in their struct clk_regmap and then use
* this as their disable() operation, saving some code.
*/
void clk_disable_regmap(struct clk_hw *hw)
{
struct clk_regmap *rclk = to_clk_regmap(hw);
unsigned int val;
if (rclk->enable_is_inverted)
val = rclk->enable_mask;
else
val = 0;
regmap_update_bits(rclk->regmap, rclk->enable_reg, rclk->enable_mask,
val);
}
EXPORT_SYMBOL_GPL(clk_disable_regmap);
/**
* devm_clk_register_regmap - register a clk_regmap clock
*
* @rclk: clk to operate on
*
* Clocks that use regmap for their register I/O should register their
* clk_regmap struct via this function so that the regmap is initialized
* and so that the clock is registered with the common clock framework.
*/
struct clk *devm_clk_register_regmap(struct device *dev,
struct clk_regmap *rclk)
{
if (dev && dev_get_regmap(dev, NULL))
rclk->regmap = dev_get_regmap(dev, NULL);
else if (dev && dev->parent)
rclk->regmap = dev_get_regmap(dev->parent, NULL);
return devm_clk_register(dev, &rclk->hw);
}
EXPORT_SYMBOL_GPL(devm_clk_register_regmap);
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 __QCOM_CLK_REGMAP_H__
#define __QCOM_CLK_REGMAP_H__
#include <linux/clk-provider.h>
struct regmap;
/**
* struct clk_regmap - regmap supporting clock
* @hw: handle between common and hardware-specific interfaces
* @regmap: regmap to use for regmap helpers and/or by providers
* @enable_reg: register when using regmap enable/disable ops
* @enable_mask: mask when using regmap enable/disable ops
* @enable_is_inverted: flag to indicate set enable_mask bits to disable
* when using clock_enable_regmap and friends APIs.
*/
struct clk_regmap {
struct clk_hw hw;
struct regmap *regmap;
unsigned int enable_reg;
unsigned int enable_mask;
bool enable_is_inverted;
};
#define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw)
int clk_is_enabled_regmap(struct clk_hw *hw);
int clk_enable_regmap(struct clk_hw *hw);
void clk_disable_regmap(struct clk_hw *hw);
struct clk *
devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/bitops.h>
#include <linux/export.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#include <linux/delay.h>
#include "reset.h"
static int qcom_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
rcdev->ops->assert(rcdev, id);
udelay(1);
rcdev->ops->deassert(rcdev, id);
return 0;
}
static int
qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
{
struct qcom_reset_controller *rst;
const struct qcom_reset_map *map;
u32 mask;
rst = to_qcom_reset_controller(rcdev);
map = &rst->reset_map[id];
mask = BIT(map->bit);
return regmap_update_bits(rst->regmap, map->reg, mask, mask);
}
static int
qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
{
struct qcom_reset_controller *rst;
const struct qcom_reset_map *map;
u32 mask;
rst = to_qcom_reset_controller(rcdev);
map = &rst->reset_map[id];
mask = BIT(map->bit);
return regmap_update_bits(rst->regmap, map->reg, mask, 0);
}
struct reset_control_ops qcom_reset_ops = {
.reset = qcom_reset,
.assert = qcom_reset_assert,
.deassert = qcom_reset_deassert,
};
EXPORT_SYMBOL_GPL(qcom_reset_ops);
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 __QCOM_CLK_RESET_H__
#define __QCOM_CLK_RESET_H__
#include <linux/reset-controller.h>
struct qcom_reset_map {
unsigned int reg;
u8 bit;
};
struct regmap;
struct qcom_reset_controller {
const struct qcom_reset_map *reset_map;
struct regmap *regmap;
struct reset_controller_dev rcdev;
};
#define to_qcom_reset_controller(r) \
container_of(r, struct qcom_reset_controller, rcdev);
extern struct reset_control_ops qcom_reset_ops;
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -9,6 +9,7 @@
* Common Clock Framework support for Exynos5440 SoC.
*/
#include <dt-bindings/clock/exynos5440.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
......@@ -22,79 +23,65 @@
#define CPU_CLK_STATUS 0xfc
#define MISC_DOUT1 0x558
/*
* Let each supported clock get a unique id. This id is used to lookup the clock
* for device tree based platforms.
*/
enum exynos5440_clks {
none, xtal, arm_clk,
spi_baud = 16, pb0_250, pr0_250, pr1_250, b_250, b_125, b_200, sata,
usb, gmac0, cs250, pb0_250_o, pr0_250_o, pr1_250_o, b_250_o, b_125_o,
b_200_o, sata_o, usb_o, gmac0_o, cs250_o,
nr_clks,
};
/* parent clock name list */
PNAME(mout_armclk_p) = { "cplla", "cpllb" };
PNAME(mout_spi_p) = { "div125", "div200" };
/* fixed rate clocks generated outside the soc */
static struct samsung_fixed_rate_clock exynos5440_fixed_rate_ext_clks[] __initdata = {
FRATE(none, "xtal", NULL, CLK_IS_ROOT, 0),
FRATE(0, "xtal", NULL, CLK_IS_ROOT, 0),
};
/* fixed rate clocks */
static struct samsung_fixed_rate_clock exynos5440_fixed_rate_clks[] __initdata = {
FRATE(none, "ppll", NULL, CLK_IS_ROOT, 1000000000),
FRATE(none, "usb_phy0", NULL, CLK_IS_ROOT, 60000000),
FRATE(none, "usb_phy1", NULL, CLK_IS_ROOT, 60000000),
FRATE(none, "usb_ohci12", NULL, CLK_IS_ROOT, 12000000),
FRATE(none, "usb_ohci48", NULL, CLK_IS_ROOT, 48000000),
FRATE(0, "ppll", NULL, CLK_IS_ROOT, 1000000000),
FRATE(0, "usb_phy0", NULL, CLK_IS_ROOT, 60000000),
FRATE(0, "usb_phy1", NULL, CLK_IS_ROOT, 60000000),
FRATE(0, "usb_ohci12", NULL, CLK_IS_ROOT, 12000000),
FRATE(0, "usb_ohci48", NULL, CLK_IS_ROOT, 48000000),
};
/* fixed factor clocks */
static struct samsung_fixed_factor_clock exynos5440_fixed_factor_clks[] __initdata = {
FFACTOR(none, "div250", "ppll", 1, 4, 0),
FFACTOR(none, "div200", "ppll", 1, 5, 0),
FFACTOR(none, "div125", "div250", 1, 2, 0),
FFACTOR(0, "div250", "ppll", 1, 4, 0),
FFACTOR(0, "div200", "ppll", 1, 5, 0),
FFACTOR(0, "div125", "div250", 1, 2, 0),
};
/* mux clocks */
static struct samsung_mux_clock exynos5440_mux_clks[] __initdata = {
MUX(none, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1),
MUX_A(arm_clk, "arm_clk", mout_armclk_p,
MUX(0, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1),
MUX_A(CLK_ARM_CLK, "arm_clk", mout_armclk_p,
CPU_CLK_STATUS, 0, 1, "armclk"),
};
/* divider clocks */
static struct samsung_div_clock exynos5440_div_clks[] __initdata = {
DIV(spi_baud, "div_spi", "mout_spi", MISC_DOUT1, 3, 2),
DIV(CLK_SPI_BAUD, "div_spi", "mout_spi", MISC_DOUT1, 3, 2),
};
/* gate clocks */
static struct samsung_gate_clock exynos5440_gate_clks[] __initdata = {
GATE(pb0_250, "pb0_250", "div250", CLKEN_OV_VAL, 3, 0, 0),
GATE(pr0_250, "pr0_250", "div250", CLKEN_OV_VAL, 4, 0, 0),
GATE(pr1_250, "pr1_250", "div250", CLKEN_OV_VAL, 5, 0, 0),
GATE(b_250, "b_250", "div250", CLKEN_OV_VAL, 9, 0, 0),
GATE(b_125, "b_125", "div125", CLKEN_OV_VAL, 10, 0, 0),
GATE(b_200, "b_200", "div200", CLKEN_OV_VAL, 11, 0, 0),
GATE(sata, "sata", "div200", CLKEN_OV_VAL, 12, 0, 0),
GATE(usb, "usb", "div200", CLKEN_OV_VAL, 13, 0, 0),
GATE(gmac0, "gmac0", "div200", CLKEN_OV_VAL, 14, 0, 0),
GATE(cs250, "cs250", "div250", CLKEN_OV_VAL, 19, 0, 0),
GATE(pb0_250_o, "pb0_250_o", "pb0_250", CLKEN_OV_VAL, 3, 0, 0),
GATE(pr0_250_o, "pr0_250_o", "pr0_250", CLKEN_OV_VAL, 4, 0, 0),
GATE(pr1_250_o, "pr1_250_o", "pr1_250", CLKEN_OV_VAL, 5, 0, 0),
GATE(b_250_o, "b_250_o", "b_250", CLKEN_OV_VAL, 9, 0, 0),
GATE(b_125_o, "b_125_o", "b_125", CLKEN_OV_VAL, 10, 0, 0),
GATE(b_200_o, "b_200_o", "b_200", CLKEN_OV_VAL, 11, 0, 0),
GATE(sata_o, "sata_o", "sata", CLKEN_OV_VAL, 12, 0, 0),
GATE(usb_o, "usb_o", "usb", CLKEN_OV_VAL, 13, 0, 0),
GATE(gmac0_o, "gmac0_o", "gmac", CLKEN_OV_VAL, 14, 0, 0),
GATE(cs250_o, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0),
GATE(CLK_PB0_250, "pb0_250", "div250", CLKEN_OV_VAL, 3, 0, 0),
GATE(CLK_PR0_250, "pr0_250", "div250", CLKEN_OV_VAL, 4, 0, 0),
GATE(CLK_PR1_250, "pr1_250", "div250", CLKEN_OV_VAL, 5, 0, 0),
GATE(CLK_B_250, "b_250", "div250", CLKEN_OV_VAL, 9, 0, 0),
GATE(CLK_B_125, "b_125", "div125", CLKEN_OV_VAL, 10, 0, 0),
GATE(CLK_B_200, "b_200", "div200", CLKEN_OV_VAL, 11, 0, 0),
GATE(CLK_SATA, "sata", "div200", CLKEN_OV_VAL, 12, 0, 0),
GATE(CLK_USB, "usb", "div200", CLKEN_OV_VAL, 13, 0, 0),
GATE(CLK_GMAC0, "gmac0", "div200", CLKEN_OV_VAL, 14, 0, 0),
GATE(CLK_CS250, "cs250", "div250", CLKEN_OV_VAL, 19, 0, 0),
GATE(CLK_PB0_250_O, "pb0_250_o", "pb0_250", CLKEN_OV_VAL, 3, 0, 0),
GATE(CLK_PR0_250_O, "pr0_250_o", "pr0_250", CLKEN_OV_VAL, 4, 0, 0),
GATE(CLK_PR1_250_O, "pr1_250_o", "pr1_250", CLKEN_OV_VAL, 5, 0, 0),
GATE(CLK_B_250_O, "b_250_o", "b_250", CLKEN_OV_VAL, 9, 0, 0),
GATE(CLK_B_125_O, "b_125_o", "b_125", CLKEN_OV_VAL, 10, 0, 0),
GATE(CLK_B_200_O, "b_200_o", "b_200", CLKEN_OV_VAL, 11, 0, 0),
GATE(CLK_SATA_O, "sata_o", "sata", CLKEN_OV_VAL, 12, 0, 0),
GATE(CLK_USB_O, "usb_o", "usb", CLKEN_OV_VAL, 13, 0, 0),
GATE(CLK_GMAC0_O, "gmac0_o", "gmac", CLKEN_OV_VAL, 14, 0, 0),
GATE(CLK_CS250_O, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0),
};
static struct of_device_id ext_clk_match[] __initdata = {
......@@ -114,7 +101,7 @@ static void __init exynos5440_clk_init(struct device_node *np)
return;
}
samsung_clk_init(np, reg_base, nr_clks, NULL, 0, NULL, 0);
samsung_clk_init(np, reg_base, CLK_NR_CLKS, NULL, 0, NULL, 0);
samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match);
......
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-mstp.o
# for emply built-in.o
obj-n := dummy
This diff is collapsed.
......@@ -160,7 +160,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np)
unsigned int i;
group = kzalloc(sizeof(*group), GFP_KERNEL);
clks = kzalloc(MSTP_MAX_CLOCKS * sizeof(*clks), GFP_KERNEL);
clks = kmalloc(MSTP_MAX_CLOCKS * sizeof(*clks), GFP_KERNEL);
if (group == NULL || clks == NULL) {
kfree(group);
kfree(clks);
......@@ -181,6 +181,9 @@ static void __init cpg_mstp_clocks_init(struct device_node *np)
return;
}
for (i = 0; i < MSTP_MAX_CLOCKS; ++i)
clks[i] = ERR_PTR(-ENOENT);
for (i = 0; i < MSTP_MAX_CLOCKS; ++i) {
const char *parent_name;
const char *name;
......@@ -205,10 +208,11 @@ static void __init cpg_mstp_clocks_init(struct device_node *np)
continue;
}
clks[clkidx] = cpg_mstp_clock_register(name, parent_name, i,
group);
clks[clkidx] = cpg_mstp_clock_register(name, parent_name,
clkidx, group);
if (!IS_ERR(clks[clkidx])) {
group->data.clk_num = max(group->data.clk_num, clkidx);
group->data.clk_num = max(group->data.clk_num,
clkidx + 1);
/*
* Register a clkdev to let board code retrieve the
* clock by name and register aliases for non-DT
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment