forked from Minki/linux
Merge remote-tracking branch 'linaro/clk-next' into clk-next
This commit is contained in:
commit
dedca6abaf
@ -68,21 +68,27 @@ the operations defined in clk.h:
|
||||
int (*is_enabled)(struct clk_hw *hw);
|
||||
unsigned long (*recalc_rate)(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
long (*round_rate)(struct clk_hw *hw, unsigned long,
|
||||
unsigned long *);
|
||||
long (*round_rate)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate);
|
||||
long (*determine_rate)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *best_parent_rate,
|
||||
struct clk **best_parent_clk);
|
||||
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)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int (*set_rate_and_parent)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate, u8 index);
|
||||
unsigned long parent_rate,
|
||||
u8 index);
|
||||
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
|
||||
unsigned long parent_accuracy);
|
||||
unsigned long parent_accuracy);
|
||||
void (*init)(struct clk_hw *hw);
|
||||
int (*debug_init)(struct clk_hw *hw,
|
||||
struct dentry *dentry);
|
||||
};
|
||||
|
||||
Part 3 - hardware clk implementations
|
||||
|
@ -10,12 +10,12 @@ This binding uses the common clock binding:
|
||||
|
||||
Required properties:
|
||||
- compatible
|
||||
Shall have one of the following values:
|
||||
- "brcm,bcm11351-root-ccu"
|
||||
- "brcm,bcm11351-aon-ccu"
|
||||
- "brcm,bcm11351-hub-ccu"
|
||||
- "brcm,bcm11351-master-ccu"
|
||||
- "brcm,bcm11351-slave-ccu"
|
||||
Shall have a value of the form "brcm,<model>-<which>-ccu",
|
||||
where <model> is a Broadcom SoC model number and <which> is
|
||||
the name of a defined CCU. For example:
|
||||
"brcm,bcm11351-root-ccu"
|
||||
The compatible strings used for each supported SoC family
|
||||
are defined below.
|
||||
- reg
|
||||
Shall define the base and range of the address space
|
||||
containing clock control registers
|
||||
@ -26,12 +26,48 @@ Required properties:
|
||||
Shall be an ordered list of strings defining the names of
|
||||
the clocks provided by the CCU.
|
||||
|
||||
Device tree example:
|
||||
|
||||
BCM281XX family SoCs use Kona CCUs. The following table defines
|
||||
the set of CCUs and clock specifiers for BCM281XX clocks. When
|
||||
a clock consumer references a clocks, its symbolic specifier
|
||||
(rather than its numeric index value) should be used. These
|
||||
specifiers are defined in "include/dt-bindings/clock/bcm281xx.h".
|
||||
slave_ccu: slave_ccu {
|
||||
compatible = "brcm,bcm11351-slave-ccu";
|
||||
reg = <0x3e011000 0x0f00>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "uartb",
|
||||
"uartb2",
|
||||
"uartb3",
|
||||
"uartb4";
|
||||
};
|
||||
|
||||
ref_crystal_clk: ref_crystal {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <26000000>;
|
||||
};
|
||||
|
||||
uart@3e002000 {
|
||||
compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart";
|
||||
status = "disabled";
|
||||
reg = <0x3e002000 0x1000>;
|
||||
clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB3>;
|
||||
interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg-shift = <2>;
|
||||
reg-io-width = <4>;
|
||||
};
|
||||
|
||||
BCM281XX family
|
||||
---------------
|
||||
CCU compatible string values for SoCs in the BCM281XX family are:
|
||||
"brcm,bcm11351-root-ccu"
|
||||
"brcm,bcm11351-aon-ccu"
|
||||
"brcm,bcm11351-hub-ccu"
|
||||
"brcm,bcm11351-master-ccu"
|
||||
"brcm,bcm11351-slave-ccu"
|
||||
|
||||
The following table defines the set of CCUs and clock specifiers for
|
||||
BCM281XX family clocks. When a clock consumer references a clocks,
|
||||
its symbolic specifier (rather than its numeric index value) should
|
||||
be used. These specifiers are defined in:
|
||||
"include/dt-bindings/clock/bcm281xx.h"
|
||||
|
||||
CCU Clock Type Index Specifier
|
||||
--- ----- ---- ----- ---------
|
||||
@ -64,30 +100,40 @@ specifiers are defined in "include/dt-bindings/clock/bcm281xx.h".
|
||||
slave pwm peri 9 BCM281XX_SLAVE_CCU_PWM
|
||||
|
||||
|
||||
Device tree example:
|
||||
BCM21664 family
|
||||
---------------
|
||||
CCU compatible string values for SoCs in the BCM21664 family are:
|
||||
"brcm,bcm21664-root-ccu"
|
||||
"brcm,bcm21664-aon-ccu"
|
||||
"brcm,bcm21664-master-ccu"
|
||||
"brcm,bcm21664-slave-ccu"
|
||||
|
||||
slave_ccu: slave_ccu {
|
||||
compatible = "brcm,bcm11351-slave-ccu";
|
||||
reg = <0x3e011000 0x0f00>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "uartb",
|
||||
"uartb2",
|
||||
"uartb3",
|
||||
"uartb4";
|
||||
};
|
||||
The following table defines the set of CCUs and clock specifiers for
|
||||
BCM21664 family clocks. When a clock consumer references a clocks,
|
||||
its symbolic specifier (rather than its numeric index value) should
|
||||
be used. These specifiers are defined in:
|
||||
"include/dt-bindings/clock/bcm21664.h"
|
||||
|
||||
ref_crystal_clk: ref_crystal {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <26000000>;
|
||||
};
|
||||
CCU Clock Type Index Specifier
|
||||
--- ----- ---- ----- ---------
|
||||
root frac_1m peri 0 BCM21664_ROOT_CCU_FRAC_1M
|
||||
|
||||
uart@3e002000 {
|
||||
compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart";
|
||||
status = "disabled";
|
||||
reg = <0x3e002000 0x1000>;
|
||||
clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB3>;
|
||||
interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg-shift = <2>;
|
||||
reg-io-width = <4>;
|
||||
};
|
||||
aon hub_timer peri 0 BCM21664_AON_CCU_HUB_TIMER
|
||||
|
||||
master sdio1 peri 0 BCM21664_MASTER_CCU_SDIO1
|
||||
master sdio2 peri 1 BCM21664_MASTER_CCU_SDIO2
|
||||
master sdio3 peri 2 BCM21664_MASTER_CCU_SDIO3
|
||||
master sdio4 peri 3 BCM21664_MASTER_CCU_SDIO4
|
||||
master sdio1_sleep peri 4 BCM21664_MASTER_CCU_SDIO1_SLEEP
|
||||
master sdio2_sleep peri 5 BCM21664_MASTER_CCU_SDIO2_SLEEP
|
||||
master sdio3_sleep peri 6 BCM21664_MASTER_CCU_SDIO3_SLEEP
|
||||
master sdio4_sleep peri 7 BCM21664_MASTER_CCU_SDIO4_SLEEP
|
||||
|
||||
slave uartb peri 0 BCM21664_SLAVE_CCU_UARTB
|
||||
slave uartb2 peri 1 BCM21664_SLAVE_CCU_UARTB2
|
||||
slave uartb3 peri 2 BCM21664_SLAVE_CCU_UARTB3
|
||||
slave uartb4 peri 3 BCM21664_SLAVE_CCU_UARTB4
|
||||
slave bsc1 peri 4 BCM21664_SLAVE_CCU_BSC1
|
||||
slave bsc2 peri 5 BCM21664_SLAVE_CCU_BSC2
|
||||
slave bsc3 peri 6 BCM21664_SLAVE_CCU_BSC3
|
||||
slave bsc4 peri 7 BCM21664_SLAVE_CCU_BSC4
|
||||
|
@ -44,10 +44,9 @@ For example:
|
||||
clocks by index. The names should reflect the clock output signal
|
||||
names for the device.
|
||||
|
||||
clock-indices: If the identifyng number for the clocks in the node
|
||||
is not linear from zero, then the this mapping allows
|
||||
the mapping of identifiers into the clock-output-names
|
||||
array.
|
||||
clock-indices: If the identifying number for the clocks in the node
|
||||
is not linear from zero, then this allows the mapping of
|
||||
identifiers into the clock-output-names array.
|
||||
|
||||
For example, if we have two clocks <&oscillator 1> and <&oscillator 3>:
|
||||
|
||||
@ -58,7 +57,7 @@ For example, if we have two clocks <&oscillator 1> and <&oscillator 3>:
|
||||
clock-output-names = "clka", "clkb";
|
||||
}
|
||||
|
||||
This ensures we do not have any empty nodes in clock-output-names
|
||||
This ensures we do not have any empty strings in clock-output-names
|
||||
|
||||
|
||||
==Clock consumers==
|
||||
|
@ -12,7 +12,6 @@ Required properties:
|
||||
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.
|
||||
|
||||
Example:
|
||||
|
31
Documentation/devicetree/bindings/clock/hix5hd2-clock.txt
Normal file
31
Documentation/devicetree/bindings/clock/hix5hd2-clock.txt
Normal file
@ -0,0 +1,31 @@
|
||||
* Hisilicon Hix5hd2 Clock Controller
|
||||
|
||||
The hix5hd2 clock controller generates and supplies clock to various
|
||||
controllers within the hix5hd2 SoC.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: should be "hisilicon,hix5hd2-clock"
|
||||
- reg: Address and length of the register set
|
||||
- #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/hix5hd2-clock.h>.
|
||||
|
||||
Examples:
|
||||
clock: clock@f8a22000 {
|
||||
compatible = "hisilicon,hix5hd2-clock";
|
||||
reg = <0xf8a22000 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
uart0: uart@f8b00000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0xf8b00000 0x1000>;
|
||||
interrupts = <0 49 4>;
|
||||
clocks = <&clock HIX5HD2_FIXED_83M>;
|
||||
clock-names = "apb_pclk";
|
||||
status = "disabled";
|
||||
};
|
29
Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt
Normal file
29
Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt
Normal file
@ -0,0 +1,29 @@
|
||||
AXM5516 clock driver bindings
|
||||
-----------------------------
|
||||
|
||||
Required properties :
|
||||
- compatible : shall contain "lsi,axm5516-clks"
|
||||
- reg : shall contain base register location and length
|
||||
- #clock-cells : shall contain 1
|
||||
|
||||
The consumer specifies the desired clock by having the clock ID in its "clocks"
|
||||
phandle cell. See <dt-bindings/clock/lsi,axxia-clock.h> for the list of
|
||||
supported clock IDs.
|
||||
|
||||
Example:
|
||||
|
||||
clks: clock-controller@2010020000 {
|
||||
compatible = "lsi,axm5516-clks";
|
||||
#clock-cells = <1>;
|
||||
reg = <0x20 0x10020000 0 0x20000>;
|
||||
};
|
||||
|
||||
serial0: uart@2010080000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x20 0x10080000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks AXXIA_CLK_PER>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
};
|
||||
|
@ -29,6 +29,11 @@ The following is a list of provided IDs and clock names on Kirkwood and Dove:
|
||||
2 = l2clk (L2 Cache clock derived from CPU0 clock)
|
||||
3 = ddrclk (DDR controller clock derived from CPU0 clock)
|
||||
|
||||
The following is a list of provided IDs and clock names on Orion5x:
|
||||
0 = tclk (Internal Bus clock)
|
||||
1 = cpuclk (CPU0 clock)
|
||||
2 = ddrclk (DDR controller clock derived from CPU0 clock)
|
||||
|
||||
Required properties:
|
||||
- compatible : shall be one of the following:
|
||||
"marvell,armada-370-core-clock" - For Armada 370 SoC core clocks
|
||||
@ -38,6 +43,9 @@ Required properties:
|
||||
"marvell,dove-core-clock" - for Dove SoC core clocks
|
||||
"marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180)
|
||||
"marvell,mv88f6180-core-clock" - for Kirkwood MV88f6180 SoC
|
||||
"marvell,mv88f5182-core-clock" - for Orion MV88F5182 SoC
|
||||
"marvell,mv88f5281-core-clock" - for Orion MV88F5281 SoC
|
||||
"marvell,mv88f6183-core-clock" - for Orion MV88F6183 SoC
|
||||
- reg : shall be the register address of the Sample-At-Reset (SAR) register
|
||||
- #clock-cells : from common clock binding; shall be set to 1
|
||||
|
||||
|
@ -4,6 +4,7 @@ Qualcomm Global Clock & Reset Controller Binding
|
||||
Required properties :
|
||||
- compatible : shall contain only one of the following:
|
||||
|
||||
"qcom,gcc-apq8064"
|
||||
"qcom,gcc-msm8660"
|
||||
"qcom,gcc-msm8960"
|
||||
"qcom,gcc-msm8974"
|
||||
|
@ -10,6 +10,7 @@ index in the group, from 0 to 31.
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be one of the following
|
||||
- "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,cpg-mstp-clock" for generic MSTP gate clocks
|
||||
|
@ -0,0 +1,41 @@
|
||||
These bindings should be considered EXPERIMENTAL for now.
|
||||
|
||||
* Renesas R8A7740 Clock Pulse Generator (CPG)
|
||||
|
||||
The CPG generates core clocks for the R8A7740 SoC. It includes three PLLs
|
||||
and several fixed ratio and variable ratio dividers.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be "renesas,r8a7740-cpg-clocks"
|
||||
|
||||
- reg: Base address and length of the memory resource used by the CPG
|
||||
|
||||
- clocks: Reference to the three parent clocks
|
||||
- #clock-cells: Must be 1
|
||||
- clock-output-names: The names of the clocks. Supported clocks are
|
||||
"system", "pllc0", "pllc1", "pllc2", "r", "usb24s", "i", "zg", "b",
|
||||
"m1", "hp", "hpp", "usbp", "s", "zb", "m3", and "cp".
|
||||
|
||||
- renesas,mode: board-specific settings of the MD_CK* bits
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
cpg_clocks: cpg_clocks@e6150000 {
|
||||
compatible = "renesas,r8a7740-cpg-clocks";
|
||||
reg = <0xe6150000 0x10000>;
|
||||
clocks = <&extal1_clk>, <&extal2_clk>, <&extalr_clk>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "system", "pllc0", "pllc1",
|
||||
"pllc2", "r",
|
||||
"usb24s",
|
||||
"i", "zg", "b", "m1", "hp",
|
||||
"hpp", "usbp", "s", "zb", "m3",
|
||||
"cp";
|
||||
};
|
||||
|
||||
&cpg_clocks {
|
||||
renesas,mode = <0x05>;
|
||||
};
|
@ -0,0 +1,27 @@
|
||||
* Renesas R8A7779 Clock Pulse Generator (CPG)
|
||||
|
||||
The CPG generates core clocks for the R8A7779. It includes one PLL and
|
||||
several fixed ratio dividers
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be "renesas,r8a7779-cpg-clocks"
|
||||
- reg: Base address and length of the memory resource used by the CPG
|
||||
|
||||
- clocks: Reference to the parent clock
|
||||
- #clock-cells: Must be 1
|
||||
- clock-output-names: The names of the clocks. Supported clocks are "plla",
|
||||
"z", "zs", "s", "s1", "p", "b", "out".
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
cpg_clocks: cpg_clocks@ffc80000 {
|
||||
compatible = "renesas,r8a7779-cpg-clocks";
|
||||
reg = <0 0xffc80000 0 0x30>;
|
||||
clocks = <&extal_clk>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "plla", "z", "zs", "s", "s1", "p",
|
||||
"b", "out";
|
||||
};
|
@ -14,6 +14,8 @@
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
#include "dt-bindings/clock/bcm21664.h"
|
||||
|
||||
#include "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
@ -43,7 +45,7 @@
|
||||
compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart";
|
||||
status = "disabled";
|
||||
reg = <0x3e000000 0x118>;
|
||||
clocks = <&uartb_clk>;
|
||||
clocks = <&slave_ccu BCM21664_SLAVE_CCU_UARTB>;
|
||||
interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg-shift = <2>;
|
||||
reg-io-width = <4>;
|
||||
@ -53,7 +55,7 @@
|
||||
compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart";
|
||||
status = "disabled";
|
||||
reg = <0x3e001000 0x118>;
|
||||
clocks = <&uartb2_clk>;
|
||||
clocks = <&slave_ccu BCM21664_SLAVE_CCU_UARTB2>;
|
||||
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg-shift = <2>;
|
||||
reg-io-width = <4>;
|
||||
@ -63,7 +65,7 @@
|
||||
compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart";
|
||||
status = "disabled";
|
||||
reg = <0x3e002000 0x118>;
|
||||
clocks = <&uartb3_clk>;
|
||||
clocks = <&slave_ccu BCM21664_SLAVE_CCU_UARTB3>;
|
||||
interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg-shift = <2>;
|
||||
reg-io-width = <4>;
|
||||
@ -85,7 +87,7 @@
|
||||
compatible = "brcm,kona-timer";
|
||||
reg = <0x35006000 0x1c>;
|
||||
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&hub_timer_clk>;
|
||||
clocks = <&aon_ccu BCM21664_AON_CCU_HUB_TIMER>;
|
||||
};
|
||||
|
||||
gpio: gpio@35003000 {
|
||||
@ -106,7 +108,7 @@
|
||||
compatible = "brcm,kona-sdhci";
|
||||
reg = <0x3f180000 0x801c>;
|
||||
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&sdio1_clk>;
|
||||
clocks = <&master_ccu BCM21664_MASTER_CCU_SDIO1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -114,7 +116,7 @@
|
||||
compatible = "brcm,kona-sdhci";
|
||||
reg = <0x3f190000 0x801c>;
|
||||
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&sdio2_clk>;
|
||||
clocks = <&master_ccu BCM21664_MASTER_CCU_SDIO2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -122,7 +124,7 @@
|
||||
compatible = "brcm,kona-sdhci";
|
||||
reg = <0x3f1a0000 0x801c>;
|
||||
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&sdio3_clk>;
|
||||
clocks = <&master_ccu BCM21664_MASTER_CCU_SDIO3>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -130,7 +132,7 @@
|
||||
compatible = "brcm,kona-sdhci";
|
||||
reg = <0x3f1b0000 0x801c>;
|
||||
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&sdio4_clk>;
|
||||
clocks = <&master_ccu BCM21664_MASTER_CCU_SDIO4>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -140,7 +142,7 @@
|
||||
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&bsc1_clk>;
|
||||
clocks = <&slave_ccu BCM21664_SLAVE_CCU_BSC1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -150,7 +152,7 @@
|
||||
interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&bsc2_clk>;
|
||||
clocks = <&slave_ccu BCM21664_SLAVE_CCU_BSC2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -160,7 +162,7 @@
|
||||
interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&bsc3_clk>;
|
||||
clocks = <&slave_ccu BCM21664_SLAVE_CCU_BSC3>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -170,105 +172,149 @@
|
||||
interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&bsc4_clk>;
|
||||
clocks = <&slave_ccu BCM21664_SLAVE_CCU_BSC4>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
clocks {
|
||||
bsc1_clk: bsc1 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
bsc2_clk: bsc2 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
/*
|
||||
* Fixed clocks are defined before CCUs whose
|
||||
* clocks may depend on them.
|
||||
*/
|
||||
|
||||
bsc3_clk: bsc3 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
ref_32k_clk: ref_32k {
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
bsc4_clk: bsc4 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
pmu_bsc_clk: pmu_bsc {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
hub_timer_clk: hub_timer {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <32768>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
pwm_clk: pwm {
|
||||
bbl_32k_clk: bbl_32k {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <32768>;
|
||||
};
|
||||
|
||||
ref_13m_clk: ref_13m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
};
|
||||
|
||||
var_13m_clk: var_13m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
};
|
||||
|
||||
dft_19_5m_clk: dft_19_5m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <19500000>;
|
||||
};
|
||||
|
||||
ref_crystal_clk: ref_crystal {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <26000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
sdio1_clk: sdio1 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <48000000>;
|
||||
ref_52m_clk: ref_52m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <52000000>;
|
||||
};
|
||||
|
||||
sdio2_clk: sdio2 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <48000000>;
|
||||
var_52m_clk: var_52m {
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
sdio3_clk: sdio3 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <48000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
sdio4_clk: sdio4 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <48000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
tmon_1m_clk: tmon_1m {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <1000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
uartb_clk: uartb {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
uartb2_clk: uartb2 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
uartb3_clk: uartb3 {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <13000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <52000000>;
|
||||
};
|
||||
|
||||
usb_otg_ahb_clk: usb_otg_ahb {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <52000000>;
|
||||
};
|
||||
|
||||
ref_96m_clk: ref_96m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <96000000>;
|
||||
};
|
||||
|
||||
var_96m_clk: var_96m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <96000000>;
|
||||
};
|
||||
|
||||
ref_104m_clk: ref_104m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <104000000>;
|
||||
};
|
||||
|
||||
var_104m_clk: var_104m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <104000000>;
|
||||
};
|
||||
|
||||
ref_156m_clk: ref_156m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <156000000>;
|
||||
};
|
||||
|
||||
var_156m_clk: var_156m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <156000000>;
|
||||
};
|
||||
|
||||
root_ccu: root_ccu {
|
||||
compatible = BCM21664_DT_ROOT_CCU_COMPAT;
|
||||
reg = <0x35001000 0x0f00>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "frac_1m";
|
||||
};
|
||||
|
||||
aon_ccu: aon_ccu {
|
||||
compatible = BCM21664_DT_AON_CCU_COMPAT;
|
||||
reg = <0x35002000 0x0f00>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "hub_timer";
|
||||
};
|
||||
|
||||
master_ccu: master_ccu {
|
||||
compatible = BCM21664_DT_MASTER_CCU_COMPAT;
|
||||
reg = <0x3f001000 0x0f00>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "sdio1",
|
||||
"sdio2",
|
||||
"sdio3",
|
||||
"sdio4",
|
||||
"sdio1_sleep",
|
||||
"sdio2_sleep",
|
||||
"sdio3_sleep",
|
||||
"sdio4_sleep";
|
||||
};
|
||||
|
||||
slave_ccu: slave_ccu {
|
||||
compatible = BCM21664_DT_SLAVE_CCU_COMPAT;
|
||||
reg = <0x3e011000 0x0f00>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "uartb",
|
||||
"uartb2",
|
||||
"uartb3",
|
||||
"bsc1",
|
||||
"bsc2",
|
||||
"bsc3",
|
||||
"bsc4";
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o
|
||||
# hardware specific clock types
|
||||
# please keep this section sorted lexicographically by file/directory path name
|
||||
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
|
||||
obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
|
||||
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
|
||||
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
|
||||
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
|
||||
@ -33,6 +34,7 @@ obj-$(CONFIG_COMMON_CLK_AT91) += at91/
|
||||
obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/
|
||||
obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_HIP04) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/
|
||||
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
|
||||
ifeq ($(CONFIG_COMMON_CLK), y)
|
||||
obj-$(CONFIG_ARCH_MMP) += mmp/
|
||||
|
@ -6,4 +6,4 @@ config CLK_BCM_KONA
|
||||
help
|
||||
Enable common clock framework support for Broadcom SoCs
|
||||
using "Kona" style clock control units, including those
|
||||
in the BCM281xx family.
|
||||
in the BCM281xx and BCM21664 families.
|
||||
|
@ -1,3 +1,4 @@
|
||||
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o
|
||||
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
|
||||
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
|
||||
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o
|
||||
|
290
drivers/clk/bcm/clk-bcm21664.c
Normal file
290
drivers/clk/bcm/clk-bcm21664.c
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
* Copyright 2014 Linaro Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "clk-kona.h"
|
||||
#include "dt-bindings/clock/bcm21664.h"
|
||||
|
||||
#define BCM21664_CCU_COMMON(_name, _capname) \
|
||||
KONA_CCU_COMMON(BCM21664, _name, _capname)
|
||||
|
||||
/* Root CCU */
|
||||
|
||||
static struct peri_clk_data frac_1m_data = {
|
||||
.gate = HW_SW_GATE(0x214, 16, 0, 1),
|
||||
.clocks = CLOCKS("ref_crystal"),
|
||||
};
|
||||
|
||||
static struct ccu_data root_ccu_data = {
|
||||
BCM21664_CCU_COMMON(root, ROOT),
|
||||
/* no policy control */
|
||||
.kona_clks = {
|
||||
[BCM21664_ROOT_CCU_FRAC_1M] =
|
||||
KONA_CLK(root, frac_1m, peri),
|
||||
[BCM21664_ROOT_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* AON CCU */
|
||||
|
||||
static struct peri_clk_data hub_timer_data = {
|
||||
.gate = HW_SW_GATE(0x0414, 16, 0, 1),
|
||||
.hyst = HYST(0x0414, 8, 9),
|
||||
.clocks = CLOCKS("bbl_32k",
|
||||
"frac_1m",
|
||||
"dft_19_5m"),
|
||||
.sel = SELECTOR(0x0a10, 0, 2),
|
||||
.trig = TRIGGER(0x0a40, 4),
|
||||
};
|
||||
|
||||
static struct ccu_data aon_ccu_data = {
|
||||
BCM21664_CCU_COMMON(aon, AON),
|
||||
.policy = {
|
||||
.enable = CCU_LVM_EN(0x0034, 0),
|
||||
.control = CCU_POLICY_CTL(0x000c, 0, 1, 2),
|
||||
},
|
||||
.kona_clks = {
|
||||
[BCM21664_AON_CCU_HUB_TIMER] =
|
||||
KONA_CLK(aon, hub_timer, peri),
|
||||
[BCM21664_AON_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* Master CCU */
|
||||
|
||||
static struct peri_clk_data sdio1_data = {
|
||||
.gate = HW_SW_GATE(0x0358, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a28, 0, 3),
|
||||
.div = DIVIDER(0x0a28, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 9),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio2_data = {
|
||||
.gate = HW_SW_GATE(0x035c, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a2c, 0, 3),
|
||||
.div = DIVIDER(0x0a2c, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 10),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio3_data = {
|
||||
.gate = HW_SW_GATE(0x0364, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a34, 0, 3),
|
||||
.div = DIVIDER(0x0a34, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 12),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio4_data = {
|
||||
.gate = HW_SW_GATE(0x0360, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_52m",
|
||||
"ref_52m",
|
||||
"var_96m",
|
||||
"ref_96m"),
|
||||
.sel = SELECTOR(0x0a30, 0, 3),
|
||||
.div = DIVIDER(0x0a30, 4, 14),
|
||||
.trig = TRIGGER(0x0afc, 11),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio1_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"), /* Verify */
|
||||
.gate = HW_SW_GATE(0x0358, 18, 2, 3),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio2_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"), /* Verify */
|
||||
.gate = HW_SW_GATE(0x035c, 18, 2, 3),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio3_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"), /* Verify */
|
||||
.gate = HW_SW_GATE(0x0364, 18, 2, 3),
|
||||
};
|
||||
|
||||
static struct peri_clk_data sdio4_sleep_data = {
|
||||
.clocks = CLOCKS("ref_32k"), /* Verify */
|
||||
.gate = HW_SW_GATE(0x0360, 18, 2, 3),
|
||||
};
|
||||
|
||||
static struct ccu_data master_ccu_data = {
|
||||
BCM21664_CCU_COMMON(master, MASTER),
|
||||
.policy = {
|
||||
.enable = CCU_LVM_EN(0x0034, 0),
|
||||
.control = CCU_POLICY_CTL(0x000c, 0, 1, 2),
|
||||
},
|
||||
.kona_clks = {
|
||||
[BCM21664_MASTER_CCU_SDIO1] =
|
||||
KONA_CLK(master, sdio1, peri),
|
||||
[BCM21664_MASTER_CCU_SDIO2] =
|
||||
KONA_CLK(master, sdio2, peri),
|
||||
[BCM21664_MASTER_CCU_SDIO3] =
|
||||
KONA_CLK(master, sdio3, peri),
|
||||
[BCM21664_MASTER_CCU_SDIO4] =
|
||||
KONA_CLK(master, sdio4, peri),
|
||||
[BCM21664_MASTER_CCU_SDIO1_SLEEP] =
|
||||
KONA_CLK(master, sdio1_sleep, peri),
|
||||
[BCM21664_MASTER_CCU_SDIO2_SLEEP] =
|
||||
KONA_CLK(master, sdio2_sleep, peri),
|
||||
[BCM21664_MASTER_CCU_SDIO3_SLEEP] =
|
||||
KONA_CLK(master, sdio3_sleep, peri),
|
||||
[BCM21664_MASTER_CCU_SDIO4_SLEEP] =
|
||||
KONA_CLK(master, sdio4_sleep, peri),
|
||||
[BCM21664_MASTER_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* Slave CCU */
|
||||
|
||||
static struct peri_clk_data uartb_data = {
|
||||
.gate = HW_SW_GATE(0x0400, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_156m",
|
||||
"ref_156m"),
|
||||
.sel = SELECTOR(0x0a10, 0, 2),
|
||||
.div = FRAC_DIVIDER(0x0a10, 4, 12, 8),
|
||||
.trig = TRIGGER(0x0afc, 2),
|
||||
};
|
||||
|
||||
static struct peri_clk_data uartb2_data = {
|
||||
.gate = HW_SW_GATE(0x0404, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_156m",
|
||||
"ref_156m"),
|
||||
.sel = SELECTOR(0x0a14, 0, 2),
|
||||
.div = FRAC_DIVIDER(0x0a14, 4, 12, 8),
|
||||
.trig = TRIGGER(0x0afc, 3),
|
||||
};
|
||||
|
||||
static struct peri_clk_data uartb3_data = {
|
||||
.gate = HW_SW_GATE(0x0408, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_156m",
|
||||
"ref_156m"),
|
||||
.sel = SELECTOR(0x0a18, 0, 2),
|
||||
.div = FRAC_DIVIDER(0x0a18, 4, 12, 8),
|
||||
.trig = TRIGGER(0x0afc, 4),
|
||||
};
|
||||
|
||||
static struct peri_clk_data bsc1_data = {
|
||||
.gate = HW_SW_GATE(0x0458, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a64, 0, 3),
|
||||
.trig = TRIGGER(0x0afc, 23),
|
||||
};
|
||||
|
||||
static struct peri_clk_data bsc2_data = {
|
||||
.gate = HW_SW_GATE(0x045c, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a68, 0, 3),
|
||||
.trig = TRIGGER(0x0afc, 24),
|
||||
};
|
||||
|
||||
static struct peri_clk_data bsc3_data = {
|
||||
.gate = HW_SW_GATE(0x0470, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a7c, 0, 3),
|
||||
.trig = TRIGGER(0x0afc, 18),
|
||||
};
|
||||
|
||||
static struct peri_clk_data bsc4_data = {
|
||||
.gate = HW_SW_GATE(0x0474, 18, 2, 3),
|
||||
.clocks = CLOCKS("ref_crystal",
|
||||
"var_104m",
|
||||
"ref_104m",
|
||||
"var_13m",
|
||||
"ref_13m"),
|
||||
.sel = SELECTOR(0x0a80, 0, 3),
|
||||
.trig = TRIGGER(0x0afc, 19),
|
||||
};
|
||||
|
||||
static struct ccu_data slave_ccu_data = {
|
||||
BCM21664_CCU_COMMON(slave, SLAVE),
|
||||
.policy = {
|
||||
.enable = CCU_LVM_EN(0x0034, 0),
|
||||
.control = CCU_POLICY_CTL(0x000c, 0, 1, 2),
|
||||
},
|
||||
.kona_clks = {
|
||||
[BCM21664_SLAVE_CCU_UARTB] =
|
||||
KONA_CLK(slave, uartb, peri),
|
||||
[BCM21664_SLAVE_CCU_UARTB2] =
|
||||
KONA_CLK(slave, uartb2, peri),
|
||||
[BCM21664_SLAVE_CCU_UARTB3] =
|
||||
KONA_CLK(slave, uartb3, peri),
|
||||
[BCM21664_SLAVE_CCU_BSC1] =
|
||||
KONA_CLK(slave, bsc1, peri),
|
||||
[BCM21664_SLAVE_CCU_BSC2] =
|
||||
KONA_CLK(slave, bsc2, peri),
|
||||
[BCM21664_SLAVE_CCU_BSC3] =
|
||||
KONA_CLK(slave, bsc3, peri),
|
||||
[BCM21664_SLAVE_CCU_BSC4] =
|
||||
KONA_CLK(slave, bsc4, peri),
|
||||
[BCM21664_SLAVE_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* Device tree match table callback functions */
|
||||
|
||||
static void __init kona_dt_root_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(&root_ccu_data, node);
|
||||
}
|
||||
|
||||
static void __init kona_dt_aon_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(&aon_ccu_data, node);
|
||||
}
|
||||
|
||||
static void __init kona_dt_master_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(&master_ccu_data, node);
|
||||
}
|
||||
|
||||
static void __init kona_dt_slave_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(&slave_ccu_data, node);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(bcm21664_root_ccu, BCM21664_DT_ROOT_CCU_COMPAT,
|
||||
kona_dt_root_ccu_setup);
|
||||
CLK_OF_DECLARE(bcm21664_aon_ccu, BCM21664_DT_AON_CCU_COMPAT,
|
||||
kona_dt_aon_ccu_setup);
|
||||
CLK_OF_DECLARE(bcm21664_master_ccu, BCM21664_DT_MASTER_CCU_COMPAT,
|
||||
kona_dt_master_ccu_setup);
|
||||
CLK_OF_DECLARE(bcm21664_slave_ccu, BCM21664_DT_SLAVE_CCU_COMPAT,
|
||||
kona_dt_slave_ccu_setup);
|
@ -15,14 +15,10 @@
|
||||
#include "clk-kona.h"
|
||||
#include "dt-bindings/clock/bcm281xx.h"
|
||||
|
||||
/* bcm11351 CCU device tree "compatible" strings */
|
||||
#define BCM11351_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu"
|
||||
#define BCM11351_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu"
|
||||
#define BCM11351_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu"
|
||||
#define BCM11351_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu"
|
||||
#define BCM11351_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu"
|
||||
#define BCM281XX_CCU_COMMON(_name, _ucase_name) \
|
||||
KONA_CCU_COMMON(BCM281XX, _name, _ucase_name)
|
||||
|
||||
/* Root CCU clocks */
|
||||
/* Root CCU */
|
||||
|
||||
static struct peri_clk_data frac_1m_data = {
|
||||
.gate = HW_SW_GATE(0x214, 16, 0, 1),
|
||||
@ -31,7 +27,16 @@ static struct peri_clk_data frac_1m_data = {
|
||||
.clocks = CLOCKS("ref_crystal"),
|
||||
};
|
||||
|
||||
/* AON CCU clocks */
|
||||
static struct ccu_data root_ccu_data = {
|
||||
BCM281XX_CCU_COMMON(root, ROOT),
|
||||
.kona_clks = {
|
||||
[BCM281XX_ROOT_CCU_FRAC_1M] =
|
||||
KONA_CLK(root, frac_1m, peri),
|
||||
[BCM281XX_ROOT_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* AON CCU */
|
||||
|
||||
static struct peri_clk_data hub_timer_data = {
|
||||
.gate = HW_SW_GATE(0x0414, 16, 0, 1),
|
||||
@ -60,7 +65,20 @@ static struct peri_clk_data pmu_bsc_var_data = {
|
||||
.trig = TRIGGER(0x0a40, 2),
|
||||
};
|
||||
|
||||
/* Hub CCU clocks */
|
||||
static struct ccu_data aon_ccu_data = {
|
||||
BCM281XX_CCU_COMMON(aon, AON),
|
||||
.kona_clks = {
|
||||
[BCM281XX_AON_CCU_HUB_TIMER] =
|
||||
KONA_CLK(aon, hub_timer, peri),
|
||||
[BCM281XX_AON_CCU_PMU_BSC] =
|
||||
KONA_CLK(aon, pmu_bsc, peri),
|
||||
[BCM281XX_AON_CCU_PMU_BSC_VAR] =
|
||||
KONA_CLK(aon, pmu_bsc_var, peri),
|
||||
[BCM281XX_AON_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* Hub CCU */
|
||||
|
||||
static struct peri_clk_data tmon_1m_data = {
|
||||
.gate = HW_SW_GATE(0x04a4, 18, 2, 3),
|
||||
@ -70,7 +88,16 @@ static struct peri_clk_data tmon_1m_data = {
|
||||
.trig = TRIGGER(0x0e84, 1),
|
||||
};
|
||||
|
||||
/* Master CCU clocks */
|
||||
static struct ccu_data hub_ccu_data = {
|
||||
BCM281XX_CCU_COMMON(hub, HUB),
|
||||
.kona_clks = {
|
||||
[BCM281XX_HUB_CCU_TMON_1M] =
|
||||
KONA_CLK(hub, tmon_1m, peri),
|
||||
[BCM281XX_HUB_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* Master CCU */
|
||||
|
||||
static struct peri_clk_data sdio1_data = {
|
||||
.gate = HW_SW_GATE(0x0358, 18, 2, 3),
|
||||
@ -153,7 +180,28 @@ static struct peri_clk_data hsic2_12m_data = {
|
||||
.trig = TRIGGER(0x0afc, 5),
|
||||
};
|
||||
|
||||
/* Slave CCU clocks */
|
||||
static struct ccu_data master_ccu_data = {
|
||||
BCM281XX_CCU_COMMON(master, MASTER),
|
||||
.kona_clks = {
|
||||
[BCM281XX_MASTER_CCU_SDIO1] =
|
||||
KONA_CLK(master, sdio1, peri),
|
||||
[BCM281XX_MASTER_CCU_SDIO2] =
|
||||
KONA_CLK(master, sdio2, peri),
|
||||
[BCM281XX_MASTER_CCU_SDIO3] =
|
||||
KONA_CLK(master, sdio3, peri),
|
||||
[BCM281XX_MASTER_CCU_SDIO4] =
|
||||
KONA_CLK(master, sdio4, peri),
|
||||
[BCM281XX_MASTER_CCU_USB_IC] =
|
||||
KONA_CLK(master, usb_ic, peri),
|
||||
[BCM281XX_MASTER_CCU_HSIC2_48M] =
|
||||
KONA_CLK(master, hsic2_48m, peri),
|
||||
[BCM281XX_MASTER_CCU_HSIC2_12M] =
|
||||
KONA_CLK(master, hsic2_12m, peri),
|
||||
[BCM281XX_MASTER_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* Slave CCU */
|
||||
|
||||
static struct peri_clk_data uartb_data = {
|
||||
.gate = HW_SW_GATE(0x0400, 18, 2, 3),
|
||||
@ -261,156 +309,67 @@ static struct peri_clk_data pwm_data = {
|
||||
.trig = TRIGGER(0x0afc, 15),
|
||||
};
|
||||
|
||||
/*
|
||||
* CCU setup routines
|
||||
*
|
||||
* These are called from kona_dt_ccu_setup() to initialize the array
|
||||
* of clocks provided by the CCU. Once allocated, the entries in
|
||||
* the array are initialized by calling kona_clk_setup() with the
|
||||
* initialization data for each clock. They return 0 if successful
|
||||
* or an error code otherwise.
|
||||
*/
|
||||
static int __init bcm281xx_root_ccu_clks_setup(struct ccu_data *ccu)
|
||||
{
|
||||
struct clk **clks;
|
||||
size_t count = BCM281XX_ROOT_CCU_CLOCK_COUNT;
|
||||
|
||||
clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
|
||||
if (!clks) {
|
||||
pr_err("%s: failed to allocate root clocks\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ccu->data.clks = clks;
|
||||
ccu->data.clk_num = count;
|
||||
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_ROOT_CCU_FRAC_1M, frac_1m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init bcm281xx_aon_ccu_clks_setup(struct ccu_data *ccu)
|
||||
{
|
||||
struct clk **clks;
|
||||
size_t count = BCM281XX_AON_CCU_CLOCK_COUNT;
|
||||
|
||||
clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
|
||||
if (!clks) {
|
||||
pr_err("%s: failed to allocate aon clocks\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ccu->data.clks = clks;
|
||||
ccu->data.clk_num = count;
|
||||
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_HUB_TIMER, hub_timer);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC, pmu_bsc);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC_VAR, pmu_bsc_var);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init bcm281xx_hub_ccu_clks_setup(struct ccu_data *ccu)
|
||||
{
|
||||
struct clk **clks;
|
||||
size_t count = BCM281XX_HUB_CCU_CLOCK_COUNT;
|
||||
|
||||
clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
|
||||
if (!clks) {
|
||||
pr_err("%s: failed to allocate hub clocks\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ccu->data.clks = clks;
|
||||
ccu->data.clk_num = count;
|
||||
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_HUB_CCU_TMON_1M, tmon_1m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init bcm281xx_master_ccu_clks_setup(struct ccu_data *ccu)
|
||||
{
|
||||
struct clk **clks;
|
||||
size_t count = BCM281XX_MASTER_CCU_CLOCK_COUNT;
|
||||
|
||||
clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
|
||||
if (!clks) {
|
||||
pr_err("%s: failed to allocate master clocks\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ccu->data.clks = clks;
|
||||
ccu->data.clk_num = count;
|
||||
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO1, sdio1);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO2, sdio2);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO3, sdio3);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO4, sdio4);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_USB_IC, usb_ic);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_48M, hsic2_48m);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_12M, hsic2_12m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init bcm281xx_slave_ccu_clks_setup(struct ccu_data *ccu)
|
||||
{
|
||||
struct clk **clks;
|
||||
size_t count = BCM281XX_SLAVE_CCU_CLOCK_COUNT;
|
||||
|
||||
clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
|
||||
if (!clks) {
|
||||
pr_err("%s: failed to allocate slave clocks\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ccu->data.clks = clks;
|
||||
ccu->data.clk_num = count;
|
||||
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB, uartb);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB2, uartb2);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB3, uartb3);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB4, uartb4);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP0, ssp0);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP2, ssp2);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC1, bsc1);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC2, bsc2);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC3, bsc3);
|
||||
PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_PWM, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static struct ccu_data slave_ccu_data = {
|
||||
BCM281XX_CCU_COMMON(slave, SLAVE),
|
||||
.kona_clks = {
|
||||
[BCM281XX_SLAVE_CCU_UARTB] =
|
||||
KONA_CLK(slave, uartb, peri),
|
||||
[BCM281XX_SLAVE_CCU_UARTB2] =
|
||||
KONA_CLK(slave, uartb2, peri),
|
||||
[BCM281XX_SLAVE_CCU_UARTB3] =
|
||||
KONA_CLK(slave, uartb3, peri),
|
||||
[BCM281XX_SLAVE_CCU_UARTB4] =
|
||||
KONA_CLK(slave, uartb4, peri),
|
||||
[BCM281XX_SLAVE_CCU_SSP0] =
|
||||
KONA_CLK(slave, ssp0, peri),
|
||||
[BCM281XX_SLAVE_CCU_SSP2] =
|
||||
KONA_CLK(slave, ssp2, peri),
|
||||
[BCM281XX_SLAVE_CCU_BSC1] =
|
||||
KONA_CLK(slave, bsc1, peri),
|
||||
[BCM281XX_SLAVE_CCU_BSC2] =
|
||||
KONA_CLK(slave, bsc2, peri),
|
||||
[BCM281XX_SLAVE_CCU_BSC3] =
|
||||
KONA_CLK(slave, bsc3, peri),
|
||||
[BCM281XX_SLAVE_CCU_PWM] =
|
||||
KONA_CLK(slave, pwm, peri),
|
||||
[BCM281XX_SLAVE_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
|
||||
},
|
||||
};
|
||||
|
||||
/* Device tree match table callback functions */
|
||||
|
||||
static void __init kona_dt_root_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(node, bcm281xx_root_ccu_clks_setup);
|
||||
kona_dt_ccu_setup(&root_ccu_data, node);
|
||||
}
|
||||
|
||||
static void __init kona_dt_aon_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(node, bcm281xx_aon_ccu_clks_setup);
|
||||
kona_dt_ccu_setup(&aon_ccu_data, node);
|
||||
}
|
||||
|
||||
static void __init kona_dt_hub_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(node, bcm281xx_hub_ccu_clks_setup);
|
||||
kona_dt_ccu_setup(&hub_ccu_data, node);
|
||||
}
|
||||
|
||||
static void __init kona_dt_master_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(node, bcm281xx_master_ccu_clks_setup);
|
||||
kona_dt_ccu_setup(&master_ccu_data, node);
|
||||
}
|
||||
|
||||
static void __init kona_dt_slave_ccu_setup(struct device_node *node)
|
||||
{
|
||||
kona_dt_ccu_setup(node, bcm281xx_slave_ccu_clks_setup);
|
||||
kona_dt_ccu_setup(&slave_ccu_data, node);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(bcm11351_root_ccu, BCM11351_DT_ROOT_CCU_COMPAT,
|
||||
CLK_OF_DECLARE(bcm281xx_root_ccu, BCM281XX_DT_ROOT_CCU_COMPAT,
|
||||
kona_dt_root_ccu_setup);
|
||||
CLK_OF_DECLARE(bcm11351_aon_ccu, BCM11351_DT_AON_CCU_COMPAT,
|
||||
CLK_OF_DECLARE(bcm281xx_aon_ccu, BCM281XX_DT_AON_CCU_COMPAT,
|
||||
kona_dt_aon_ccu_setup);
|
||||
CLK_OF_DECLARE(bcm11351_hub_ccu, BCM11351_DT_HUB_CCU_COMPAT,
|
||||
CLK_OF_DECLARE(bcm281xx_hub_ccu, BCM281XX_DT_HUB_CCU_COMPAT,
|
||||
kona_dt_hub_ccu_setup);
|
||||
CLK_OF_DECLARE(bcm11351_master_ccu, BCM11351_DT_MASTER_CCU_COMPAT,
|
||||
CLK_OF_DECLARE(bcm281xx_master_ccu, BCM281XX_DT_MASTER_CCU_COMPAT,
|
||||
kona_dt_master_ccu_setup);
|
||||
CLK_OF_DECLARE(bcm11351_slave_ccu, BCM11351_DT_SLAVE_CCU_COMPAT,
|
||||
CLK_OF_DECLARE(bcm281xx_slave_ccu, BCM281XX_DT_SLAVE_CCU_COMPAT,
|
||||
kona_dt_slave_ccu_setup);
|
||||
|
@ -25,6 +25,31 @@ LIST_HEAD(ccu_list); /* The list of set up CCUs */
|
||||
|
||||
/* Validity checking */
|
||||
|
||||
static bool ccu_data_offsets_valid(struct ccu_data *ccu)
|
||||
{
|
||||
struct ccu_policy *ccu_policy = &ccu->policy;
|
||||
u32 limit;
|
||||
|
||||
limit = ccu->range - sizeof(u32);
|
||||
limit = round_down(limit, sizeof(u32));
|
||||
if (ccu_policy_exists(ccu_policy)) {
|
||||
if (ccu_policy->enable.offset > limit) {
|
||||
pr_err("%s: bad policy enable offset for %s "
|
||||
"(%u > %u)\n", __func__,
|
||||
ccu->name, ccu_policy->enable.offset, limit);
|
||||
return false;
|
||||
}
|
||||
if (ccu_policy->control.offset > limit) {
|
||||
pr_err("%s: bad policy control offset for %s "
|
||||
"(%u > %u)\n", __func__,
|
||||
ccu->name, ccu_policy->control.offset, limit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clk_requires_trigger(struct kona_clk *bcm_clk)
|
||||
{
|
||||
struct peri_clk_data *peri = bcm_clk->u.peri;
|
||||
@ -54,7 +79,9 @@ static bool clk_requires_trigger(struct kona_clk *bcm_clk)
|
||||
static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
|
||||
{
|
||||
struct peri_clk_data *peri;
|
||||
struct bcm_clk_policy *policy;
|
||||
struct bcm_clk_gate *gate;
|
||||
struct bcm_clk_hyst *hyst;
|
||||
struct bcm_clk_div *div;
|
||||
struct bcm_clk_sel *sel;
|
||||
struct bcm_clk_trig *trig;
|
||||
@ -64,19 +91,41 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
|
||||
|
||||
BUG_ON(bcm_clk->type != bcm_clk_peri);
|
||||
peri = bcm_clk->u.peri;
|
||||
name = bcm_clk->name;
|
||||
name = bcm_clk->init_data.name;
|
||||
range = bcm_clk->ccu->range;
|
||||
|
||||
limit = range - sizeof(u32);
|
||||
limit = round_down(limit, sizeof(u32));
|
||||
|
||||
policy = &peri->policy;
|
||||
if (policy_exists(policy)) {
|
||||
if (policy->offset > limit) {
|
||||
pr_err("%s: bad policy offset for %s (%u > %u)\n",
|
||||
__func__, name, policy->offset, limit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gate = &peri->gate;
|
||||
hyst = &peri->hyst;
|
||||
if (gate_exists(gate)) {
|
||||
if (gate->offset > limit) {
|
||||
pr_err("%s: bad gate offset for %s (%u > %u)\n",
|
||||
__func__, name, gate->offset, limit);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hyst_exists(hyst)) {
|
||||
if (hyst->offset > limit) {
|
||||
pr_err("%s: bad hysteresis offset for %s "
|
||||
"(%u > %u)\n", __func__,
|
||||
name, hyst->offset, limit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (hyst_exists(hyst)) {
|
||||
pr_err("%s: hysteresis but no gate for %s\n", __func__, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
div = &peri->div;
|
||||
@ -167,6 +216,36 @@ static bool bitfield_valid(u32 shift, u32 width, const char *field_name,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ccu_policy_valid(struct ccu_policy *ccu_policy, const char *ccu_name)
|
||||
{
|
||||
struct bcm_lvm_en *enable = &ccu_policy->enable;
|
||||
struct bcm_policy_ctl *control;
|
||||
|
||||
if (!bit_posn_valid(enable->bit, "policy enable", ccu_name))
|
||||
return false;
|
||||
|
||||
control = &ccu_policy->control;
|
||||
if (!bit_posn_valid(control->go_bit, "policy control GO", ccu_name))
|
||||
return false;
|
||||
|
||||
if (!bit_posn_valid(control->atl_bit, "policy control ATL", ccu_name))
|
||||
return false;
|
||||
|
||||
if (!bit_posn_valid(control->ac_bit, "policy control AC", ccu_name))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool policy_valid(struct bcm_clk_policy *policy, const char *clock_name)
|
||||
{
|
||||
if (!bit_posn_valid(policy->bit, "policy", clock_name))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* All gates, if defined, have a status bit, and for hardware-only
|
||||
* gates, that's it. Gates that can be software controlled also
|
||||
@ -196,6 +275,17 @@ static bool gate_valid(struct bcm_clk_gate *gate, const char *field_name,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hyst_valid(struct bcm_clk_hyst *hyst, const char *clock_name)
|
||||
{
|
||||
if (!bit_posn_valid(hyst->en_bit, "hysteresis enable", clock_name))
|
||||
return false;
|
||||
|
||||
if (!bit_posn_valid(hyst->val_bit, "hysteresis value", clock_name))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* A selector bitfield must be valid. Its parent_sel array must
|
||||
* also be reasonable for the field.
|
||||
@ -312,7 +402,9 @@ static bool
|
||||
peri_clk_data_valid(struct kona_clk *bcm_clk)
|
||||
{
|
||||
struct peri_clk_data *peri;
|
||||
struct bcm_clk_policy *policy;
|
||||
struct bcm_clk_gate *gate;
|
||||
struct bcm_clk_hyst *hyst;
|
||||
struct bcm_clk_sel *sel;
|
||||
struct bcm_clk_div *div;
|
||||
struct bcm_clk_div *pre_div;
|
||||
@ -330,11 +422,20 @@ peri_clk_data_valid(struct kona_clk *bcm_clk)
|
||||
return false;
|
||||
|
||||
peri = bcm_clk->u.peri;
|
||||
name = bcm_clk->name;
|
||||
name = bcm_clk->init_data.name;
|
||||
|
||||
policy = &peri->policy;
|
||||
if (policy_exists(policy) && !policy_valid(policy, name))
|
||||
return false;
|
||||
|
||||
gate = &peri->gate;
|
||||
if (gate_exists(gate) && !gate_valid(gate, "gate", name))
|
||||
return false;
|
||||
|
||||
hyst = &peri->hyst;
|
||||
if (hyst_exists(hyst) && !hyst_valid(hyst, name))
|
||||
return false;
|
||||
|
||||
sel = &peri->sel;
|
||||
if (selector_exists(sel)) {
|
||||
if (!sel_valid(sel, "selector", name))
|
||||
@ -567,7 +668,6 @@ static void peri_clk_teardown(struct peri_clk_data *data,
|
||||
struct clk_init_data *init_data)
|
||||
{
|
||||
clk_sel_teardown(&data->sel, init_data);
|
||||
init_data->ops = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -576,10 +676,9 @@ static void peri_clk_teardown(struct peri_clk_data *data,
|
||||
* that can be assigned if the clock has one or more parent clocks
|
||||
* associated with it.
|
||||
*/
|
||||
static int peri_clk_setup(struct ccu_data *ccu, struct peri_clk_data *data,
|
||||
struct clk_init_data *init_data)
|
||||
static int
|
||||
peri_clk_setup(struct peri_clk_data *data, struct clk_init_data *init_data)
|
||||
{
|
||||
init_data->ops = &kona_peri_clk_ops;
|
||||
init_data->flags = CLK_IGNORE_UNUSED;
|
||||
|
||||
return clk_sel_setup(data->clocks, &data->sel, init_data);
|
||||
@ -617,39 +716,26 @@ static void kona_clk_teardown(struct clk *clk)
|
||||
bcm_clk_teardown(bcm_clk);
|
||||
}
|
||||
|
||||
struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
|
||||
enum bcm_clk_type type, void *data)
|
||||
struct clk *kona_clk_setup(struct kona_clk *bcm_clk)
|
||||
{
|
||||
struct kona_clk *bcm_clk;
|
||||
struct clk_init_data *init_data;
|
||||
struct clk_init_data *init_data = &bcm_clk->init_data;
|
||||
struct clk *clk = NULL;
|
||||
|
||||
bcm_clk = kzalloc(sizeof(*bcm_clk), GFP_KERNEL);
|
||||
if (!bcm_clk) {
|
||||
pr_err("%s: failed to allocate bcm_clk for %s\n", __func__,
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
bcm_clk->ccu = ccu;
|
||||
bcm_clk->name = name;
|
||||
|
||||
init_data = &bcm_clk->init_data;
|
||||
init_data->name = name;
|
||||
switch (type) {
|
||||
switch (bcm_clk->type) {
|
||||
case bcm_clk_peri:
|
||||
if (peri_clk_setup(ccu, data, init_data))
|
||||
goto out_free;
|
||||
if (peri_clk_setup(bcm_clk->u.data, init_data))
|
||||
return NULL;
|
||||
break;
|
||||
default:
|
||||
data = NULL;
|
||||
break;
|
||||
pr_err("%s: clock type %d invalid for %s\n", __func__,
|
||||
(int)bcm_clk->type, init_data->name);
|
||||
return NULL;
|
||||
}
|
||||
bcm_clk->type = type;
|
||||
bcm_clk->u.data = data;
|
||||
|
||||
/* Make sure everything makes sense before we set it up */
|
||||
if (!kona_clk_valid(bcm_clk)) {
|
||||
pr_err("%s: clock data invalid for %s\n", __func__, name);
|
||||
pr_err("%s: clock data invalid for %s\n", __func__,
|
||||
init_data->name);
|
||||
goto out_teardown;
|
||||
}
|
||||
|
||||
@ -657,7 +743,7 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
|
||||
clk = clk_register(NULL, &bcm_clk->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: error registering clock %s (%ld)\n", __func__,
|
||||
name, PTR_ERR(clk));
|
||||
init_data->name, PTR_ERR(clk));
|
||||
goto out_teardown;
|
||||
}
|
||||
BUG_ON(!clk);
|
||||
@ -665,8 +751,6 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
|
||||
return clk;
|
||||
out_teardown:
|
||||
bcm_clk_teardown(bcm_clk);
|
||||
out_free:
|
||||
kfree(bcm_clk);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -675,50 +759,64 @@ static void ccu_clks_teardown(struct ccu_data *ccu)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < ccu->data.clk_num; i++)
|
||||
kona_clk_teardown(ccu->data.clks[i]);
|
||||
kfree(ccu->data.clks);
|
||||
for (i = 0; i < ccu->clk_data.clk_num; i++)
|
||||
kona_clk_teardown(ccu->clk_data.clks[i]);
|
||||
kfree(ccu->clk_data.clks);
|
||||
}
|
||||
|
||||
static void kona_ccu_teardown(struct ccu_data *ccu)
|
||||
{
|
||||
if (!ccu)
|
||||
return;
|
||||
|
||||
kfree(ccu->clk_data.clks);
|
||||
ccu->clk_data.clks = NULL;
|
||||
if (!ccu->base)
|
||||
goto done;
|
||||
return;
|
||||
|
||||
of_clk_del_provider(ccu->node); /* safe if never added */
|
||||
ccu_clks_teardown(ccu);
|
||||
list_del(&ccu->links);
|
||||
of_node_put(ccu->node);
|
||||
ccu->node = NULL;
|
||||
iounmap(ccu->base);
|
||||
done:
|
||||
kfree(ccu->name);
|
||||
kfree(ccu);
|
||||
ccu->base = NULL;
|
||||
}
|
||||
|
||||
static bool ccu_data_valid(struct ccu_data *ccu)
|
||||
{
|
||||
struct ccu_policy *ccu_policy;
|
||||
|
||||
if (!ccu_data_offsets_valid(ccu))
|
||||
return false;
|
||||
|
||||
ccu_policy = &ccu->policy;
|
||||
if (ccu_policy_exists(ccu_policy))
|
||||
if (!ccu_policy_valid(ccu_policy, ccu->name))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a CCU. Call the provided ccu_clks_setup callback to
|
||||
* initialize the array of clocks provided by the CCU.
|
||||
*/
|
||||
void __init kona_dt_ccu_setup(struct device_node *node,
|
||||
int (*ccu_clks_setup)(struct ccu_data *))
|
||||
void __init kona_dt_ccu_setup(struct ccu_data *ccu,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct ccu_data *ccu;
|
||||
struct resource res = { 0 };
|
||||
resource_size_t range;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
ccu = kzalloc(sizeof(*ccu), GFP_KERNEL);
|
||||
if (ccu)
|
||||
ccu->name = kstrdup(node->name, GFP_KERNEL);
|
||||
if (!ccu || !ccu->name) {
|
||||
pr_err("%s: unable to allocate CCU struct for %s\n",
|
||||
__func__, node->name);
|
||||
kfree(ccu);
|
||||
if (ccu->clk_data.clk_num) {
|
||||
size_t size;
|
||||
|
||||
return;
|
||||
size = ccu->clk_data.clk_num * sizeof(*ccu->clk_data.clks);
|
||||
ccu->clk_data.clks = kzalloc(size, GFP_KERNEL);
|
||||
if (!ccu->clk_data.clks) {
|
||||
pr_err("%s: unable to allocate %u clocks for %s\n",
|
||||
__func__, ccu->clk_data.clk_num, node->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(node, 0, &res);
|
||||
@ -736,24 +834,33 @@ void __init kona_dt_ccu_setup(struct device_node *node,
|
||||
}
|
||||
|
||||
ccu->range = (u32)range;
|
||||
|
||||
if (!ccu_data_valid(ccu)) {
|
||||
pr_err("%s: ccu data not valid for %s\n", __func__, node->name);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ccu->base = ioremap(res.start, ccu->range);
|
||||
if (!ccu->base) {
|
||||
pr_err("%s: unable to map CCU registers for %s\n", __func__,
|
||||
node->name);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
spin_lock_init(&ccu->lock);
|
||||
INIT_LIST_HEAD(&ccu->links);
|
||||
ccu->node = of_node_get(node);
|
||||
|
||||
list_add_tail(&ccu->links, &ccu_list);
|
||||
|
||||
/* Set up clocks array (in ccu->data) */
|
||||
if (ccu_clks_setup(ccu))
|
||||
goto out_err;
|
||||
/*
|
||||
* Set up each defined kona clock and save the result in
|
||||
* the clock framework clock array (in ccu->data). Then
|
||||
* register as a provider for these clocks.
|
||||
*/
|
||||
for (i = 0; i < ccu->clk_data.clk_num; i++) {
|
||||
if (!ccu->kona_clks[i].ccu)
|
||||
continue;
|
||||
ccu->clk_data.clks[i] = kona_clk_setup(&ccu->kona_clks[i]);
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->data);
|
||||
ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->clk_data);
|
||||
if (ret) {
|
||||
pr_err("%s: error adding ccu %s as provider (%d)\n", __func__,
|
||||
node->name, ret);
|
||||
|
@ -16,6 +16,14 @@
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
||||
/*
|
||||
* "Policies" affect the frequencies of bus clocks provided by a
|
||||
* CCU. (I believe these polices are named "Deep Sleep", "Economy",
|
||||
* "Normal", and "Turbo".) A lower policy number has lower power
|
||||
* consumption, and policy 2 is the default.
|
||||
*/
|
||||
#define CCU_POLICY_COUNT 4
|
||||
|
||||
#define CCU_ACCESS_PASSWORD 0xA5A500
|
||||
#define CLK_GATE_DELAY_LOOP 2000
|
||||
|
||||
@ -207,9 +215,154 @@ __ccu_wait_bit(struct ccu_data *ccu, u32 reg_offset, u32 bit, bool want)
|
||||
return true;
|
||||
udelay(1);
|
||||
}
|
||||
pr_warn("%s: %s/0x%04x bit %u was never %s\n", __func__,
|
||||
ccu->name, reg_offset, bit, want ? "set" : "clear");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Policy operations */
|
||||
|
||||
static bool __ccu_policy_engine_start(struct ccu_data *ccu, bool sync)
|
||||
{
|
||||
struct bcm_policy_ctl *control = &ccu->policy.control;
|
||||
u32 offset;
|
||||
u32 go_bit;
|
||||
u32 mask;
|
||||
bool ret;
|
||||
|
||||
/* If we don't need to control policy for this CCU, we're done. */
|
||||
if (!policy_ctl_exists(control))
|
||||
return true;
|
||||
|
||||
offset = control->offset;
|
||||
go_bit = control->go_bit;
|
||||
|
||||
/* Ensure we're not busy before we start */
|
||||
ret = __ccu_wait_bit(ccu, offset, go_bit, false);
|
||||
if (!ret) {
|
||||
pr_err("%s: ccu %s policy engine wouldn't go idle\n",
|
||||
__func__, ccu->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's a synchronous request, we'll wait for the voltage
|
||||
* and frequency of the active load to stabilize before
|
||||
* returning. To do this we select the active load by
|
||||
* setting the ATL bit.
|
||||
*
|
||||
* An asynchronous request instead ramps the voltage in the
|
||||
* background, and when that process stabilizes, the target
|
||||
* load is copied to the active load and the CCU frequency
|
||||
* is switched. We do this by selecting the target load
|
||||
* (ATL bit clear) and setting the request auto-copy (AC bit
|
||||
* set).
|
||||
*
|
||||
* Note, we do NOT read-modify-write this register.
|
||||
*/
|
||||
mask = (u32)1 << go_bit;
|
||||
if (sync)
|
||||
mask |= 1 << control->atl_bit;
|
||||
else
|
||||
mask |= 1 << control->ac_bit;
|
||||
__ccu_write(ccu, offset, mask);
|
||||
|
||||
/* Wait for indication that operation is complete. */
|
||||
ret = __ccu_wait_bit(ccu, offset, go_bit, false);
|
||||
if (!ret)
|
||||
pr_err("%s: ccu %s policy engine never started\n",
|
||||
__func__, ccu->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool __ccu_policy_engine_stop(struct ccu_data *ccu)
|
||||
{
|
||||
struct bcm_lvm_en *enable = &ccu->policy.enable;
|
||||
u32 offset;
|
||||
u32 enable_bit;
|
||||
bool ret;
|
||||
|
||||
/* If we don't need to control policy for this CCU, we're done. */
|
||||
if (!policy_lvm_en_exists(enable))
|
||||
return true;
|
||||
|
||||
/* Ensure we're not busy before we start */
|
||||
offset = enable->offset;
|
||||
enable_bit = enable->bit;
|
||||
ret = __ccu_wait_bit(ccu, offset, enable_bit, false);
|
||||
if (!ret) {
|
||||
pr_err("%s: ccu %s policy engine already stopped\n",
|
||||
__func__, ccu->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now set the bit to stop the engine (NO read-modify-write) */
|
||||
__ccu_write(ccu, offset, (u32)1 << enable_bit);
|
||||
|
||||
/* Wait for indication that it has stopped. */
|
||||
ret = __ccu_wait_bit(ccu, offset, enable_bit, false);
|
||||
if (!ret)
|
||||
pr_err("%s: ccu %s policy engine never stopped\n",
|
||||
__func__, ccu->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* A CCU has four operating conditions ("policies"), and some clocks
|
||||
* can be disabled or enabled based on which policy is currently in
|
||||
* effect. Such clocks have a bit in a "policy mask" register for
|
||||
* each policy indicating whether the clock is enabled for that
|
||||
* policy or not. The bit position for a clock is the same for all
|
||||
* four registers, and the 32-bit registers are at consecutive
|
||||
* addresses.
|
||||
*/
|
||||
static bool policy_init(struct ccu_data *ccu, struct bcm_clk_policy *policy)
|
||||
{
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
int i;
|
||||
bool ret;
|
||||
|
||||
if (!policy_exists(policy))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* We need to stop the CCU policy engine to allow update
|
||||
* of our policy bits.
|
||||
*/
|
||||
if (!__ccu_policy_engine_stop(ccu)) {
|
||||
pr_err("%s: unable to stop CCU %s policy engine\n",
|
||||
__func__, ccu->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* For now, if a clock defines its policy bit we just mark
|
||||
* it "enabled" for all four policies.
|
||||
*/
|
||||
offset = policy->offset;
|
||||
mask = (u32)1 << policy->bit;
|
||||
for (i = 0; i < CCU_POLICY_COUNT; i++) {
|
||||
u32 reg_val;
|
||||
|
||||
reg_val = __ccu_read(ccu, offset);
|
||||
reg_val |= mask;
|
||||
__ccu_write(ccu, offset, reg_val);
|
||||
offset += sizeof(u32);
|
||||
}
|
||||
|
||||
/* We're done updating; fire up the policy engine again. */
|
||||
ret = __ccu_policy_engine_start(ccu, true);
|
||||
if (!ret)
|
||||
pr_err("%s: unable to restart CCU %s policy engine\n",
|
||||
__func__, ccu->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Gate operations */
|
||||
|
||||
/* Determine whether a clock is gated. CCU lock must be held. */
|
||||
@ -374,6 +527,35 @@ static int clk_gate(struct ccu_data *ccu, const char *name,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Hysteresis operations */
|
||||
|
||||
/*
|
||||
* If a clock gate requires a turn-off delay it will have
|
||||
* "hysteresis" register bits defined. The first, if set, enables
|
||||
* the delay; and if enabled, the second bit determines whether the
|
||||
* delay is "low" or "high" (1 means high). For now, if it's
|
||||
* defined for a clock, we set it.
|
||||
*/
|
||||
static bool hyst_init(struct ccu_data *ccu, struct bcm_clk_hyst *hyst)
|
||||
{
|
||||
u32 offset;
|
||||
u32 reg_val;
|
||||
u32 mask;
|
||||
|
||||
if (!hyst_exists(hyst))
|
||||
return true;
|
||||
|
||||
offset = hyst->offset;
|
||||
mask = (u32)1 << hyst->en_bit;
|
||||
mask |= (u32)1 << hyst->val_bit;
|
||||
|
||||
reg_val = __ccu_read(ccu, offset);
|
||||
reg_val |= mask;
|
||||
__ccu_write(ccu, offset, reg_val);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Trigger operations */
|
||||
|
||||
/*
|
||||
@ -806,7 +988,7 @@ static int kona_peri_clk_enable(struct clk_hw *hw)
|
||||
struct kona_clk *bcm_clk = to_kona_clk(hw);
|
||||
struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate;
|
||||
|
||||
return clk_gate(bcm_clk->ccu, bcm_clk->name, gate, true);
|
||||
return clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, true);
|
||||
}
|
||||
|
||||
static void kona_peri_clk_disable(struct clk_hw *hw)
|
||||
@ -814,7 +996,7 @@ static void kona_peri_clk_disable(struct clk_hw *hw)
|
||||
struct kona_clk *bcm_clk = to_kona_clk(hw);
|
||||
struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate;
|
||||
|
||||
(void)clk_gate(bcm_clk->ccu, bcm_clk->name, gate, false);
|
||||
(void)clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, false);
|
||||
}
|
||||
|
||||
static int kona_peri_clk_is_enabled(struct clk_hw *hw)
|
||||
@ -872,12 +1054,13 @@ static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index)
|
||||
|
||||
ret = selector_write(bcm_clk->ccu, &data->gate, sel, trig, index);
|
||||
if (ret == -ENXIO) {
|
||||
pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name);
|
||||
pr_err("%s: gating failure for %s\n", __func__,
|
||||
bcm_clk->init_data.name);
|
||||
ret = -EIO; /* Don't proliferate weird errors */
|
||||
} else if (ret == -EIO) {
|
||||
pr_err("%s: %strigger failed for %s\n", __func__,
|
||||
trig == &data->pre_trig ? "pre-" : "",
|
||||
bcm_clk->name);
|
||||
bcm_clk->init_data.name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -936,10 +1119,12 @@ static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
ret = divider_write(bcm_clk->ccu, &data->gate, &data->div,
|
||||
&data->trig, scaled_div);
|
||||
if (ret == -ENXIO) {
|
||||
pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name);
|
||||
pr_err("%s: gating failure for %s\n", __func__,
|
||||
bcm_clk->init_data.name);
|
||||
ret = -EIO; /* Don't proliferate weird errors */
|
||||
} else if (ret == -EIO) {
|
||||
pr_err("%s: trigger failed for %s\n", __func__, bcm_clk->name);
|
||||
pr_err("%s: trigger failed for %s\n", __func__,
|
||||
bcm_clk->init_data.name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -961,15 +1146,24 @@ static bool __peri_clk_init(struct kona_clk *bcm_clk)
|
||||
{
|
||||
struct ccu_data *ccu = bcm_clk->ccu;
|
||||
struct peri_clk_data *peri = bcm_clk->u.peri;
|
||||
const char *name = bcm_clk->name;
|
||||
const char *name = bcm_clk->init_data.name;
|
||||
struct bcm_clk_trig *trig;
|
||||
|
||||
BUG_ON(bcm_clk->type != bcm_clk_peri);
|
||||
|
||||
if (!policy_init(ccu, &peri->policy)) {
|
||||
pr_err("%s: error initializing policy for %s\n",
|
||||
__func__, name);
|
||||
return false;
|
||||
}
|
||||
if (!gate_init(ccu, &peri->gate)) {
|
||||
pr_err("%s: error initializing gate for %s\n", __func__, name);
|
||||
return false;
|
||||
}
|
||||
if (!hyst_init(ccu, &peri->hyst)) {
|
||||
pr_err("%s: error initializing hyst for %s\n", __func__, name);
|
||||
return false;
|
||||
}
|
||||
if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) {
|
||||
pr_err("%s: error initializing divider for %s\n", __func__,
|
||||
name);
|
||||
@ -1014,13 +1208,13 @@ bool __init kona_ccu_init(struct ccu_data *ccu)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int which;
|
||||
struct clk **clks = ccu->data.clks;
|
||||
struct clk **clks = ccu->clk_data.clks;
|
||||
bool success = true;
|
||||
|
||||
flags = ccu_lock(ccu);
|
||||
__ccu_write_enable(ccu);
|
||||
|
||||
for (which = 0; which < ccu->data.clk_num; which++) {
|
||||
for (which = 0; which < ccu->clk_data.clk_num; which++) {
|
||||
struct kona_clk *bcm_clk;
|
||||
|
||||
if (!clks[which])
|
||||
|
@ -43,8 +43,14 @@
|
||||
#define FLAG_FLIP(obj, type, flag) ((obj)->flags ^= FLAG(type, flag))
|
||||
#define FLAG_TEST(obj, type, flag) (!!((obj)->flags & FLAG(type, flag)))
|
||||
|
||||
/* CCU field state tests */
|
||||
|
||||
#define ccu_policy_exists(ccu_policy) ((ccu_policy)->enable.offset != 0)
|
||||
|
||||
/* Clock field state tests */
|
||||
|
||||
#define policy_exists(policy) ((policy)->offset != 0)
|
||||
|
||||
#define gate_exists(gate) FLAG_TEST(gate, GATE, EXISTS)
|
||||
#define gate_is_enabled(gate) FLAG_TEST(gate, GATE, ENABLED)
|
||||
#define gate_is_hw_controllable(gate) FLAG_TEST(gate, GATE, HW)
|
||||
@ -54,6 +60,8 @@
|
||||
|
||||
#define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED)
|
||||
|
||||
#define hyst_exists(hyst) ((hyst)->offset != 0)
|
||||
|
||||
#define divider_exists(div) FLAG_TEST(div, DIV, EXISTS)
|
||||
#define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED)
|
||||
#define divider_has_fraction(div) (!divider_is_fixed(div) && \
|
||||
@ -62,6 +70,9 @@
|
||||
#define selector_exists(sel) ((sel)->width != 0)
|
||||
#define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS)
|
||||
|
||||
#define policy_lvm_en_exists(enable) ((enable)->offset != 0)
|
||||
#define policy_ctl_exists(control) ((control)->offset != 0)
|
||||
|
||||
/* Clock type, used to tell common block what it's part of */
|
||||
enum bcm_clk_type {
|
||||
bcm_clk_none, /* undefined clock type */
|
||||
@ -71,25 +82,26 @@ enum bcm_clk_type {
|
||||
};
|
||||
|
||||
/*
|
||||
* Each CCU defines a mapped area of memory containing registers
|
||||
* used to manage clocks implemented by the CCU. Access to memory
|
||||
* within the CCU's space is serialized by a spinlock. Before any
|
||||
* (other) address can be written, a special access "password" value
|
||||
* must be written to its WR_ACCESS register (located at the base
|
||||
* address of the range). We keep track of the name of each CCU as
|
||||
* it is set up, and maintain them in a list.
|
||||
* CCU policy control for clocks. Clocks can be enabled or disabled
|
||||
* based on the CCU policy in effect. One bit in each policy mask
|
||||
* register (one per CCU policy) represents whether the clock is
|
||||
* enabled when that policy is effect or not. The CCU policy engine
|
||||
* must be stopped to update these bits, and must be restarted again
|
||||
* afterward.
|
||||
*/
|
||||
struct ccu_data {
|
||||
void __iomem *base; /* base of mapped address space */
|
||||
spinlock_t lock; /* serialization lock */
|
||||
bool write_enabled; /* write access is currently enabled */
|
||||
struct list_head links; /* for ccu_list */
|
||||
struct device_node *node;
|
||||
struct clk_onecell_data data;
|
||||
const char *name;
|
||||
u32 range; /* byte range of address space */
|
||||
struct bcm_clk_policy {
|
||||
u32 offset; /* first policy mask register offset */
|
||||
u32 bit; /* bit used in all mask registers */
|
||||
};
|
||||
|
||||
/* Policy initialization macro */
|
||||
|
||||
#define POLICY(_offset, _bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.bit = (_bit), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Gating control and status is managed by a 32-bit gate register.
|
||||
*
|
||||
@ -195,6 +207,22 @@ struct bcm_clk_gate {
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \
|
||||
}
|
||||
|
||||
/* Gate hysteresis for clocks */
|
||||
struct bcm_clk_hyst {
|
||||
u32 offset; /* hyst register offset (normally CLKGATE) */
|
||||
u32 en_bit; /* bit used to enable hysteresis */
|
||||
u32 val_bit; /* if enabled: 0 = low delay; 1 = high delay */
|
||||
};
|
||||
|
||||
/* Hysteresis initialization macro */
|
||||
|
||||
#define HYST(_offset, _en_bit, _val_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.en_bit = (_en_bit), \
|
||||
.val_bit = (_val_bit), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Each clock can have zero, one, or two dividers which change the
|
||||
* output rate of the clock. Each divider can be either fixed or
|
||||
@ -360,7 +388,9 @@ struct bcm_clk_trig {
|
||||
}
|
||||
|
||||
struct peri_clk_data {
|
||||
struct bcm_clk_policy policy;
|
||||
struct bcm_clk_gate gate;
|
||||
struct bcm_clk_hyst hyst;
|
||||
struct bcm_clk_trig pre_trig;
|
||||
struct bcm_clk_div pre_div;
|
||||
struct bcm_clk_trig trig;
|
||||
@ -373,8 +403,7 @@ struct peri_clk_data {
|
||||
|
||||
struct kona_clk {
|
||||
struct clk_hw hw;
|
||||
struct clk_init_data init_data;
|
||||
const char *name; /* name of this clock */
|
||||
struct clk_init_data init_data; /* includes name of this clock */
|
||||
struct ccu_data *ccu; /* ccu this clock is associated with */
|
||||
enum bcm_clk_type type;
|
||||
union {
|
||||
@ -385,15 +414,93 @@ struct kona_clk {
|
||||
#define to_kona_clk(_hw) \
|
||||
container_of(_hw, struct kona_clk, hw)
|
||||
|
||||
/* Initialization macro for an entry in a CCU's kona_clks[] array. */
|
||||
#define KONA_CLK(_ccu_name, _clk_name, _type) \
|
||||
{ \
|
||||
.init_data = { \
|
||||
.name = #_clk_name, \
|
||||
.ops = &kona_ ## _type ## _clk_ops, \
|
||||
}, \
|
||||
.ccu = &_ccu_name ## _ccu_data, \
|
||||
.type = bcm_clk_ ## _type, \
|
||||
.u.data = &_clk_name ## _data, \
|
||||
}
|
||||
#define LAST_KONA_CLK { .type = bcm_clk_none }
|
||||
|
||||
/*
|
||||
* CCU policy control. To enable software update of the policy
|
||||
* tables the CCU policy engine must be stopped by setting the
|
||||
* software update enable bit (LVM_EN). After an update the engine
|
||||
* is restarted using the GO bit and either the GO_ATL or GO_AC bit.
|
||||
*/
|
||||
struct bcm_lvm_en {
|
||||
u32 offset; /* LVM_EN register offset */
|
||||
u32 bit; /* POLICY_CONFIG_EN bit in register */
|
||||
};
|
||||
|
||||
/* Policy enable initialization macro */
|
||||
#define CCU_LVM_EN(_offset, _bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.bit = (_bit), \
|
||||
}
|
||||
|
||||
struct bcm_policy_ctl {
|
||||
u32 offset; /* POLICY_CTL register offset */
|
||||
u32 go_bit;
|
||||
u32 atl_bit; /* GO, GO_ATL, and GO_AC bits */
|
||||
u32 ac_bit;
|
||||
};
|
||||
|
||||
/* Policy control initialization macro */
|
||||
#define CCU_POLICY_CTL(_offset, _go_bit, _ac_bit, _atl_bit) \
|
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.go_bit = (_go_bit), \
|
||||
.ac_bit = (_ac_bit), \
|
||||
.atl_bit = (_atl_bit), \
|
||||
}
|
||||
|
||||
struct ccu_policy {
|
||||
struct bcm_lvm_en enable;
|
||||
struct bcm_policy_ctl control;
|
||||
};
|
||||
|
||||
/*
|
||||
* Each CCU defines a mapped area of memory containing registers
|
||||
* used to manage clocks implemented by the CCU. Access to memory
|
||||
* within the CCU's space is serialized by a spinlock. Before any
|
||||
* (other) address can be written, a special access "password" value
|
||||
* must be written to its WR_ACCESS register (located at the base
|
||||
* address of the range). We keep track of the name of each CCU as
|
||||
* it is set up, and maintain them in a list.
|
||||
*/
|
||||
struct ccu_data {
|
||||
void __iomem *base; /* base of mapped address space */
|
||||
spinlock_t lock; /* serialization lock */
|
||||
bool write_enabled; /* write access is currently enabled */
|
||||
struct ccu_policy policy;
|
||||
struct list_head links; /* for ccu_list */
|
||||
struct device_node *node;
|
||||
struct clk_onecell_data clk_data;
|
||||
const char *name;
|
||||
u32 range; /* byte range of address space */
|
||||
struct kona_clk kona_clks[]; /* must be last */
|
||||
};
|
||||
|
||||
/* Initialization for common fields in a Kona ccu_data structure */
|
||||
#define KONA_CCU_COMMON(_prefix, _name, _ccuname) \
|
||||
.name = #_name "_ccu", \
|
||||
.lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \
|
||||
.links = LIST_HEAD_INIT(_name ## _ccu_data.links), \
|
||||
.clk_data = { \
|
||||
.clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \
|
||||
}
|
||||
|
||||
/* Exported globals */
|
||||
|
||||
extern struct clk_ops kona_peri_clk_ops;
|
||||
|
||||
/* Help functions */
|
||||
|
||||
#define PERI_CLK_SETUP(clks, ccu, id, name) \
|
||||
clks[id] = kona_clk_setup(ccu, #name, bcm_clk_peri, &name ## _data)
|
||||
|
||||
/* Externally visible functions */
|
||||
|
||||
extern u64 do_div_round_closest(u64 dividend, unsigned long divisor);
|
||||
@ -401,10 +508,9 @@ extern u64 scaled_div_max(struct bcm_clk_div *div);
|
||||
extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value,
|
||||
u32 billionths);
|
||||
|
||||
extern struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
|
||||
enum bcm_clk_type type, void *data);
|
||||
extern void __init kona_dt_ccu_setup(struct device_node *node,
|
||||
int (*ccu_clks_setup)(struct ccu_data *));
|
||||
extern struct clk *kona_clk_setup(struct kona_clk *bcm_clk);
|
||||
extern void __init kona_dt_ccu_setup(struct ccu_data *ccu,
|
||||
struct device_node *node);
|
||||
extern bool __init kona_ccu_init(struct ccu_data *ccu);
|
||||
|
||||
#endif /* _CLK_KONA_H */
|
||||
|
615
drivers/clk/clk-axm5516.c
Normal file
615
drivers/clk/clk-axm5516.c
Normal file
@ -0,0 +1,615 @@
|
||||
/*
|
||||
* drivers/clk/clk-axm5516.c
|
||||
*
|
||||
* Provides clock implementations for three different types of clock devices on
|
||||
* the Axxia device: PLL clock, a clock divider and a clock mux.
|
||||
*
|
||||
* Copyright (C) 2014 LSI Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <dt-bindings/clock/lsi,axm5516-clks.h>
|
||||
|
||||
|
||||
/**
|
||||
* struct axxia_clk - Common struct to all Axxia clocks.
|
||||
* @hw: clk_hw for the common clk framework
|
||||
* @regmap: Regmap for the clock control registers
|
||||
*/
|
||||
struct axxia_clk {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
#define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw)
|
||||
|
||||
/**
|
||||
* struct axxia_pllclk - Axxia PLL generated clock.
|
||||
* @aclk: Common struct
|
||||
* @reg: Offset into regmap for PLL control register
|
||||
*/
|
||||
struct axxia_pllclk {
|
||||
struct axxia_clk aclk;
|
||||
u32 reg;
|
||||
};
|
||||
#define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk)
|
||||
|
||||
/**
|
||||
* axxia_pllclk_recalc - Calculate the PLL generated clock rate given the
|
||||
* parent clock rate.
|
||||
*/
|
||||
static unsigned long
|
||||
axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate)
|
||||
{
|
||||
struct axxia_clk *aclk = to_axxia_clk(hw);
|
||||
struct axxia_pllclk *pll = to_axxia_pllclk(aclk);
|
||||
unsigned long rate, fbdiv, refdiv, postdiv;
|
||||
u32 control;
|
||||
|
||||
regmap_read(aclk->regmap, pll->reg, &control);
|
||||
postdiv = ((control >> 0) & 0xf) + 1;
|
||||
fbdiv = ((control >> 4) & 0xfff) + 3;
|
||||
refdiv = ((control >> 16) & 0x1f) + 1;
|
||||
rate = (parent_rate / (refdiv * postdiv)) * fbdiv;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static const struct clk_ops axxia_pllclk_ops = {
|
||||
.recalc_rate = axxia_pllclk_recalc,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct axxia_divclk - Axxia clock divider
|
||||
* @aclk: Common struct
|
||||
* @reg: Offset into regmap for PLL control register
|
||||
* @shift: Bit position for divider value
|
||||
* @width: Number of bits in divider value
|
||||
*/
|
||||
struct axxia_divclk {
|
||||
struct axxia_clk aclk;
|
||||
u32 reg;
|
||||
u32 shift;
|
||||
u32 width;
|
||||
};
|
||||
#define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk)
|
||||
|
||||
/**
|
||||
* axxia_divclk_recalc_rate - Calculate clock divider output rage
|
||||
*/
|
||||
static unsigned long
|
||||
axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
{
|
||||
struct axxia_clk *aclk = to_axxia_clk(hw);
|
||||
struct axxia_divclk *divclk = to_axxia_divclk(aclk);
|
||||
u32 ctrl, div;
|
||||
|
||||
regmap_read(aclk->regmap, divclk->reg, &ctrl);
|
||||
div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1));
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static const struct clk_ops axxia_divclk_ops = {
|
||||
.recalc_rate = axxia_divclk_recalc_rate,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct axxia_clkmux - Axxia clock mux
|
||||
* @aclk: Common struct
|
||||
* @reg: Offset into regmap for PLL control register
|
||||
* @shift: Bit position for selection value
|
||||
* @width: Number of bits in selection value
|
||||
*/
|
||||
struct axxia_clkmux {
|
||||
struct axxia_clk aclk;
|
||||
u32 reg;
|
||||
u32 shift;
|
||||
u32 width;
|
||||
};
|
||||
#define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk)
|
||||
|
||||
/**
|
||||
* axxia_clkmux_get_parent - Return the index of selected parent clock
|
||||
*/
|
||||
static u8 axxia_clkmux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct axxia_clk *aclk = to_axxia_clk(hw);
|
||||
struct axxia_clkmux *mux = to_axxia_clkmux(aclk);
|
||||
u32 ctrl, parent;
|
||||
|
||||
regmap_read(aclk->regmap, mux->reg, &ctrl);
|
||||
parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1);
|
||||
|
||||
return (u8) parent;
|
||||
}
|
||||
|
||||
static const struct clk_ops axxia_clkmux_ops = {
|
||||
.get_parent = axxia_clkmux_get_parent,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* PLLs
|
||||
*/
|
||||
|
||||
static struct axxia_pllclk clk_fab_pll = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_fab_pll",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_pllclk_ops,
|
||||
},
|
||||
.reg = 0x01800,
|
||||
};
|
||||
|
||||
static struct axxia_pllclk clk_cpu_pll = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu_pll",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_pllclk_ops,
|
||||
},
|
||||
.reg = 0x02000,
|
||||
};
|
||||
|
||||
static struct axxia_pllclk clk_sys_pll = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_sys_pll",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_pllclk_ops,
|
||||
},
|
||||
.reg = 0x02800,
|
||||
};
|
||||
|
||||
static struct axxia_pllclk clk_sm0_pll = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_sm0_pll",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref2"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_pllclk_ops,
|
||||
},
|
||||
.reg = 0x03000,
|
||||
};
|
||||
|
||||
static struct axxia_pllclk clk_sm1_pll = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_sm1_pll",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref1"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_pllclk_ops,
|
||||
},
|
||||
.reg = 0x03800,
|
||||
};
|
||||
|
||||
/*
|
||||
* Clock dividers
|
||||
*/
|
||||
|
||||
static struct axxia_divclk clk_cpu0_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu0_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_cpu_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x10008,
|
||||
.shift = 0,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct axxia_divclk clk_cpu1_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu1_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_cpu_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x10008,
|
||||
.shift = 4,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct axxia_divclk clk_cpu2_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu2_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_cpu_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x10008,
|
||||
.shift = 8,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct axxia_divclk clk_cpu3_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu3_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_cpu_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x10008,
|
||||
.shift = 12,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct axxia_divclk clk_nrcp_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_nrcp_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_sys_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x1000c,
|
||||
.shift = 0,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct axxia_divclk clk_sys_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_sys_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_sys_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x1000c,
|
||||
.shift = 4,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct axxia_divclk clk_fab_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_fab_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_fab_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x1000c,
|
||||
.shift = 8,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct axxia_divclk clk_per_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_per_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_sm1_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_IS_BASIC,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x1000c,
|
||||
.shift = 12,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct axxia_divclk clk_mmc_div = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_mmc_div",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_sm1_pll"
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_IS_BASIC,
|
||||
.ops = &axxia_divclk_ops,
|
||||
},
|
||||
.reg = 0x1000c,
|
||||
.shift = 16,
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
/*
|
||||
* Clock MUXes
|
||||
*/
|
||||
|
||||
static struct axxia_clkmux clk_cpu0_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu0",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0",
|
||||
"clk_cpu_pll",
|
||||
"clk_cpu0_div",
|
||||
"clk_cpu0_div"
|
||||
},
|
||||
.num_parents = 4,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10000,
|
||||
.shift = 0,
|
||||
.width = 2,
|
||||
};
|
||||
|
||||
static struct axxia_clkmux clk_cpu1_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu1",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0",
|
||||
"clk_cpu_pll",
|
||||
"clk_cpu1_div",
|
||||
"clk_cpu1_div"
|
||||
},
|
||||
.num_parents = 4,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10000,
|
||||
.shift = 2,
|
||||
.width = 2,
|
||||
};
|
||||
|
||||
static struct axxia_clkmux clk_cpu2_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu2",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0",
|
||||
"clk_cpu_pll",
|
||||
"clk_cpu2_div",
|
||||
"clk_cpu2_div"
|
||||
},
|
||||
.num_parents = 4,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10000,
|
||||
.shift = 4,
|
||||
.width = 2,
|
||||
};
|
||||
|
||||
static struct axxia_clkmux clk_cpu3_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_cpu3",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0",
|
||||
"clk_cpu_pll",
|
||||
"clk_cpu3_div",
|
||||
"clk_cpu3_div"
|
||||
},
|
||||
.num_parents = 4,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10000,
|
||||
.shift = 6,
|
||||
.width = 2,
|
||||
};
|
||||
|
||||
static struct axxia_clkmux clk_nrcp_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_nrcp",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0",
|
||||
"clk_sys_pll",
|
||||
"clk_nrcp_div",
|
||||
"clk_nrcp_div"
|
||||
},
|
||||
.num_parents = 4,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10004,
|
||||
.shift = 0,
|
||||
.width = 2,
|
||||
};
|
||||
|
||||
static struct axxia_clkmux clk_sys_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_sys",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0",
|
||||
"clk_sys_pll",
|
||||
"clk_sys_div",
|
||||
"clk_sys_div"
|
||||
},
|
||||
.num_parents = 4,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10004,
|
||||
.shift = 2,
|
||||
.width = 2,
|
||||
};
|
||||
|
||||
static struct axxia_clkmux clk_fab_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_fab",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref0",
|
||||
"clk_fab_pll",
|
||||
"clk_fab_div",
|
||||
"clk_fab_div"
|
||||
},
|
||||
.num_parents = 4,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10004,
|
||||
.shift = 4,
|
||||
.width = 2,
|
||||
};
|
||||
|
||||
static struct axxia_clkmux clk_per_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_per",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref1",
|
||||
"clk_per_div"
|
||||
},
|
||||
.num_parents = 2,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10004,
|
||||
.shift = 6,
|
||||
.width = 1,
|
||||
};
|
||||
|
||||
static struct axxia_clkmux clk_mmc_mux = {
|
||||
.aclk.hw.init = &(struct clk_init_data){
|
||||
.name = "clk_mmc",
|
||||
.parent_names = (const char *[]){
|
||||
"clk_ref1",
|
||||
"clk_mmc_div"
|
||||
},
|
||||
.num_parents = 2,
|
||||
.ops = &axxia_clkmux_ops,
|
||||
},
|
||||
.reg = 0x10004,
|
||||
.shift = 9,
|
||||
.width = 1,
|
||||
};
|
||||
|
||||
/* Table of all supported clocks indexed by the clock identifiers from the
|
||||
* device tree binding
|
||||
*/
|
||||
static struct axxia_clk *axmclk_clocks[] = {
|
||||
[AXXIA_CLK_FAB_PLL] = &clk_fab_pll.aclk,
|
||||
[AXXIA_CLK_CPU_PLL] = &clk_cpu_pll.aclk,
|
||||
[AXXIA_CLK_SYS_PLL] = &clk_sys_pll.aclk,
|
||||
[AXXIA_CLK_SM0_PLL] = &clk_sm0_pll.aclk,
|
||||
[AXXIA_CLK_SM1_PLL] = &clk_sm1_pll.aclk,
|
||||
[AXXIA_CLK_FAB_DIV] = &clk_fab_div.aclk,
|
||||
[AXXIA_CLK_SYS_DIV] = &clk_sys_div.aclk,
|
||||
[AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk,
|
||||
[AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk,
|
||||
[AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk,
|
||||
[AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk,
|
||||
[AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk,
|
||||
[AXXIA_CLK_PER_DIV] = &clk_per_div.aclk,
|
||||
[AXXIA_CLK_MMC_DIV] = &clk_mmc_div.aclk,
|
||||
[AXXIA_CLK_FAB] = &clk_fab_mux.aclk,
|
||||
[AXXIA_CLK_SYS] = &clk_sys_mux.aclk,
|
||||
[AXXIA_CLK_NRCP] = &clk_nrcp_mux.aclk,
|
||||
[AXXIA_CLK_CPU0] = &clk_cpu0_mux.aclk,
|
||||
[AXXIA_CLK_CPU1] = &clk_cpu1_mux.aclk,
|
||||
[AXXIA_CLK_CPU2] = &clk_cpu2_mux.aclk,
|
||||
[AXXIA_CLK_CPU3] = &clk_cpu3_mux.aclk,
|
||||
[AXXIA_CLK_PER] = &clk_per_mux.aclk,
|
||||
[AXXIA_CLK_MMC] = &clk_mmc_mux.aclk,
|
||||
};
|
||||
|
||||
static const struct regmap_config axmclk_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = 0x1fffc,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id axmclk_match_table[] = {
|
||||
{ .compatible = "lsi,axm5516-clks" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axmclk_match_table);
|
||||
|
||||
struct axmclk_priv {
|
||||
struct clk_onecell_data onecell;
|
||||
struct clk *clks[];
|
||||
};
|
||||
|
||||
static int axmclk_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
size_t num_clks;
|
||||
struct axmclk_priv *priv;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
num_clks = ARRAY_SIZE(axmclk_clocks);
|
||||
pr_info("axmclk: supporting %u clocks\n", num_clks);
|
||||
priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks,
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->onecell.clks = priv->clks;
|
||||
priv->onecell.clk_num = num_clks;
|
||||
|
||||
/* Update each entry with the allocated regmap and register the clock
|
||||
* with the common clock framework
|
||||
*/
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
axmclk_clocks[i]->regmap = regmap;
|
||||
clk = devm_clk_register(dev, &axmclk_clocks[i]->hw);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
priv->clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(dev->of_node,
|
||||
of_clk_src_onecell_get, &priv->onecell);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int axmclk_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver axmclk_driver = {
|
||||
.probe = axmclk_probe,
|
||||
.remove = axmclk_remove,
|
||||
.driver = {
|
||||
.name = "clk-axm5516",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = axmclk_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init axmclk_init(void)
|
||||
{
|
||||
return platform_driver_register(&axmclk_driver);
|
||||
}
|
||||
core_initcall(axmclk_init);
|
||||
|
||||
static void __exit axmclk_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&axmclk_driver);
|
||||
}
|
||||
module_exit(axmclk_exit);
|
||||
|
||||
MODULE_DESCRIPTION("AXM5516 clock driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:clk-axm5516");
|
@ -43,6 +43,17 @@ static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
|
||||
return maxdiv;
|
||||
}
|
||||
|
||||
static unsigned int _get_table_mindiv(const struct clk_div_table *table)
|
||||
{
|
||||
unsigned int mindiv = UINT_MAX;
|
||||
const struct clk_div_table *clkt;
|
||||
|
||||
for (clkt = table; clkt->div; clkt++)
|
||||
if (clkt->div < mindiv)
|
||||
mindiv = clkt->div;
|
||||
return mindiv;
|
||||
}
|
||||
|
||||
static unsigned int _get_maxdiv(struct clk_divider *divider)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_ONE_BASED)
|
||||
@ -162,6 +173,24 @@ static int _round_up_table(const struct clk_div_table *table, int div)
|
||||
return up;
|
||||
}
|
||||
|
||||
static int _round_down_table(const struct clk_div_table *table, int div)
|
||||
{
|
||||
const struct clk_div_table *clkt;
|
||||
int down = _get_table_mindiv(table);
|
||||
|
||||
for (clkt = table; clkt->div; clkt++) {
|
||||
if (clkt->div == div)
|
||||
return clkt->div;
|
||||
else if (clkt->div > div)
|
||||
continue;
|
||||
|
||||
if ((div - clkt->div) < (div - down))
|
||||
down = clkt->div;
|
||||
}
|
||||
|
||||
return down;
|
||||
}
|
||||
|
||||
static int _div_round_up(struct clk_divider *divider,
|
||||
unsigned long parent_rate, unsigned long rate)
|
||||
{
|
||||
@ -175,6 +204,54 @@ static int _div_round_up(struct clk_divider *divider,
|
||||
return div;
|
||||
}
|
||||
|
||||
static int _div_round_closest(struct clk_divider *divider,
|
||||
unsigned long parent_rate, unsigned long rate)
|
||||
{
|
||||
int up, down, div;
|
||||
|
||||
up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate);
|
||||
|
||||
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) {
|
||||
up = __roundup_pow_of_two(div);
|
||||
down = __rounddown_pow_of_two(div);
|
||||
} else if (divider->table) {
|
||||
up = _round_up_table(divider->table, div);
|
||||
down = _round_down_table(divider->table, div);
|
||||
}
|
||||
|
||||
return (up - div) <= (div - down) ? up : down;
|
||||
}
|
||||
|
||||
static int _div_round(struct clk_divider *divider, unsigned long parent_rate,
|
||||
unsigned long rate)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
|
||||
return _div_round_closest(divider, parent_rate, rate);
|
||||
|
||||
return _div_round_up(divider, parent_rate, rate);
|
||||
}
|
||||
|
||||
static bool _is_best_div(struct clk_divider *divider,
|
||||
int rate, int now, int best)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
|
||||
return abs(rate - now) < abs(rate - best);
|
||||
|
||||
return now <= rate && now > best;
|
||||
}
|
||||
|
||||
static int _next_div(struct clk_divider *divider, int div)
|
||||
{
|
||||
div++;
|
||||
|
||||
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return __roundup_pow_of_two(div);
|
||||
if (divider->table)
|
||||
return _round_up_table(divider->table, div);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *best_parent_rate)
|
||||
{
|
||||
@ -190,7 +267,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
|
||||
if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
|
||||
parent_rate = *best_parent_rate;
|
||||
bestdiv = _div_round_up(divider, parent_rate, rate);
|
||||
bestdiv = _div_round(divider, parent_rate, rate);
|
||||
bestdiv = bestdiv == 0 ? 1 : bestdiv;
|
||||
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
|
||||
return bestdiv;
|
||||
@ -202,7 +279,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
*/
|
||||
maxdiv = min(ULONG_MAX / rate, maxdiv);
|
||||
|
||||
for (i = 1; i <= maxdiv; i++) {
|
||||
for (i = 1; i <= maxdiv; i = _next_div(divider, i)) {
|
||||
if (!_is_valid_div(divider, i))
|
||||
continue;
|
||||
if (rate * i == parent_rate_saved) {
|
||||
@ -217,7 +294,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
||||
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
|
||||
MULT_ROUND_UP(rate, i));
|
||||
now = DIV_ROUND_UP(parent_rate, i);
|
||||
if (now <= rate && now > best) {
|
||||
if (_is_best_div(divider, rate, now, best)) {
|
||||
bestdiv = i;
|
||||
best = now;
|
||||
*best_parent_rate = parent_rate;
|
||||
@ -284,6 +361,11 @@ const struct clk_ops clk_divider_ops = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_divider_ops);
|
||||
|
||||
const struct clk_ops clk_divider_ro_ops = {
|
||||
.recalc_rate = clk_divider_recalc_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_divider_ro_ops);
|
||||
|
||||
static struct clk *_register_divider(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
@ -309,7 +391,10 @@ static struct clk *_register_divider(struct device *dev, const char *name,
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_divider_ops;
|
||||
if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
|
||||
init.ops = &clk_divider_ro_ops;
|
||||
else
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.parent_names = (parent_name ? &parent_name: NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
@ -526,6 +526,6 @@ static struct i2c_driver si570_driver = {
|
||||
module_i2c_driver(si570_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
|
||||
MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com");
|
||||
MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>");
|
||||
MODULE_DESCRIPTION("Si570 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -106,12 +106,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 %-10lu %-11lu",
|
||||
seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu\n",
|
||||
level * 3 + 1, "",
|
||||
30 - level * 3, c->name,
|
||||
c->enable_count, c->prepare_count, clk_get_rate(c),
|
||||
clk_get_accuracy(c));
|
||||
seq_printf(s, "\n");
|
||||
}
|
||||
|
||||
static void clk_summary_show_subtree(struct seq_file *s, struct clk *c,
|
||||
@ -132,8 +131,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct clk *c;
|
||||
|
||||
seq_printf(s, " clock enable_cnt prepare_cnt rate accuracy\n");
|
||||
seq_printf(s, "---------------------------------------------------------------------------------\n");
|
||||
seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n");
|
||||
seq_puts(s, "--------------------------------------------------------------------------------\n");
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
@ -822,6 +821,9 @@ void __clk_unprepare(struct clk *clk)
|
||||
*/
|
||||
void clk_unprepare(struct clk *clk)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(clk))
|
||||
return;
|
||||
|
||||
clk_prepare_lock();
|
||||
__clk_unprepare(clk);
|
||||
clk_prepare_unlock();
|
||||
@ -883,9 +885,6 @@ static void __clk_disable(struct clk *clk)
|
||||
if (!clk)
|
||||
return;
|
||||
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
return;
|
||||
|
||||
if (WARN_ON(clk->enable_count == 0))
|
||||
return;
|
||||
|
||||
@ -914,6 +913,9 @@ void clk_disable(struct clk *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (IS_ERR_OR_NULL(clk))
|
||||
return;
|
||||
|
||||
flags = clk_enable_lock();
|
||||
__clk_disable(clk);
|
||||
clk_enable_unlock(flags);
|
||||
@ -1115,6 +1117,13 @@ long clk_get_accuracy(struct clk *clk)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_get_accuracy);
|
||||
|
||||
static unsigned long clk_recalc(struct clk *clk, unsigned long parent_rate)
|
||||
{
|
||||
if (clk->ops->recalc_rate)
|
||||
return clk->ops->recalc_rate(clk->hw, parent_rate);
|
||||
return parent_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* __clk_recalc_rates
|
||||
* @clk: first clk in the subtree
|
||||
@ -1140,10 +1149,7 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg)
|
||||
if (clk->parent)
|
||||
parent_rate = clk->parent->rate;
|
||||
|
||||
if (clk->ops->recalc_rate)
|
||||
clk->rate = clk->ops->recalc_rate(clk->hw, parent_rate);
|
||||
else
|
||||
clk->rate = parent_rate;
|
||||
clk->rate = clk_recalc(clk, parent_rate);
|
||||
|
||||
/*
|
||||
* ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE
|
||||
@ -1334,10 +1340,7 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
|
||||
unsigned long new_rate;
|
||||
int ret = NOTIFY_DONE;
|
||||
|
||||
if (clk->ops->recalc_rate)
|
||||
new_rate = clk->ops->recalc_rate(clk->hw, parent_rate);
|
||||
else
|
||||
new_rate = parent_rate;
|
||||
new_rate = clk_recalc(clk, parent_rate);
|
||||
|
||||
/* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */
|
||||
if (clk->notifier_count)
|
||||
@ -1373,10 +1376,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
|
||||
new_parent->new_child = clk;
|
||||
|
||||
hlist_for_each_entry(child, &clk->children, child_node) {
|
||||
if (child->ops->recalc_rate)
|
||||
child->new_rate = child->ops->recalc_rate(child->hw, new_rate);
|
||||
else
|
||||
child->new_rate = new_rate;
|
||||
child->new_rate = clk_recalc(child, new_rate);
|
||||
clk_calc_subtree(child, child->new_rate, NULL, 0);
|
||||
}
|
||||
}
|
||||
@ -1524,10 +1524,7 @@ static void clk_change_rate(struct clk *clk)
|
||||
if (!skip_set_rate && clk->ops->set_rate)
|
||||
clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
|
||||
|
||||
if (clk->ops->recalc_rate)
|
||||
clk->rate = clk->ops->recalc_rate(clk->hw, best_parent_rate);
|
||||
else
|
||||
clk->rate = best_parent_rate;
|
||||
clk->rate = clk_recalc(clk, best_parent_rate);
|
||||
|
||||
if (clk->notifier_count && old_rate != clk->rate)
|
||||
__clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
|
||||
@ -1716,9 +1713,6 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
|
||||
if (!clk)
|
||||
return 0;
|
||||
|
||||
if (!clk->ops)
|
||||
return -EINVAL;
|
||||
|
||||
/* verify ops for for multi-parent clks */
|
||||
if ((clk->num_parents > 1) && (!clk->ops->set_parent))
|
||||
return -ENOSYS;
|
||||
|
@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
|
||||
struct clk *of_clk_get_by_clkspec(struct of_phandle_args *clkspec);
|
||||
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec);
|
||||
void of_clk_lock(void);
|
||||
void of_clk_unlock(void);
|
||||
|
@ -27,6 +27,32 @@ static LIST_HEAD(clocks);
|
||||
static DEFINE_MUTEX(clocks_mutex);
|
||||
|
||||
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
|
||||
|
||||
/**
|
||||
* of_clk_get_by_clkspec() - Lookup a clock form a clock provider
|
||||
* @clkspec: pointer to a clock specifier data structure
|
||||
*
|
||||
* This function looks up a struct clk from the registered list of clock
|
||||
* providers, an input is a clock specifier data structure as returned
|
||||
* from the of_parse_phandle_with_args() function call.
|
||||
*/
|
||||
struct clk *of_clk_get_by_clkspec(struct of_phandle_args *clkspec)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
if (!clkspec)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
of_clk_lock();
|
||||
clk = __of_clk_get_from_provider(clkspec);
|
||||
|
||||
if (!IS_ERR(clk) && !__clk_get(clk))
|
||||
clk = ERR_PTR(-ENOENT);
|
||||
|
||||
of_clk_unlock();
|
||||
return clk;
|
||||
}
|
||||
|
||||
struct clk *of_clk_get(struct device_node *np, int index)
|
||||
{
|
||||
struct of_phandle_args clkspec;
|
||||
@ -41,13 +67,7 @@ struct clk *of_clk_get(struct device_node *np, int index)
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
|
||||
of_clk_lock();
|
||||
clk = __of_clk_get_from_provider(&clkspec);
|
||||
|
||||
if (!IS_ERR(clk) && !__clk_get(clk))
|
||||
clk = ERR_PTR(-ENOENT);
|
||||
|
||||
of_clk_unlock();
|
||||
clk = of_clk_get_by_clkspec(&clkspec);
|
||||
of_node_put(clkspec.np);
|
||||
return clk;
|
||||
}
|
||||
|
@ -6,3 +6,4 @@ obj-y += clk.o clkgate-separated.o
|
||||
|
||||
obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o
|
||||
obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
|
||||
obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o
|
||||
|
101
drivers/clk/hisilicon/clk-hix5hd2.c
Normal file
101
drivers/clk/hisilicon/clk-hix5hd2.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Linaro Ltd.
|
||||
* Copyright (c) 2014 Hisilicon Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/of_address.h>
|
||||
#include <dt-bindings/clock/hix5hd2-clock.h>
|
||||
#include "clk.h"
|
||||
|
||||
static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = {
|
||||
{ HIX5HD2_FIXED_1200M, "1200m", NULL, CLK_IS_ROOT, 1200000000, },
|
||||
{ HIX5HD2_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, },
|
||||
{ HIX5HD2_FIXED_48M, "48m", NULL, CLK_IS_ROOT, 48000000, },
|
||||
{ HIX5HD2_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, },
|
||||
{ HIX5HD2_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, },
|
||||
{ HIX5HD2_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, },
|
||||
{ HIX5HD2_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, },
|
||||
{ HIX5HD2_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, },
|
||||
{ HIX5HD2_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, },
|
||||
{ HIX5HD2_FIXED_40M, "40m", NULL, CLK_IS_ROOT, 40000000, },
|
||||
{ HIX5HD2_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, },
|
||||
{ HIX5HD2_FIXED_1728M, "1728m", NULL, CLK_IS_ROOT, 1728000000, },
|
||||
{ HIX5HD2_FIXED_28P8M, "28p8m", NULL, CLK_IS_ROOT, 28000000, },
|
||||
{ HIX5HD2_FIXED_432M, "432m", NULL, CLK_IS_ROOT, 432000000, },
|
||||
{ HIX5HD2_FIXED_345P6M, "345p6m", NULL, CLK_IS_ROOT, 345000000, },
|
||||
{ HIX5HD2_FIXED_288M, "288m", NULL, CLK_IS_ROOT, 288000000, },
|
||||
{ HIX5HD2_FIXED_60M, "60m", NULL, CLK_IS_ROOT, 60000000, },
|
||||
{ HIX5HD2_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, },
|
||||
{ HIX5HD2_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, },
|
||||
{ HIX5HD2_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, },
|
||||
{ HIX5HD2_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 27000000, },
|
||||
{ HIX5HD2_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000, },
|
||||
{ HIX5HD2_FIXED_375M, "375m", NULL, CLK_IS_ROOT, 375000000, },
|
||||
{ HIX5HD2_FIXED_187M, "187m", NULL, CLK_IS_ROOT, 187000000, },
|
||||
{ HIX5HD2_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, },
|
||||
{ HIX5HD2_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, },
|
||||
{ HIX5HD2_FIXED_2P02M, "2m", NULL, CLK_IS_ROOT, 2000000, },
|
||||
{ HIX5HD2_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, },
|
||||
{ HIX5HD2_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, },
|
||||
{ HIX5HD2_FIXED_83M, "83m", NULL, CLK_IS_ROOT, 83333333, },
|
||||
};
|
||||
|
||||
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 = {
|
||||
"75m", "100m", "50m", "15m", };
|
||||
static u32 sdio1_mux_table[] = {0, 1, 2, 3};
|
||||
|
||||
static const char *fephy_mux_p[] __initconst = { "25m", "125m"};
|
||||
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_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*/
|
||||
{ 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*/
|
||||
{ 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, },
|
||||
};
|
||||
|
||||
static void __init hix5hd2_clk_init(struct device_node *np)
|
||||
{
|
||||
struct hisi_clock_data *clk_data;
|
||||
|
||||
clk_data = hisi_clk_init(np, HIX5HD2_NR_CLKS);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
hisi_clk_register_fixed_rate(hix5hd2_fixed_rate_clks,
|
||||
ARRAY_SIZE(hix5hd2_fixed_rate_clks),
|
||||
clk_data);
|
||||
hisi_clk_register_mux(hix5hd2_mux_clks, ARRAY_SIZE(hix5hd2_mux_clks),
|
||||
clk_data);
|
||||
hisi_clk_register_gate(hix5hd2_gate_clks,
|
||||
ARRAY_SIZE(hix5hd2_gate_clks), clk_data);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init);
|
@ -127,11 +127,14 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
|
||||
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);
|
||||
u32 mask = BIT(clks[i].width) - 1;
|
||||
|
||||
clk = clk_register_mux_table(NULL, clks[i].name,
|
||||
clks[i].parent_names,
|
||||
clks[i].num_parents, clks[i].flags,
|
||||
base + clks[i].offset, clks[i].shift,
|
||||
mask, clks[i].mux_flags,
|
||||
clks[i].table, &hisi_clk_lock);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock %s\n",
|
||||
__func__, clks[i].name);
|
||||
@ -174,6 +177,34 @@ void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_gate(struct hisi_gate_clock *clks,
|
||||
int nums, struct hisi_clock_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
void __iomem *base = data->base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk = clk_register_gate(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);
|
||||
|
||||
data->clk_data.clks[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
|
||||
int nums, struct hisi_clock_data *data)
|
||||
{
|
||||
|
@ -62,6 +62,7 @@ struct hisi_mux_clock {
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 mux_flags;
|
||||
u32 *table;
|
||||
const char *alias;
|
||||
};
|
||||
|
||||
@ -103,6 +104,8 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *, int,
|
||||
struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_divider(struct hisi_divider_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_gate(struct hisi_gate_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
#endif /* __HISI_CLK_H */
|
||||
|
@ -34,3 +34,7 @@ config DOVE_CLK
|
||||
config KIRKWOOD_CLK
|
||||
bool
|
||||
select MVEBU_CLK_COMMON
|
||||
|
||||
config ORION_CLK
|
||||
bool
|
||||
select MVEBU_CLK_COMMON
|
||||
|
@ -8,3 +8,4 @@ obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o
|
||||
obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
|
||||
obj-$(CONFIG_DOVE_CLK) += dove.o
|
||||
obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o
|
||||
obj-$(CONFIG_ORION_CLK) += orion.o
|
||||
|
210
drivers/clk/mvebu/orion.c
Normal file
210
drivers/clk/mvebu/orion.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Marvell Orion SoC clocks
|
||||
*
|
||||
* Copyright (C) 2014 Thomas Petazzoni
|
||||
*
|
||||
* Thomas Petazzoni <thomas.petazzoni@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/io.h>
|
||||
#include <linux/of.h>
|
||||
#include "common.h"
|
||||
|
||||
static const struct coreclk_ratio orion_coreclk_ratios[] __initconst = {
|
||||
{ .id = 0, .name = "ddrclk", }
|
||||
};
|
||||
|
||||
/*
|
||||
* Orion 5182
|
||||
*/
|
||||
|
||||
#define SAR_MV88F5182_TCLK_FREQ 8
|
||||
#define SAR_MV88F5182_TCLK_FREQ_MASK 0x3
|
||||
|
||||
static u32 __init mv88f5182_get_tclk_freq(void __iomem *sar)
|
||||
{
|
||||
u32 opt = (readl(sar) >> SAR_MV88F5182_TCLK_FREQ) &
|
||||
SAR_MV88F5182_TCLK_FREQ_MASK;
|
||||
if (opt == 1)
|
||||
return 150000000;
|
||||
else if (opt == 2)
|
||||
return 166666667;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SAR_MV88F5182_CPU_FREQ 4
|
||||
#define SAR_MV88F5182_CPU_FREQ_MASK 0xf
|
||||
|
||||
static u32 __init mv88f5182_get_cpu_freq(void __iomem *sar)
|
||||
{
|
||||
u32 opt = (readl(sar) >> SAR_MV88F5182_CPU_FREQ) &
|
||||
SAR_MV88F5182_CPU_FREQ_MASK;
|
||||
if (opt == 0)
|
||||
return 333333333;
|
||||
else if (opt == 1 || opt == 2)
|
||||
return 400000000;
|
||||
else if (opt == 3)
|
||||
return 500000000;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init mv88f5182_get_clk_ratio(void __iomem *sar, int id,
|
||||
int *mult, int *div)
|
||||
{
|
||||
u32 opt = (readl(sar) >> SAR_MV88F5182_CPU_FREQ) &
|
||||
SAR_MV88F5182_CPU_FREQ_MASK;
|
||||
if (opt == 0 || opt == 1) {
|
||||
*mult = 1;
|
||||
*div = 2;
|
||||
} else if (opt == 2 || opt == 3) {
|
||||
*mult = 1;
|
||||
*div = 3;
|
||||
} else {
|
||||
*mult = 0;
|
||||
*div = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct coreclk_soc_desc mv88f5182_coreclks = {
|
||||
.get_tclk_freq = mv88f5182_get_tclk_freq,
|
||||
.get_cpu_freq = mv88f5182_get_cpu_freq,
|
||||
.get_clk_ratio = mv88f5182_get_clk_ratio,
|
||||
.ratios = orion_coreclk_ratios,
|
||||
.num_ratios = ARRAY_SIZE(orion_coreclk_ratios),
|
||||
};
|
||||
|
||||
static void __init mv88f5182_clk_init(struct device_node *np)
|
||||
{
|
||||
return mvebu_coreclk_setup(np, &mv88f5182_coreclks);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(mv88f5182_clk, "marvell,mv88f5182-core-clock", mv88f5182_clk_init);
|
||||
|
||||
/*
|
||||
* Orion 5281
|
||||
*/
|
||||
|
||||
static u32 __init mv88f5281_get_tclk_freq(void __iomem *sar)
|
||||
{
|
||||
/* On 5281, tclk is always 166 Mhz */
|
||||
return 166666667;
|
||||
}
|
||||
|
||||
#define SAR_MV88F5281_CPU_FREQ 4
|
||||
#define SAR_MV88F5281_CPU_FREQ_MASK 0xf
|
||||
|
||||
static u32 __init mv88f5281_get_cpu_freq(void __iomem *sar)
|
||||
{
|
||||
u32 opt = (readl(sar) >> SAR_MV88F5281_CPU_FREQ) &
|
||||
SAR_MV88F5281_CPU_FREQ_MASK;
|
||||
if (opt == 1 || opt == 2)
|
||||
return 400000000;
|
||||
else if (opt == 3)
|
||||
return 500000000;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init mv88f5281_get_clk_ratio(void __iomem *sar, int id,
|
||||
int *mult, int *div)
|
||||
{
|
||||
u32 opt = (readl(sar) >> SAR_MV88F5281_CPU_FREQ) &
|
||||
SAR_MV88F5281_CPU_FREQ_MASK;
|
||||
if (opt == 1) {
|
||||
*mult = 1;
|
||||
*div = 2;
|
||||
} else if (opt == 2 || opt == 3) {
|
||||
*mult = 1;
|
||||
*div = 3;
|
||||
} else {
|
||||
*mult = 0;
|
||||
*div = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct coreclk_soc_desc mv88f5281_coreclks = {
|
||||
.get_tclk_freq = mv88f5281_get_tclk_freq,
|
||||
.get_cpu_freq = mv88f5281_get_cpu_freq,
|
||||
.get_clk_ratio = mv88f5281_get_clk_ratio,
|
||||
.ratios = orion_coreclk_ratios,
|
||||
.num_ratios = ARRAY_SIZE(orion_coreclk_ratios),
|
||||
};
|
||||
|
||||
static void __init mv88f5281_clk_init(struct device_node *np)
|
||||
{
|
||||
return mvebu_coreclk_setup(np, &mv88f5281_coreclks);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(mv88f5281_clk, "marvell,mv88f5281-core-clock", mv88f5281_clk_init);
|
||||
|
||||
/*
|
||||
* Orion 6183
|
||||
*/
|
||||
|
||||
#define SAR_MV88F6183_TCLK_FREQ 9
|
||||
#define SAR_MV88F6183_TCLK_FREQ_MASK 0x1
|
||||
|
||||
static u32 __init mv88f6183_get_tclk_freq(void __iomem *sar)
|
||||
{
|
||||
u32 opt = (readl(sar) >> SAR_MV88F6183_TCLK_FREQ) &
|
||||
SAR_MV88F6183_TCLK_FREQ_MASK;
|
||||
if (opt == 0)
|
||||
return 133333333;
|
||||
else if (opt == 1)
|
||||
return 166666667;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SAR_MV88F6183_CPU_FREQ 1
|
||||
#define SAR_MV88F6183_CPU_FREQ_MASK 0x3f
|
||||
|
||||
static u32 __init mv88f6183_get_cpu_freq(void __iomem *sar)
|
||||
{
|
||||
u32 opt = (readl(sar) >> SAR_MV88F6183_CPU_FREQ) &
|
||||
SAR_MV88F6183_CPU_FREQ_MASK;
|
||||
if (opt == 9)
|
||||
return 333333333;
|
||||
else if (opt == 17)
|
||||
return 400000000;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init mv88f6183_get_clk_ratio(void __iomem *sar, int id,
|
||||
int *mult, int *div)
|
||||
{
|
||||
u32 opt = (readl(sar) >> SAR_MV88F6183_CPU_FREQ) &
|
||||
SAR_MV88F6183_CPU_FREQ_MASK;
|
||||
if (opt == 9 || opt == 17) {
|
||||
*mult = 1;
|
||||
*div = 2;
|
||||
} else {
|
||||
*mult = 0;
|
||||
*div = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct coreclk_soc_desc mv88f6183_coreclks = {
|
||||
.get_tclk_freq = mv88f6183_get_tclk_freq,
|
||||
.get_cpu_freq = mv88f6183_get_cpu_freq,
|
||||
.get_clk_ratio = mv88f6183_get_clk_ratio,
|
||||
.ratios = orion_coreclk_ratios,
|
||||
.num_ratios = ARRAY_SIZE(orion_coreclk_ratios),
|
||||
};
|
||||
|
||||
|
||||
static void __init mv88f6183_clk_init(struct device_node *np)
|
||||
{
|
||||
return mvebu_coreclk_setup(np, &mv88f6183_coreclks);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(mv88f6183_clk, "marvell,mv88f6183-core-clock", mv88f6183_clk_init);
|
@ -13,10 +13,10 @@ config MSM_GCC_8660
|
||||
i2c, USB, SD/eMMC, etc.
|
||||
|
||||
config MSM_GCC_8960
|
||||
tristate "MSM8960 Global Clock Controller"
|
||||
tristate "APQ8064/MSM8960 Global Clock Controller"
|
||||
depends on COMMON_CLK_QCOM
|
||||
help
|
||||
Support for the global clock controller on msm8960 devices.
|
||||
Support for the global clock controller on apq8064/msm8960 devices.
|
||||
Say Y if you want to use peripheral devices such as UART, SPI,
|
||||
i2c, USB, SD/eMMC, SATA, PCIe, etc.
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
obj-$(CONFIG_COMMON_CLK_QCOM) += clk-qcom.o
|
||||
|
||||
clk-qcom-y += common.o
|
||||
clk-qcom-y += clk-regmap.o
|
||||
clk-qcom-y += clk-pll.o
|
||||
clk-qcom-y += clk-rcg.o
|
||||
|
99
drivers/clk/qcom/common.c
Normal file
99
drivers/clk/qcom/common.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2013-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/export.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "clk-regmap.h"
|
||||
#include "reset.h"
|
||||
|
||||
struct qcom_cc {
|
||||
struct qcom_reset_controller reset;
|
||||
struct clk_onecell_data data;
|
||||
struct clk *clks[];
|
||||
};
|
||||
|
||||
int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
struct clk_onecell_data *data;
|
||||
struct clk **clks;
|
||||
struct regmap *regmap;
|
||||
struct qcom_reset_controller *reset;
|
||||
struct qcom_cc *cc;
|
||||
size_t num_clks = desc->num_clks;
|
||||
struct clk_regmap **rclks = desc->clks;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, desc->config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
|
||||
GFP_KERNEL);
|
||||
if (!cc)
|
||||
return -ENOMEM;
|
||||
|
||||
clks = cc->clks;
|
||||
data = &cc->data;
|
||||
data->clks = clks;
|
||||
data->clk_num = num_clks;
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
if (!rclks[i])
|
||||
continue;
|
||||
clk = devm_clk_register_regmap(dev, rclks[i]);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reset = &cc->reset;
|
||||
reset->rcdev.of_node = dev->of_node;
|
||||
reset->rcdev.ops = &qcom_reset_ops;
|
||||
reset->rcdev.owner = dev->driver->owner;
|
||||
reset->rcdev.nr_resets = desc->num_resets;
|
||||
reset->regmap = regmap;
|
||||
reset->reset_map = desc->resets;
|
||||
platform_set_drvdata(pdev, &reset->rcdev);
|
||||
|
||||
ret = reset_controller_register(&reset->rcdev);
|
||||
if (ret)
|
||||
of_clk_del_provider(dev->of_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_cc_probe);
|
||||
|
||||
void qcom_cc_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
reset_controller_unregister(platform_get_drvdata(pdev));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_cc_remove);
|
34
drivers/clk/qcom/common.h
Normal file
34
drivers/clk/qcom/common.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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_COMMON_H__
|
||||
#define __QCOM_CLK_COMMON_H__
|
||||
|
||||
struct platform_device;
|
||||
struct regmap_config;
|
||||
struct clk_regmap;
|
||||
struct qcom_reset_map;
|
||||
|
||||
struct qcom_cc_desc {
|
||||
const struct regmap_config *config;
|
||||
struct clk_regmap **clks;
|
||||
size_t num_clks;
|
||||
const struct qcom_reset_map *resets;
|
||||
size_t num_resets;
|
||||
};
|
||||
|
||||
extern int qcom_cc_probe(struct platform_device *pdev,
|
||||
const struct qcom_cc_desc *desc);
|
||||
|
||||
extern void qcom_cc_remove(struct platform_device *pdev);
|
||||
|
||||
#endif
|
@ -25,6 +25,7 @@
|
||||
#include <dt-bindings/clock/qcom,gcc-msm8660.h>
|
||||
#include <dt-bindings/reset/qcom,gcc-msm8660.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "clk-regmap.h"
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
@ -2701,51 +2702,24 @@ static const struct regmap_config gcc_msm8660_regmap_config = {
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc gcc_msm8660_desc = {
|
||||
.config = &gcc_msm8660_regmap_config,
|
||||
.clks = gcc_msm8660_clks,
|
||||
.num_clks = ARRAY_SIZE(gcc_msm8660_clks),
|
||||
.resets = gcc_msm8660_resets,
|
||||
.num_resets = ARRAY_SIZE(gcc_msm8660_resets),
|
||||
};
|
||||
|
||||
static const struct of_device_id gcc_msm8660_match_table[] = {
|
||||
{ .compatible = "qcom,gcc-msm8660" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table);
|
||||
|
||||
struct qcom_cc {
|
||||
struct qcom_reset_controller reset;
|
||||
struct clk_onecell_data data;
|
||||
struct clk *clks[];
|
||||
};
|
||||
|
||||
static int gcc_msm8660_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
struct clk_onecell_data *data;
|
||||
struct clk **clks;
|
||||
struct regmap *regmap;
|
||||
size_t num_clks;
|
||||
struct qcom_reset_controller *reset;
|
||||
struct qcom_cc *cc;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8660_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
num_clks = ARRAY_SIZE(gcc_msm8660_clks);
|
||||
cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
|
||||
GFP_KERNEL);
|
||||
if (!cc)
|
||||
return -ENOMEM;
|
||||
|
||||
clks = cc->clks;
|
||||
data = &cc->data;
|
||||
data->clks = clks;
|
||||
data->clk_num = num_clks;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
/* Temporary until RPM clocks supported */
|
||||
clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000);
|
||||
@ -2756,39 +2730,12 @@ static int gcc_msm8660_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
if (!gcc_msm8660_clks[i])
|
||||
continue;
|
||||
clk = devm_clk_register_regmap(dev, gcc_msm8660_clks[i]);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reset = &cc->reset;
|
||||
reset->rcdev.of_node = dev->of_node;
|
||||
reset->rcdev.ops = &qcom_reset_ops,
|
||||
reset->rcdev.owner = THIS_MODULE,
|
||||
reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8660_resets),
|
||||
reset->regmap = regmap;
|
||||
reset->reset_map = gcc_msm8660_resets,
|
||||
platform_set_drvdata(pdev, &reset->rcdev);
|
||||
|
||||
ret = reset_controller_register(&reset->rcdev);
|
||||
if (ret)
|
||||
of_clk_del_provider(dev->of_node);
|
||||
|
||||
return ret;
|
||||
return qcom_cc_probe(pdev, &gcc_msm8660_desc);
|
||||
}
|
||||
|
||||
static int gcc_msm8660_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
reset_controller_unregister(platform_get_drvdata(pdev));
|
||||
qcom_cc_remove(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-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
|
||||
@ -25,6 +25,7 @@
|
||||
#include <dt-bindings/clock/qcom,gcc-msm8960.h>
|
||||
#include <dt-bindings/reset/qcom,gcc-msm8960.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "clk-regmap.h"
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
@ -2809,7 +2810,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = {
|
||||
[PPSS_PROC_RESET] = { 0x2594, 1 },
|
||||
[PPSS_RESET] = { 0x2594},
|
||||
[DMA_BAM_RESET] = { 0x25c0, 7 },
|
||||
[SIC_TIC_RESET] = { 0x2600, 7 },
|
||||
[SPS_TIC_H_RESET] = { 0x2600, 7 },
|
||||
[SLIMBUS_H_RESET] = { 0x2620, 7 },
|
||||
[SFAB_CFPB_M_RESET] = { 0x2680, 7 },
|
||||
[SFAB_CFPB_S_RESET] = { 0x26c0, 7 },
|
||||
@ -2822,7 +2823,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = {
|
||||
[SFAB_SFPB_M_RESET] = { 0x2780, 7 },
|
||||
[SFAB_SFPB_S_RESET] = { 0x27a0, 7 },
|
||||
[RPM_PROC_RESET] = { 0x27c0, 7 },
|
||||
[PMIC_SSBI2_RESET] = { 0x270c, 12 },
|
||||
[PMIC_SSBI2_RESET] = { 0x280c, 12 },
|
||||
[SDC1_RESET] = { 0x2830 },
|
||||
[SDC2_RESET] = { 0x2850 },
|
||||
[SDC3_RESET] = { 0x2870 },
|
||||
@ -2867,6 +2868,16 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = {
|
||||
[RIVA_RESET] = { 0x35e0 },
|
||||
};
|
||||
|
||||
static struct clk_regmap *gcc_apq8064_clks[] = {
|
||||
[PLL8] = &pll8.clkr,
|
||||
[PLL8_VOTE] = &pll8_vote,
|
||||
[GSBI7_UART_SRC] = &gsbi7_uart_src.clkr,
|
||||
[GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr,
|
||||
[GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr,
|
||||
[GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr,
|
||||
[GSBI7_H_CLK] = &gsbi7_h_clk.clkr,
|
||||
};
|
||||
|
||||
static const struct regmap_config gcc_msm8960_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
@ -2875,51 +2886,38 @@ static const struct regmap_config gcc_msm8960_regmap_config = {
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc gcc_msm8960_desc = {
|
||||
.config = &gcc_msm8960_regmap_config,
|
||||
.clks = gcc_msm8960_clks,
|
||||
.num_clks = ARRAY_SIZE(gcc_msm8960_clks),
|
||||
.resets = gcc_msm8960_resets,
|
||||
.num_resets = ARRAY_SIZE(gcc_msm8960_resets),
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc gcc_apq8064_desc = {
|
||||
.config = &gcc_msm8960_regmap_config,
|
||||
.clks = gcc_apq8064_clks,
|
||||
.num_clks = ARRAY_SIZE(gcc_apq8064_clks),
|
||||
.resets = gcc_msm8960_resets,
|
||||
.num_resets = ARRAY_SIZE(gcc_msm8960_resets),
|
||||
};
|
||||
|
||||
static const struct of_device_id gcc_msm8960_match_table[] = {
|
||||
{ .compatible = "qcom,gcc-msm8960" },
|
||||
{ .compatible = "qcom,gcc-msm8960", .data = &gcc_msm8960_desc },
|
||||
{ .compatible = "qcom,gcc-apq8064", .data = &gcc_apq8064_desc },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table);
|
||||
|
||||
struct qcom_cc {
|
||||
struct qcom_reset_controller reset;
|
||||
struct clk_onecell_data data;
|
||||
struct clk *clks[];
|
||||
};
|
||||
|
||||
static int gcc_msm8960_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
struct clk_onecell_data *data;
|
||||
struct clk **clks;
|
||||
struct regmap *regmap;
|
||||
size_t num_clks;
|
||||
struct qcom_reset_controller *reset;
|
||||
struct qcom_cc *cc;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *match;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8960_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
num_clks = ARRAY_SIZE(gcc_msm8960_clks);
|
||||
cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
|
||||
GFP_KERNEL);
|
||||
if (!cc)
|
||||
return -ENOMEM;
|
||||
|
||||
clks = cc->clks;
|
||||
data = &cc->data;
|
||||
data->clks = clks;
|
||||
data->clk_num = num_clks;
|
||||
match = of_match_device(gcc_msm8960_match_table, &pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
/* Temporary until RPM clocks supported */
|
||||
clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000);
|
||||
@ -2930,39 +2928,12 @@ static int gcc_msm8960_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
if (!gcc_msm8960_clks[i])
|
||||
continue;
|
||||
clk = devm_clk_register_regmap(dev, gcc_msm8960_clks[i]);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reset = &cc->reset;
|
||||
reset->rcdev.of_node = dev->of_node;
|
||||
reset->rcdev.ops = &qcom_reset_ops,
|
||||
reset->rcdev.owner = THIS_MODULE,
|
||||
reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8960_resets),
|
||||
reset->regmap = regmap;
|
||||
reset->reset_map = gcc_msm8960_resets,
|
||||
platform_set_drvdata(pdev, &reset->rcdev);
|
||||
|
||||
ret = reset_controller_register(&reset->rcdev);
|
||||
if (ret)
|
||||
of_clk_del_provider(dev->of_node);
|
||||
|
||||
return ret;
|
||||
return qcom_cc_probe(pdev, match->data);
|
||||
}
|
||||
|
||||
static int gcc_msm8960_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
reset_controller_unregister(platform_get_drvdata(pdev));
|
||||
qcom_cc_remove(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <dt-bindings/clock/qcom,gcc-msm8974.h>
|
||||
#include <dt-bindings/reset/qcom,gcc-msm8974.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "clk-regmap.h"
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
@ -2574,51 +2575,24 @@ static const struct regmap_config gcc_msm8974_regmap_config = {
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc gcc_msm8974_desc = {
|
||||
.config = &gcc_msm8974_regmap_config,
|
||||
.clks = gcc_msm8974_clocks,
|
||||
.num_clks = ARRAY_SIZE(gcc_msm8974_clocks),
|
||||
.resets = gcc_msm8974_resets,
|
||||
.num_resets = ARRAY_SIZE(gcc_msm8974_resets),
|
||||
};
|
||||
|
||||
static const struct of_device_id gcc_msm8974_match_table[] = {
|
||||
{ .compatible = "qcom,gcc-msm8974" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table);
|
||||
|
||||
struct qcom_cc {
|
||||
struct qcom_reset_controller reset;
|
||||
struct clk_onecell_data data;
|
||||
struct clk *clks[];
|
||||
};
|
||||
|
||||
static int gcc_msm8974_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
struct clk_onecell_data *data;
|
||||
struct clk **clks;
|
||||
struct regmap *regmap;
|
||||
size_t num_clks;
|
||||
struct qcom_reset_controller *reset;
|
||||
struct qcom_cc *cc;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8974_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
num_clks = ARRAY_SIZE(gcc_msm8974_clocks);
|
||||
cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
|
||||
GFP_KERNEL);
|
||||
if (!cc)
|
||||
return -ENOMEM;
|
||||
|
||||
clks = cc->clks;
|
||||
data = &cc->data;
|
||||
data->clks = clks;
|
||||
data->clk_num = num_clks;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
/* Temporary until RPM clocks supported */
|
||||
clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000);
|
||||
@ -2631,39 +2605,12 @@ static int gcc_msm8974_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
if (!gcc_msm8974_clocks[i])
|
||||
continue;
|
||||
clk = devm_clk_register_regmap(dev, gcc_msm8974_clocks[i]);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reset = &cc->reset;
|
||||
reset->rcdev.of_node = dev->of_node;
|
||||
reset->rcdev.ops = &qcom_reset_ops,
|
||||
reset->rcdev.owner = THIS_MODULE,
|
||||
reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8974_resets),
|
||||
reset->regmap = regmap;
|
||||
reset->reset_map = gcc_msm8974_resets,
|
||||
platform_set_drvdata(pdev, &reset->rcdev);
|
||||
|
||||
ret = reset_controller_register(&reset->rcdev);
|
||||
if (ret)
|
||||
of_clk_del_provider(dev->of_node);
|
||||
|
||||
return ret;
|
||||
return qcom_cc_probe(pdev, &gcc_msm8974_desc);
|
||||
}
|
||||
|
||||
static int gcc_msm8974_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
reset_controller_unregister(platform_get_drvdata(pdev));
|
||||
qcom_cc_remove(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <dt-bindings/clock/qcom,mmcc-msm8960.h>
|
||||
#include <dt-bindings/reset/qcom,mmcc-msm8960.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "clk-regmap.h"
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
@ -2222,85 +2223,28 @@ static const struct regmap_config mmcc_msm8960_regmap_config = {
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc mmcc_msm8960_desc = {
|
||||
.config = &mmcc_msm8960_regmap_config,
|
||||
.clks = mmcc_msm8960_clks,
|
||||
.num_clks = ARRAY_SIZE(mmcc_msm8960_clks),
|
||||
.resets = mmcc_msm8960_resets,
|
||||
.num_resets = ARRAY_SIZE(mmcc_msm8960_resets),
|
||||
};
|
||||
|
||||
static const struct of_device_id mmcc_msm8960_match_table[] = {
|
||||
{ .compatible = "qcom,mmcc-msm8960" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mmcc_msm8960_match_table);
|
||||
|
||||
struct qcom_cc {
|
||||
struct qcom_reset_controller reset;
|
||||
struct clk_onecell_data data;
|
||||
struct clk *clks[];
|
||||
};
|
||||
|
||||
static int mmcc_msm8960_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
struct clk_onecell_data *data;
|
||||
struct clk **clks;
|
||||
struct regmap *regmap;
|
||||
size_t num_clks;
|
||||
struct qcom_reset_controller *reset;
|
||||
struct qcom_cc *cc;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, &mmcc_msm8960_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
num_clks = ARRAY_SIZE(mmcc_msm8960_clks);
|
||||
cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
|
||||
GFP_KERNEL);
|
||||
if (!cc)
|
||||
return -ENOMEM;
|
||||
|
||||
clks = cc->clks;
|
||||
data = &cc->data;
|
||||
data->clks = clks;
|
||||
data->clk_num = num_clks;
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
if (!mmcc_msm8960_clks[i])
|
||||
continue;
|
||||
clk = devm_clk_register_regmap(dev, mmcc_msm8960_clks[i]);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reset = &cc->reset;
|
||||
reset->rcdev.of_node = dev->of_node;
|
||||
reset->rcdev.ops = &qcom_reset_ops,
|
||||
reset->rcdev.owner = THIS_MODULE,
|
||||
reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8960_resets),
|
||||
reset->regmap = regmap;
|
||||
reset->reset_map = mmcc_msm8960_resets,
|
||||
platform_set_drvdata(pdev, &reset->rcdev);
|
||||
|
||||
ret = reset_controller_register(&reset->rcdev);
|
||||
if (ret)
|
||||
of_clk_del_provider(dev->of_node);
|
||||
|
||||
return ret;
|
||||
return qcom_cc_probe(pdev, &mmcc_msm8960_desc);
|
||||
}
|
||||
|
||||
static int mmcc_msm8960_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
reset_controller_unregister(platform_get_drvdata(pdev));
|
||||
qcom_cc_remove(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <dt-bindings/clock/qcom,mmcc-msm8974.h>
|
||||
#include <dt-bindings/reset/qcom,mmcc-msm8974.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "clk-regmap.h"
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
@ -2524,88 +2525,39 @@ static const struct regmap_config mmcc_msm8974_regmap_config = {
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc mmcc_msm8974_desc = {
|
||||
.config = &mmcc_msm8974_regmap_config,
|
||||
.clks = mmcc_msm8974_clocks,
|
||||
.num_clks = ARRAY_SIZE(mmcc_msm8974_clocks),
|
||||
.resets = mmcc_msm8974_resets,
|
||||
.num_resets = ARRAY_SIZE(mmcc_msm8974_resets),
|
||||
};
|
||||
|
||||
static const struct of_device_id mmcc_msm8974_match_table[] = {
|
||||
{ .compatible = "qcom,mmcc-msm8974" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mmcc_msm8974_match_table);
|
||||
|
||||
struct qcom_cc {
|
||||
struct qcom_reset_controller reset;
|
||||
struct clk_onecell_data data;
|
||||
struct clk *clks[];
|
||||
};
|
||||
|
||||
static int mmcc_msm8974_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk;
|
||||
struct clk_onecell_data *data;
|
||||
struct clk **clks;
|
||||
int ret;
|
||||
struct regmap *regmap;
|
||||
size_t num_clks;
|
||||
struct qcom_reset_controller *reset;
|
||||
struct qcom_cc *cc;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, &mmcc_msm8974_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
num_clks = ARRAY_SIZE(mmcc_msm8974_clocks);
|
||||
cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
|
||||
GFP_KERNEL);
|
||||
if (!cc)
|
||||
return -ENOMEM;
|
||||
|
||||
clks = cc->clks;
|
||||
data = &cc->data;
|
||||
data->clks = clks;
|
||||
data->clk_num = num_clks;
|
||||
|
||||
clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true);
|
||||
clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false);
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
if (!mmcc_msm8974_clocks[i])
|
||||
continue;
|
||||
clk = devm_clk_register_regmap(dev, mmcc_msm8974_clocks[i]);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
|
||||
ret = qcom_cc_probe(pdev, &mmcc_msm8974_desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reset = &cc->reset;
|
||||
reset->rcdev.of_node = dev->of_node;
|
||||
reset->rcdev.ops = &qcom_reset_ops,
|
||||
reset->rcdev.owner = THIS_MODULE,
|
||||
reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8974_resets),
|
||||
reset->regmap = regmap;
|
||||
reset->reset_map = mmcc_msm8974_resets,
|
||||
platform_set_drvdata(pdev, &reset->rcdev);
|
||||
regmap = dev_get_regmap(&pdev->dev, NULL);
|
||||
clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true);
|
||||
clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false);
|
||||
|
||||
ret = reset_controller_register(&reset->rcdev);
|
||||
if (ret)
|
||||
of_clk_del_provider(dev->of_node);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmcc_msm8974_remove(struct platform_device *pdev)
|
||||
{
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
reset_controller_unregister(platform_get_drvdata(pdev));
|
||||
qcom_cc_remove(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
|
||||
obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o
|
||||
obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o
|
||||
obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.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
|
||||
|
@ -112,7 +112,7 @@ static int cpg_mstp_clock_is_enabled(struct clk_hw *hw)
|
||||
else
|
||||
value = clk_readl(group->smstpcr);
|
||||
|
||||
return !!(value & BIT(clock->bit_index));
|
||||
return !(value & BIT(clock->bit_index));
|
||||
}
|
||||
|
||||
static const struct clk_ops cpg_mstp_clock_ops = {
|
||||
|
199
drivers/clk/shmobile/clk-r8a7740.c
Normal file
199
drivers/clk/shmobile/clk-r8a7740.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* r8a7740 Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2014 Ulrich Hecht
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/shmobile.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct r8a7740_cpg {
|
||||
struct clk_onecell_data data;
|
||||
spinlock_t lock;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
#define CPG_FRQCRA 0x00
|
||||
#define CPG_FRQCRB 0x04
|
||||
#define CPG_PLLC2CR 0x2c
|
||||
#define CPG_USBCKCR 0x8c
|
||||
#define CPG_FRQCRC 0xe0
|
||||
|
||||
#define CLK_ENABLE_ON_INIT BIT(0)
|
||||
|
||||
struct div4_clk {
|
||||
const char *name;
|
||||
unsigned int reg;
|
||||
unsigned int shift;
|
||||
int flags;
|
||||
};
|
||||
|
||||
static struct div4_clk div4_clks[] = {
|
||||
{ "i", CPG_FRQCRA, 20, CLK_ENABLE_ON_INIT },
|
||||
{ "zg", CPG_FRQCRA, 16, CLK_ENABLE_ON_INIT },
|
||||
{ "b", CPG_FRQCRA, 8, CLK_ENABLE_ON_INIT },
|
||||
{ "m1", CPG_FRQCRA, 4, CLK_ENABLE_ON_INIT },
|
||||
{ "hp", CPG_FRQCRB, 4, 0 },
|
||||
{ "hpp", CPG_FRQCRC, 20, 0 },
|
||||
{ "usbp", CPG_FRQCRC, 16, 0 },
|
||||
{ "s", CPG_FRQCRC, 12, 0 },
|
||||
{ "zb", CPG_FRQCRC, 8, 0 },
|
||||
{ "m3", CPG_FRQCRC, 4, 0 },
|
||||
{ "cp", CPG_FRQCRC, 0, 0 },
|
||||
{ NULL, 0, 0, 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table div4_div_table[] = {
|
||||
{ 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 },
|
||||
{ 6, 16 }, { 7, 18 }, { 8, 24 }, { 9, 32 }, { 10, 36 }, { 11, 48 },
|
||||
{ 13, 72 }, { 14, 96 }, { 0, 0 }
|
||||
};
|
||||
|
||||
static u32 cpg_mode __initdata;
|
||||
|
||||
static struct clk * __init
|
||||
r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg,
|
||||
const char *name)
|
||||
{
|
||||
const struct clk_div_table *table = NULL;
|
||||
const char *parent_name;
|
||||
unsigned int shift, reg;
|
||||
unsigned int mult = 1;
|
||||
unsigned int div = 1;
|
||||
|
||||
if (!strcmp(name, "r")) {
|
||||
switch (cpg_mode & (BIT(2) | BIT(1))) {
|
||||
case BIT(1) | BIT(2):
|
||||
/* extal1 */
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
div = 2048;
|
||||
break;
|
||||
case BIT(2):
|
||||
/* extal1 */
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
div = 1024;
|
||||
break;
|
||||
default:
|
||||
/* extalr */
|
||||
parent_name = of_clk_get_parent_name(np, 2);
|
||||
break;
|
||||
}
|
||||
} else if (!strcmp(name, "system")) {
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (cpg_mode & BIT(1))
|
||||
div = 2;
|
||||
} else if (!strcmp(name, "pllc0")) {
|
||||
/* PLLC0/1 are configurable multiplier clocks. Register them as
|
||||
* fixed factor clocks for now as there's no generic multiplier
|
||||
* clock implementation and we currently have no need to change
|
||||
* the multiplier value.
|
||||
*/
|
||||
u32 value = clk_readl(cpg->reg + CPG_FRQCRC);
|
||||
parent_name = "system";
|
||||
mult = ((value >> 24) & 0x7f) + 1;
|
||||
} else if (!strcmp(name, "pllc1")) {
|
||||
u32 value = clk_readl(cpg->reg + CPG_FRQCRA);
|
||||
parent_name = "system";
|
||||
mult = ((value >> 24) & 0x7f) + 1;
|
||||
div = 2;
|
||||
} else if (!strcmp(name, "pllc2")) {
|
||||
u32 value = clk_readl(cpg->reg + CPG_PLLC2CR);
|
||||
parent_name = "system";
|
||||
mult = ((value >> 24) & 0x3f) + 1;
|
||||
} else if (!strcmp(name, "usb24s")) {
|
||||
u32 value = clk_readl(cpg->reg + CPG_USBCKCR);
|
||||
if (value & BIT(7))
|
||||
/* extal2 */
|
||||
parent_name = of_clk_get_parent_name(np, 1);
|
||||
else
|
||||
parent_name = "system";
|
||||
if (!(value & BIT(6)))
|
||||
div = 2;
|
||||
} else {
|
||||
struct div4_clk *c;
|
||||
for (c = div4_clks; c->name; c++) {
|
||||
if (!strcmp(name, c->name)) {
|
||||
parent_name = "pllc1";
|
||||
table = div4_div_table;
|
||||
reg = c->reg;
|
||||
shift = c->shift;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!c->name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!table) {
|
||||
return clk_register_fixed_factor(NULL, name, parent_name, 0,
|
||||
mult, div);
|
||||
} else {
|
||||
return clk_register_divider_table(NULL, name, parent_name, 0,
|
||||
cpg->reg + reg, shift, 4, 0,
|
||||
table, &cpg->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init r8a7740_cpg_clocks_init(struct device_node *np)
|
||||
{
|
||||
struct r8a7740_cpg *cpg;
|
||||
struct clk **clks;
|
||||
unsigned int i;
|
||||
int num_clks;
|
||||
|
||||
if (of_property_read_u32(np, "renesas,mode", &cpg_mode))
|
||||
pr_warn("%s: missing renesas,mode property\n", __func__);
|
||||
|
||||
num_clks = of_property_count_strings(np, "clock-output-names");
|
||||
if (num_clks < 0) {
|
||||
pr_err("%s: failed to count clocks\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
|
||||
clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL);
|
||||
if (cpg == NULL || clks == NULL) {
|
||||
/* We're leaking memory on purpose, there's no point in cleaning
|
||||
* up as the system won't boot anyway.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&cpg->lock);
|
||||
|
||||
cpg->data.clks = clks;
|
||||
cpg->data.clk_num = num_clks;
|
||||
|
||||
cpg->reg = of_iomap(np, 0);
|
||||
if (WARN_ON(cpg->reg == NULL))
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_clks; ++i) {
|
||||
const char *name;
|
||||
struct clk *clk;
|
||||
|
||||
of_property_read_string_index(np, "clock-output-names", i,
|
||||
&name);
|
||||
|
||||
clk = r8a7740_cpg_register_clock(np, cpg, name);
|
||||
if (IS_ERR(clk))
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clk));
|
||||
else
|
||||
cpg->data.clks[i] = clk;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
|
||||
}
|
||||
CLK_OF_DECLARE(r8a7740_cpg_clks, "renesas,r8a7740-cpg-clocks",
|
||||
r8a7740_cpg_clocks_init);
|
180
drivers/clk/shmobile/clk-r8a7779.c
Normal file
180
drivers/clk/shmobile/clk-r8a7779.c
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* r8a7779 Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2013, 2014 Horms Solutions Ltd.
|
||||
*
|
||||
* Contact: Simon Horman <horms@verge.net.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/shmobile.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <dt-bindings/clock/r8a7779-clock.h>
|
||||
|
||||
#define CPG_NUM_CLOCKS (R8A7779_CLK_OUT + 1)
|
||||
|
||||
struct r8a7779_cpg {
|
||||
struct clk_onecell_data data;
|
||||
spinlock_t lock;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* CPG Clock Data
|
||||
*/
|
||||
|
||||
/*
|
||||
* MD1 = 1 MD1 = 0
|
||||
* (PLLA = 1500) (PLLA = 1600)
|
||||
* (MHz) (MHz)
|
||||
*------------------------------------------------+--------------------
|
||||
* clkz 1000 (2/3) 800 (1/2)
|
||||
* clkzs 250 (1/6) 200 (1/8)
|
||||
* clki 750 (1/2) 800 (1/2)
|
||||
* clks 250 (1/6) 200 (1/8)
|
||||
* clks1 125 (1/12) 100 (1/16)
|
||||
* clks3 187.5 (1/8) 200 (1/8)
|
||||
* clks4 93.7 (1/16) 100 (1/16)
|
||||
* clkp 62.5 (1/24) 50 (1/32)
|
||||
* clkg 62.5 (1/24) 66.6 (1/24)
|
||||
* clkb, CLKOUT
|
||||
* (MD2 = 0) 62.5 (1/24) 66.6 (1/24)
|
||||
* (MD2 = 1) 41.6 (1/36) 50 (1/32)
|
||||
*/
|
||||
|
||||
#define CPG_CLK_CONFIG_INDEX(md) (((md) & (BIT(2)|BIT(1))) >> 1)
|
||||
|
||||
struct cpg_clk_config {
|
||||
unsigned int z_mult;
|
||||
unsigned int z_div;
|
||||
unsigned int zs_and_s_div;
|
||||
unsigned int s1_div;
|
||||
unsigned int p_div;
|
||||
unsigned int b_and_out_div;
|
||||
};
|
||||
|
||||
static const struct cpg_clk_config cpg_clk_configs[4] __initconst = {
|
||||
{ 1, 2, 8, 16, 32, 24 },
|
||||
{ 2, 3, 6, 12, 24, 24 },
|
||||
{ 1, 2, 8, 16, 32, 32 },
|
||||
{ 2, 3, 6, 12, 24, 36 },
|
||||
};
|
||||
|
||||
/*
|
||||
* MD PLLA Ratio
|
||||
* 12 11
|
||||
*------------------------
|
||||
* 0 0 x42
|
||||
* 0 1 x48
|
||||
* 1 0 x56
|
||||
* 1 1 x64
|
||||
*/
|
||||
|
||||
#define CPG_PLLA_MULT_INDEX(md) (((md) & (BIT(12)|BIT(11))) >> 11)
|
||||
|
||||
static const unsigned int cpg_plla_mult[4] __initconst = { 42, 48, 56, 64 };
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
static u32 cpg_mode __initdata;
|
||||
|
||||
static struct clk * __init
|
||||
r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg,
|
||||
const struct cpg_clk_config *config,
|
||||
unsigned int plla_mult, const char *name)
|
||||
{
|
||||
const char *parent_name = "plla";
|
||||
unsigned int mult = 1;
|
||||
unsigned int div = 1;
|
||||
|
||||
if (!strcmp(name, "plla")) {
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
mult = plla_mult;
|
||||
} else if (!strcmp(name, "z")) {
|
||||
div = config->z_div;
|
||||
mult = config->z_mult;
|
||||
} else if (!strcmp(name, "zs") || !strcmp(name, "s")) {
|
||||
div = config->zs_and_s_div;
|
||||
} else if (!strcmp(name, "s1")) {
|
||||
div = config->s1_div;
|
||||
} else if (!strcmp(name, "p")) {
|
||||
div = config->p_div;
|
||||
} else if (!strcmp(name, "b") || !strcmp(name, "out")) {
|
||||
div = config->b_and_out_div;
|
||||
} else {
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, div);
|
||||
}
|
||||
|
||||
static void __init r8a7779_cpg_clocks_init(struct device_node *np)
|
||||
{
|
||||
const struct cpg_clk_config *config;
|
||||
struct r8a7779_cpg *cpg;
|
||||
struct clk **clks;
|
||||
unsigned int i, plla_mult;
|
||||
int num_clks;
|
||||
|
||||
num_clks = of_property_count_strings(np, "clock-output-names");
|
||||
if (num_clks < 0) {
|
||||
pr_err("%s: failed to count clocks\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
|
||||
clks = kzalloc(CPG_NUM_CLOCKS * sizeof(*clks), GFP_KERNEL);
|
||||
if (cpg == NULL || clks == NULL) {
|
||||
/* We're leaking memory on purpose, there's no point in cleaning
|
||||
* up as the system won't boot anyway.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&cpg->lock);
|
||||
|
||||
cpg->data.clks = clks;
|
||||
cpg->data.clk_num = num_clks;
|
||||
|
||||
config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(cpg_mode)];
|
||||
plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(cpg_mode)];
|
||||
|
||||
for (i = 0; i < num_clks; ++i) {
|
||||
const char *name;
|
||||
struct clk *clk;
|
||||
|
||||
of_property_read_string_index(np, "clock-output-names", i,
|
||||
&name);
|
||||
|
||||
clk = r8a7779_cpg_register_clock(np, cpg, config,
|
||||
plla_mult, name);
|
||||
if (IS_ERR(clk))
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clk));
|
||||
else
|
||||
cpg->data.clks[i] = clk;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
|
||||
}
|
||||
CLK_OF_DECLARE(r8a7779_cpg_clks, "renesas,r8a7779-cpg-clocks",
|
||||
r8a7779_cpg_clocks_init);
|
||||
|
||||
void __init r8a7779_clocks_init(u32 mode)
|
||||
{
|
||||
cpg_mode = mode;
|
||||
|
||||
of_clk_init(NULL);
|
||||
}
|
@ -32,7 +32,6 @@
|
||||
#define SOCFPGA_MMC_CLK "sdmmc_clk"
|
||||
#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8
|
||||
|
||||
#define div_mask(width) ((1 << (width)) - 1)
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0)
|
||||
|
||||
#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
|
||||
|
@ -29,12 +29,18 @@ static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk);
|
||||
u32 div;
|
||||
u32 div, val;
|
||||
|
||||
if (socfpgaclk->fixed_div)
|
||||
if (socfpgaclk->fixed_div) {
|
||||
div = socfpgaclk->fixed_div;
|
||||
else
|
||||
} else {
|
||||
if (socfpgaclk->div_reg) {
|
||||
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
|
||||
val &= div_mask(socfpgaclk->width);
|
||||
parent_rate /= (val + 1);
|
||||
}
|
||||
div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
|
||||
}
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
@ -54,6 +60,7 @@ static __init void __socfpga_periph_init(struct device_node *node,
|
||||
struct clk_init_data init;
|
||||
int rc;
|
||||
u32 fixed_div;
|
||||
u32 div_reg[3];
|
||||
|
||||
of_property_read_u32(node, "reg", ®);
|
||||
|
||||
@ -63,6 +70,15 @@ static __init void __socfpga_periph_init(struct device_node *node,
|
||||
|
||||
periph_clk->hw.reg = clk_mgr_base_addr + reg;
|
||||
|
||||
rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
|
||||
if (!rc) {
|
||||
periph_clk->div_reg = clk_mgr_base_addr + div_reg[0];
|
||||
periph_clk->shift = div_reg[1];
|
||||
periph_clk->width = div_reg[2];
|
||||
} else {
|
||||
periph_clk->div_reg = 0;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
|
||||
if (rc)
|
||||
periph_clk->fixed_div = 0;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define CLKMGR_PERPLL_SRC 0xAC
|
||||
|
||||
#define SOCFPGA_MAX_PARENTS 3
|
||||
#define div_mask(width) ((1 << (width)) - 1)
|
||||
|
||||
extern void __iomem *clk_mgr_base_addr;
|
||||
|
||||
@ -52,6 +53,9 @@ struct socfpga_periph_clk {
|
||||
struct clk_gate hw;
|
||||
char *parent_name;
|
||||
u32 fixed_div;
|
||||
void __iomem *div_reg;
|
||||
u32 width; /* only valid if div_reg != 0 */
|
||||
u32 shift; /* only valid if div_reg != 0 */
|
||||
};
|
||||
|
||||
#endif /* SOCFPGA_CLK_H */
|
||||
|
@ -77,6 +77,41 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
return rate;
|
||||
}
|
||||
|
||||
static long clk_factors_determine_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *best_parent_rate,
|
||||
struct clk **best_parent_p)
|
||||
{
|
||||
struct clk *clk = hw->clk, *parent, *best_parent = NULL;
|
||||
int i, num_parents;
|
||||
unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0;
|
||||
|
||||
/* find the parent that can help provide the fastest rate <= rate */
|
||||
num_parents = __clk_get_num_parents(clk);
|
||||
for (i = 0; i < num_parents; i++) {
|
||||
parent = clk_get_parent_by_index(clk, i);
|
||||
if (!parent)
|
||||
continue;
|
||||
if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT)
|
||||
parent_rate = __clk_round_rate(parent, rate);
|
||||
else
|
||||
parent_rate = __clk_get_rate(parent);
|
||||
|
||||
child_rate = clk_factors_round_rate(hw, rate, &parent_rate);
|
||||
|
||||
if (child_rate <= rate && child_rate > best_child_rate) {
|
||||
best_parent = parent;
|
||||
best = parent_rate;
|
||||
best_child_rate = child_rate;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_parent)
|
||||
*best_parent_p = best_parent;
|
||||
*best_parent_rate = best;
|
||||
|
||||
return best_child_rate;
|
||||
}
|
||||
|
||||
static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@ -113,6 +148,7 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
}
|
||||
|
||||
const struct clk_ops clk_factors_ops = {
|
||||
.determine_rate = clk_factors_determine_rate,
|
||||
.recalc_rate = clk_factors_recalc_rate,
|
||||
.round_rate = clk_factors_round_rate,
|
||||
.set_rate = clk_factors_set_rate,
|
||||
|
@ -506,6 +506,43 @@ CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk",
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* clk_sunxi_mmc_phase_control() - configures MMC clock phase control
|
||||
*/
|
||||
|
||||
void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output)
|
||||
{
|
||||
#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
|
||||
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
|
||||
|
||||
struct clk_hw *hw = __clk_get_hw(clk);
|
||||
struct clk_composite *composite = to_clk_composite(hw);
|
||||
struct clk_hw *rate_hw = composite->rate_hw;
|
||||
struct clk_factors *factors = to_clk_factors(rate_hw);
|
||||
unsigned long flags = 0;
|
||||
u32 reg;
|
||||
|
||||
if (factors->lock)
|
||||
spin_lock_irqsave(factors->lock, flags);
|
||||
|
||||
reg = readl(factors->reg);
|
||||
|
||||
/* set sample clock phase control */
|
||||
reg &= ~(0x7 << 20);
|
||||
reg |= ((sample & 0x7) << 20);
|
||||
|
||||
/* set output clock phase control */
|
||||
reg &= ~(0x7 << 8);
|
||||
reg |= ((output & 0x7) << 8);
|
||||
|
||||
writel(reg, factors->reg);
|
||||
|
||||
if (factors->lock)
|
||||
spin_unlock_irqrestore(factors->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(clk_sunxi_mmc_phase_control);
|
||||
|
||||
|
||||
/**
|
||||
* sunxi_factors_clk_setup() - Setup function for factor clocks
|
||||
*/
|
||||
|
@ -233,6 +233,7 @@ enum clk_id {
|
||||
tegra_clk_xusb_hs_src,
|
||||
tegra_clk_xusb_ss,
|
||||
tegra_clk_xusb_ss_src,
|
||||
tegra_clk_xusb_ss_div2,
|
||||
tegra_clk_max,
|
||||
};
|
||||
|
||||
|
@ -96,10 +96,20 @@
|
||||
(PLLE_SS_MAX_VAL | PLLE_SS_INC_VAL | PLLE_SS_INCINTRV_VAL)
|
||||
|
||||
#define PLLE_AUX_PLLP_SEL BIT(2)
|
||||
#define PLLE_AUX_USE_LOCKDET BIT(3)
|
||||
#define PLLE_AUX_ENABLE_SWCTL BIT(4)
|
||||
#define PLLE_AUX_SS_SWCTL BIT(6)
|
||||
#define PLLE_AUX_SEQ_ENABLE BIT(24)
|
||||
#define PLLE_AUX_SEQ_START_STATE BIT(25)
|
||||
#define PLLE_AUX_PLLRE_SEL BIT(28)
|
||||
|
||||
#define XUSBIO_PLL_CFG0 0x51c
|
||||
#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0)
|
||||
#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL BIT(2)
|
||||
#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET BIT(6)
|
||||
#define XUSBIO_PLL_CFG0_SEQ_ENABLE BIT(24)
|
||||
#define XUSBIO_PLL_CFG0_SEQ_START_STATE BIT(25)
|
||||
|
||||
#define PLLE_MISC_PLLE_PTS BIT(8)
|
||||
#define PLLE_MISC_IDDQ_SW_VALUE BIT(13)
|
||||
#define PLLE_MISC_IDDQ_SW_CTRL BIT(14)
|
||||
@ -1318,7 +1328,28 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw)
|
||||
pll_writel(val, PLLE_SS_CTRL, pll);
|
||||
udelay(1);
|
||||
|
||||
/* TODO: enable hw control of xusb brick pll */
|
||||
/* Enable hw control of xusb brick pll */
|
||||
val = pll_readl_misc(pll);
|
||||
val &= ~PLLE_MISC_IDDQ_SW_CTRL;
|
||||
pll_writel_misc(val, pll);
|
||||
|
||||
val = pll_readl(pll->params->aux_reg, pll);
|
||||
val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SEQ_START_STATE);
|
||||
val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
|
||||
pll_writel(val, pll->params->aux_reg, pll);
|
||||
udelay(1);
|
||||
val |= PLLE_AUX_SEQ_ENABLE;
|
||||
pll_writel(val, pll->params->aux_reg, pll);
|
||||
|
||||
val = pll_readl(XUSBIO_PLL_CFG0, pll);
|
||||
val |= (XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET |
|
||||
XUSBIO_PLL_CFG0_SEQ_START_STATE);
|
||||
val &= ~(XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL |
|
||||
XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL);
|
||||
pll_writel(val, XUSBIO_PLL_CFG0, pll);
|
||||
udelay(1);
|
||||
val |= XUSBIO_PLL_CFG0_SEQ_ENABLE;
|
||||
pll_writel(val, XUSBIO_PLL_CFG0, pll);
|
||||
|
||||
out:
|
||||
if (pll->lock)
|
||||
|
@ -329,7 +329,9 @@ static u32 mux_clkm_pllp_pllc_pllre_idx[] = {
|
||||
static const char *mux_clkm_48M_pllp_480M[] = {
|
||||
"clk_m", "pll_u_48M", "pll_p", "pll_u_480M"
|
||||
};
|
||||
#define mux_clkm_48M_pllp_480M_idx NULL
|
||||
static u32 mux_clkm_48M_pllp_480M_idx[] = {
|
||||
[0] = 0, [1] = 2, [2] = 4, [3] = 6,
|
||||
};
|
||||
|
||||
static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = {
|
||||
"clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref"
|
||||
@ -338,6 +340,11 @@ static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = {
|
||||
[0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7,
|
||||
};
|
||||
|
||||
static const char *mux_ss_60M[] = {
|
||||
"xusb_ss_div2", "pll_u_60M"
|
||||
};
|
||||
#define mux_ss_60M_idx NULL
|
||||
|
||||
static const char *mux_d_audio_clk[] = {
|
||||
"pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync",
|
||||
"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
|
||||
@ -499,6 +506,7 @@ static struct tegra_periph_init_data periph_clks[] = {
|
||||
XUSB("xusb_falcon_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_falcon_src),
|
||||
XUSB("xusb_fs_src", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_fs_src),
|
||||
XUSB("xusb_ss_src", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_ss_src),
|
||||
NODIV("xusb_hs_src", mux_ss_60M, CLK_SOURCE_XUSB_SS_SRC, 25, MASK(1), 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_hs_src, NULL),
|
||||
XUSB("xusb_dev_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_dev_src),
|
||||
};
|
||||
|
||||
|
@ -142,7 +142,6 @@
|
||||
#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0)
|
||||
|
||||
#define CLK_SOURCE_CSITE 0x1d4
|
||||
#define CLK_SOURCE_XUSB_SS_SRC 0x610
|
||||
#define CLK_SOURCE_EMC 0x19c
|
||||
|
||||
/* PLLM override registers */
|
||||
@ -834,6 +833,7 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = {
|
||||
[tegra_clk_xusb_falcon_src] = { .dt_id = TEGRA114_CLK_XUSB_FALCON_SRC, .present = true },
|
||||
[tegra_clk_xusb_fs_src] = { .dt_id = TEGRA114_CLK_XUSB_FS_SRC, .present = true },
|
||||
[tegra_clk_xusb_ss_src] = { .dt_id = TEGRA114_CLK_XUSB_SS_SRC, .present = true },
|
||||
[tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA114_CLK_XUSB_SS_DIV2, .present = true},
|
||||
[tegra_clk_xusb_dev_src] = { .dt_id = TEGRA114_CLK_XUSB_DEV_SRC, .present = true },
|
||||
[tegra_clk_xusb_dev] = { .dt_id = TEGRA114_CLK_XUSB_DEV, .present = true },
|
||||
[tegra_clk_xusb_hs_src] = { .dt_id = TEGRA114_CLK_XUSB_HS_SRC, .present = true },
|
||||
@ -1182,16 +1182,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base,
|
||||
void __iomem *pmc_base)
|
||||
{
|
||||
struct clk *clk;
|
||||
u32 val;
|
||||
|
||||
/* xusb_hs_src */
|
||||
val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC);
|
||||
val |= BIT(25); /* always select PLLU_60M */
|
||||
writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC);
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0,
|
||||
1, 1);
|
||||
clks[TEGRA114_CLK_XUSB_HS_SRC] = clk;
|
||||
/* xusb_ss_div2 */
|
||||
clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0,
|
||||
1, 2);
|
||||
clks[TEGRA114_CLK_XUSB_SS_DIV2] = clk;
|
||||
|
||||
/* dsia mux */
|
||||
clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0,
|
||||
@ -1301,7 +1296,12 @@ static struct tegra_clk_init_table init_table[] __initdata = {
|
||||
{TEGRA114_CLK_GR3D, TEGRA114_CLK_PLL_C2, 300000000, 0},
|
||||
{TEGRA114_CLK_DSIALP, TEGRA114_CLK_PLL_P, 68000000, 0},
|
||||
{TEGRA114_CLK_DSIBLP, TEGRA114_CLK_PLL_P, 68000000, 0},
|
||||
|
||||
{TEGRA114_CLK_PLL_RE_VCO, TEGRA114_CLK_CLK_MAX, 612000000, 0},
|
||||
{TEGRA114_CLK_XUSB_SS_SRC, TEGRA114_CLK_PLL_RE_OUT, 122400000, 0},
|
||||
{TEGRA114_CLK_XUSB_FS_SRC, TEGRA114_CLK_PLL_U_48M, 48000000, 0},
|
||||
{TEGRA114_CLK_XUSB_HS_SRC, TEGRA114_CLK_XUSB_SS_DIV2, 61200000, 0},
|
||||
{TEGRA114_CLK_XUSB_FALCON_SRC, TEGRA114_CLK_PLL_P, 204000000, 0},
|
||||
{TEGRA114_CLK_XUSB_HOST_SRC, TEGRA114_CLK_PLL_P, 102000000, 0},
|
||||
/* This MUST be the last entry. */
|
||||
{TEGRA114_CLK_CLK_MAX, TEGRA114_CLK_CLK_MAX, 0, 0},
|
||||
};
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
#define CLK_SOURCE_CSITE 0x1d4
|
||||
#define CLK_SOURCE_EMC 0x19c
|
||||
#define CLK_SOURCE_XUSB_SS_SRC 0x610
|
||||
|
||||
#define PLLC_BASE 0x80
|
||||
#define PLLC_OUT 0x84
|
||||
@ -925,6 +924,7 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = {
|
||||
[tegra_clk_xusb_falcon_src] = { .dt_id = TEGRA124_CLK_XUSB_FALCON_SRC, .present = true },
|
||||
[tegra_clk_xusb_fs_src] = { .dt_id = TEGRA124_CLK_XUSB_FS_SRC, .present = true },
|
||||
[tegra_clk_xusb_ss_src] = { .dt_id = TEGRA124_CLK_XUSB_SS_SRC, .present = true },
|
||||
[tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA124_CLK_XUSB_SS_DIV2, .present = true },
|
||||
[tegra_clk_xusb_dev_src] = { .dt_id = TEGRA124_CLK_XUSB_DEV_SRC, .present = true },
|
||||
[tegra_clk_xusb_dev] = { .dt_id = TEGRA124_CLK_XUSB_DEV, .present = true },
|
||||
[tegra_clk_xusb_hs_src] = { .dt_id = TEGRA124_CLK_XUSB_HS_SRC, .present = true },
|
||||
@ -1105,16 +1105,11 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base,
|
||||
void __iomem *pmc_base)
|
||||
{
|
||||
struct clk *clk;
|
||||
u32 val;
|
||||
|
||||
/* xusb_hs_src */
|
||||
val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC);
|
||||
val |= BIT(25); /* always select PLLU_60M */
|
||||
writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC);
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0,
|
||||
1, 1);
|
||||
clks[TEGRA124_CLK_XUSB_HS_SRC] = clk;
|
||||
/* xusb_ss_div2 */
|
||||
clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0,
|
||||
1, 2);
|
||||
clks[TEGRA124_CLK_XUSB_SS_DIV2] = clk;
|
||||
|
||||
/* dsia mux */
|
||||
clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0,
|
||||
@ -1368,6 +1363,12 @@ static struct tegra_clk_init_table init_table[] __initdata = {
|
||||
{TEGRA124_CLK_SBC4, TEGRA124_CLK_PLL_P, 12000000, 1},
|
||||
{TEGRA124_CLK_TSEC, TEGRA124_CLK_PLL_C3, 0, 0},
|
||||
{TEGRA124_CLK_MSENC, TEGRA124_CLK_PLL_C3, 0, 0},
|
||||
{TEGRA124_CLK_PLL_RE_VCO, TEGRA124_CLK_CLK_MAX, 672000000, 0},
|
||||
{TEGRA124_CLK_XUSB_SS_SRC, TEGRA124_CLK_PLL_U_480M, 120000000, 0},
|
||||
{TEGRA124_CLK_XUSB_FS_SRC, TEGRA124_CLK_PLL_U_48M, 48000000, 0},
|
||||
{TEGRA124_CLK_XUSB_HS_SRC, TEGRA124_CLK_PLL_U_60M, 60000000, 0},
|
||||
{TEGRA124_CLK_XUSB_FALCON_SRC, TEGRA124_CLK_PLL_RE_OUT, 224000000, 0},
|
||||
{TEGRA124_CLK_XUSB_HOST_SRC, TEGRA124_CLK_PLL_RE_OUT, 112000000, 0},
|
||||
/* This MUST be the last entry. */
|
||||
{TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0},
|
||||
};
|
||||
|
@ -160,3 +160,4 @@ struct clk *icst_clk_register(struct device *dev,
|
||||
|
||||
return clk;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(icst_clk_register);
|
||||
|
@ -20,6 +20,8 @@
|
||||
#define IMPD1_LOCK 0x08
|
||||
|
||||
struct impd1_clk {
|
||||
char *pclkname;
|
||||
struct clk *pclk;
|
||||
char *vco1name;
|
||||
struct clk *vco1clk;
|
||||
char *vco2name;
|
||||
@ -31,7 +33,7 @@ struct impd1_clk {
|
||||
struct clk *spiclk;
|
||||
char *scname;
|
||||
struct clk *scclk;
|
||||
struct clk_lookup *clks[6];
|
||||
struct clk_lookup *clks[15];
|
||||
};
|
||||
|
||||
/* One entry for each connected IM-PD1 LM */
|
||||
@ -86,6 +88,7 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id)
|
||||
{
|
||||
struct impd1_clk *imc;
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
int i;
|
||||
|
||||
if (id > 3) {
|
||||
@ -94,11 +97,18 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id)
|
||||
}
|
||||
imc = &impd1_clks[id];
|
||||
|
||||
/* Register the fixed rate PCLK */
|
||||
imc->pclkname = kasprintf(GFP_KERNEL, "lm%x-pclk", id);
|
||||
pclk = clk_register_fixed_rate(NULL, imc->pclkname, NULL,
|
||||
CLK_IS_ROOT, 0);
|
||||
imc->pclk = pclk;
|
||||
|
||||
imc->vco1name = kasprintf(GFP_KERNEL, "lm%x-vco1", id);
|
||||
clk = icst_clk_register(NULL, &impd1_icst1_desc, imc->vco1name, NULL,
|
||||
base);
|
||||
imc->vco1clk = clk;
|
||||
imc->clks[0] = clkdev_alloc(clk, NULL, "lm%x:01000", id);
|
||||
imc->clks[0] = clkdev_alloc(pclk, "apb_pclk", "lm%x:01000", id);
|
||||
imc->clks[1] = clkdev_alloc(clk, NULL, "lm%x:01000", id);
|
||||
|
||||
/* VCO2 is also called "CLK2" */
|
||||
imc->vco2name = kasprintf(GFP_KERNEL, "lm%x-vco2", id);
|
||||
@ -107,32 +117,43 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id)
|
||||
imc->vco2clk = clk;
|
||||
|
||||
/* MMCI uses CLK2 right off */
|
||||
imc->clks[1] = clkdev_alloc(clk, NULL, "lm%x:00700", id);
|
||||
imc->clks[2] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00700", id);
|
||||
imc->clks[3] = clkdev_alloc(clk, NULL, "lm%x:00700", id);
|
||||
|
||||
/* UART reference clock divides CLK2 by a fixed factor 4 */
|
||||
imc->uartname = kasprintf(GFP_KERNEL, "lm%x-uartclk", id);
|
||||
clk = clk_register_fixed_factor(NULL, imc->uartname, imc->vco2name,
|
||||
CLK_IGNORE_UNUSED, 1, 4);
|
||||
imc->uartclk = clk;
|
||||
imc->clks[2] = clkdev_alloc(clk, NULL, "lm%x:00100", id);
|
||||
imc->clks[3] = clkdev_alloc(clk, NULL, "lm%x:00200", id);
|
||||
imc->clks[4] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00100", id);
|
||||
imc->clks[5] = clkdev_alloc(clk, NULL, "lm%x:00100", id);
|
||||
imc->clks[6] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00200", id);
|
||||
imc->clks[7] = clkdev_alloc(clk, NULL, "lm%x:00200", id);
|
||||
|
||||
/* SPI PL022 clock divides CLK2 by a fixed factor 64 */
|
||||
imc->spiname = kasprintf(GFP_KERNEL, "lm%x-spiclk", id);
|
||||
clk = clk_register_fixed_factor(NULL, imc->spiname, imc->vco2name,
|
||||
CLK_IGNORE_UNUSED, 1, 64);
|
||||
imc->clks[4] = clkdev_alloc(clk, NULL, "lm%x:00300", id);
|
||||
imc->clks[8] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00300", id);
|
||||
imc->clks[9] = clkdev_alloc(clk, NULL, "lm%x:00300", id);
|
||||
|
||||
/* The GPIO blocks and AACI have only PCLK */
|
||||
imc->clks[10] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00400", id);
|
||||
imc->clks[11] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00500", id);
|
||||
imc->clks[12] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00800", id);
|
||||
|
||||
/* Smart Card clock divides CLK2 by a fixed factor 4 */
|
||||
imc->scname = kasprintf(GFP_KERNEL, "lm%x-scclk", id);
|
||||
clk = clk_register_fixed_factor(NULL, imc->scname, imc->vco2name,
|
||||
CLK_IGNORE_UNUSED, 1, 4);
|
||||
imc->scclk = clk;
|
||||
imc->clks[5] = clkdev_alloc(clk, NULL, "lm%x:00600", id);
|
||||
imc->clks[13] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00600", id);
|
||||
imc->clks[14] = clkdev_alloc(clk, NULL, "lm%x:00600", id);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(imc->clks); i++)
|
||||
clkdev_add(imc->clks[i]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(integrator_impd1_clk_init);
|
||||
|
||||
void integrator_impd1_clk_exit(unsigned int id)
|
||||
{
|
||||
@ -149,9 +170,12 @@ void integrator_impd1_clk_exit(unsigned int id)
|
||||
clk_unregister(imc->uartclk);
|
||||
clk_unregister(imc->vco2clk);
|
||||
clk_unregister(imc->vco1clk);
|
||||
clk_unregister(imc->pclk);
|
||||
kfree(imc->scname);
|
||||
kfree(imc->spiname);
|
||||
kfree(imc->uartname);
|
||||
kfree(imc->vco2name);
|
||||
kfree(imc->vco1name);
|
||||
kfree(imc->pclkname);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(integrator_impd1_clk_exit);
|
||||
|
@ -53,6 +53,9 @@ static void __iomem *zynq_clkc_base;
|
||||
|
||||
#define NUM_MIO_PINS 54
|
||||
|
||||
#define DBG_CLK_CTRL_CLKACT_TRC BIT(0)
|
||||
#define DBG_CLK_CTRL_CPU_1XCLKACT BIT(1)
|
||||
|
||||
enum zynq_clk {
|
||||
armpll, ddrpll, iopll,
|
||||
cpu_6or4x, cpu_3or2x, cpu_2x, cpu_1x,
|
||||
@ -499,6 +502,15 @@ static void __init zynq_clk_setup(struct device_node *np)
|
||||
clk_output_name[cpu_1x], 0, SLCR_DBG_CLK_CTRL, 1, 0,
|
||||
&dbgclk_lock);
|
||||
|
||||
/* leave debug clocks in the state the bootloader set them up to */
|
||||
tmp = clk_readl(SLCR_DBG_CLK_CTRL);
|
||||
if (tmp & DBG_CLK_CTRL_CLKACT_TRC)
|
||||
if (clk_prepare_enable(clks[dbg_trc]))
|
||||
pr_warn("%s: trace clk enable failed\n", __func__);
|
||||
if (tmp & DBG_CLK_CTRL_CPU_1XCLKACT)
|
||||
if (clk_prepare_enable(clks[dbg_apb]))
|
||||
pr_warn("%s: debug APB clk enable failed\n", __func__);
|
||||
|
||||
/* One gated clock for all APER clocks. */
|
||||
clks[dma] = clk_register_gate(NULL, clk_output_name[dma],
|
||||
clk_output_name[cpu_2x], 0, SLCR_APER_CLK_CTRL, 0, 0,
|
||||
|
62
include/dt-bindings/clock/bcm21664.h
Normal file
62
include/dt-bindings/clock/bcm21664.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Broadcom Corporation
|
||||
* Copyright 2013 Linaro Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _CLOCK_BCM21664_H
|
||||
#define _CLOCK_BCM21664_H
|
||||
|
||||
/*
|
||||
* This file defines the values used to specify clocks provided by
|
||||
* the clock control units (CCUs) on Broadcom BCM21664 family SoCs.
|
||||
*/
|
||||
|
||||
/* bcm21664 CCU device tree "compatible" strings */
|
||||
#define BCM21664_DT_ROOT_CCU_COMPAT "brcm,bcm21664-root-ccu"
|
||||
#define BCM21664_DT_AON_CCU_COMPAT "brcm,bcm21664-aon-ccu"
|
||||
#define BCM21664_DT_MASTER_CCU_COMPAT "brcm,bcm21664-master-ccu"
|
||||
#define BCM21664_DT_SLAVE_CCU_COMPAT "brcm,bcm21664-slave-ccu"
|
||||
|
||||
/* root CCU clock ids */
|
||||
|
||||
#define BCM21664_ROOT_CCU_FRAC_1M 0
|
||||
#define BCM21664_ROOT_CCU_CLOCK_COUNT 1
|
||||
|
||||
/* aon CCU clock ids */
|
||||
|
||||
#define BCM21664_AON_CCU_HUB_TIMER 0
|
||||
#define BCM21664_AON_CCU_CLOCK_COUNT 1
|
||||
|
||||
/* master CCU clock ids */
|
||||
|
||||
#define BCM21664_MASTER_CCU_SDIO1 0
|
||||
#define BCM21664_MASTER_CCU_SDIO2 1
|
||||
#define BCM21664_MASTER_CCU_SDIO3 2
|
||||
#define BCM21664_MASTER_CCU_SDIO4 3
|
||||
#define BCM21664_MASTER_CCU_SDIO1_SLEEP 4
|
||||
#define BCM21664_MASTER_CCU_SDIO2_SLEEP 5
|
||||
#define BCM21664_MASTER_CCU_SDIO3_SLEEP 6
|
||||
#define BCM21664_MASTER_CCU_SDIO4_SLEEP 7
|
||||
#define BCM21664_MASTER_CCU_CLOCK_COUNT 8
|
||||
|
||||
/* slave CCU clock ids */
|
||||
|
||||
#define BCM21664_SLAVE_CCU_UARTB 0
|
||||
#define BCM21664_SLAVE_CCU_UARTB2 1
|
||||
#define BCM21664_SLAVE_CCU_UARTB3 2
|
||||
#define BCM21664_SLAVE_CCU_BSC1 3
|
||||
#define BCM21664_SLAVE_CCU_BSC2 4
|
||||
#define BCM21664_SLAVE_CCU_BSC3 5
|
||||
#define BCM21664_SLAVE_CCU_BSC4 6
|
||||
#define BCM21664_SLAVE_CCU_CLOCK_COUNT 7
|
||||
|
||||
#endif /* _CLOCK_BCM21664_H */
|
@ -20,6 +20,18 @@
|
||||
* the clock control units (CCUs) on Broadcom BCM281XX family SoCs.
|
||||
*/
|
||||
|
||||
/*
|
||||
* These are the bcm281xx CCU device tree "compatible" strings.
|
||||
* We're stuck with using "bcm11351" in the string because wild
|
||||
* cards aren't allowed, and that name was the first one defined
|
||||
* in this family of devices.
|
||||
*/
|
||||
#define BCM281XX_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu"
|
||||
#define BCM281XX_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu"
|
||||
#define BCM281XX_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu"
|
||||
#define BCM281XX_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu"
|
||||
#define BCM281XX_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu"
|
||||
|
||||
/* root CCU clock ids */
|
||||
|
||||
#define BCM281XX_ROOT_CCU_FRAC_1M 0
|
||||
|
58
include/dt-bindings/clock/hix5hd2-clock.h
Normal file
58
include/dt-bindings/clock/hix5hd2-clock.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Linaro Ltd.
|
||||
* Copyright (c) 2014 Hisilicon Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __DTS_HIX5HD2_CLOCK_H
|
||||
#define __DTS_HIX5HD2_CLOCK_H
|
||||
|
||||
/* fixed rate */
|
||||
#define HIX5HD2_FIXED_1200M 1
|
||||
#define HIX5HD2_FIXED_400M 2
|
||||
#define HIX5HD2_FIXED_48M 3
|
||||
#define HIX5HD2_FIXED_24M 4
|
||||
#define HIX5HD2_FIXED_600M 5
|
||||
#define HIX5HD2_FIXED_300M 6
|
||||
#define HIX5HD2_FIXED_75M 7
|
||||
#define HIX5HD2_FIXED_200M 8
|
||||
#define HIX5HD2_FIXED_100M 9
|
||||
#define HIX5HD2_FIXED_40M 10
|
||||
#define HIX5HD2_FIXED_150M 11
|
||||
#define HIX5HD2_FIXED_1728M 12
|
||||
#define HIX5HD2_FIXED_28P8M 13
|
||||
#define HIX5HD2_FIXED_432M 14
|
||||
#define HIX5HD2_FIXED_345P6M 15
|
||||
#define HIX5HD2_FIXED_288M 16
|
||||
#define HIX5HD2_FIXED_60M 17
|
||||
#define HIX5HD2_FIXED_750M 18
|
||||
#define HIX5HD2_FIXED_500M 19
|
||||
#define HIX5HD2_FIXED_54M 20
|
||||
#define HIX5HD2_FIXED_27M 21
|
||||
#define HIX5HD2_FIXED_1500M 22
|
||||
#define HIX5HD2_FIXED_375M 23
|
||||
#define HIX5HD2_FIXED_187M 24
|
||||
#define HIX5HD2_FIXED_250M 25
|
||||
#define HIX5HD2_FIXED_125M 26
|
||||
#define HIX5HD2_FIXED_2P02M 27
|
||||
#define HIX5HD2_FIXED_50M 28
|
||||
#define HIX5HD2_FIXED_25M 29
|
||||
#define HIX5HD2_FIXED_83M 30
|
||||
|
||||
/* mux clocks */
|
||||
#define HIX5HD2_SFC_MUX 64
|
||||
#define HIX5HD2_MMC_MUX 65
|
||||
#define HIX5HD2_FEPHY_MUX 66
|
||||
|
||||
/* gate clocks */
|
||||
#define HIX5HD2_SFC_RST 128
|
||||
#define HIX5HD2_SFC_CLK 129
|
||||
#define HIX5HD2_MMC_CIU_CLK 130
|
||||
#define HIX5HD2_MMC_BIU_CLK 131
|
||||
#define HIX5HD2_MMC_CIU_RST 132
|
||||
|
||||
#define HIX5HD2_NR_CLKS 256
|
||||
#endif /* __DTS_HIX5HD2_CLOCK_H */
|
@ -51,7 +51,7 @@
|
||||
#define QDSS_TSCTR_CLK 34
|
||||
#define SFAB_ADM0_M0_A_CLK 35
|
||||
#define SFAB_ADM0_M1_A_CLK 36
|
||||
#define SFAB_ADM0_M2_A_CLK 37
|
||||
#define SFAB_ADM0_M2_H_CLK 37
|
||||
#define ADM0_CLK 38
|
||||
#define ADM0_PBUS_CLK 39
|
||||
#define MSS_XPU_CLK 40
|
||||
@ -99,7 +99,7 @@
|
||||
#define CFPB2_H_CLK 82
|
||||
#define SFAB_CFPB_M_H_CLK 83
|
||||
#define CFPB_MASTER_H_CLK 84
|
||||
#define SFAB_CFPB_S_HCLK 85
|
||||
#define SFAB_CFPB_S_H_CLK 85
|
||||
#define CFPB_SPLITTER_H_CLK 86
|
||||
#define TSIF_H_CLK 87
|
||||
#define TSIF_INACTIVITY_TIMERS_CLK 88
|
||||
@ -110,7 +110,6 @@
|
||||
#define CE1_SLEEP_CLK 93
|
||||
#define CE2_H_CLK 94
|
||||
#define CE2_CORE_CLK 95
|
||||
#define CE2_SLEEP_CLK 96
|
||||
#define SFPB_H_CLK_SRC 97
|
||||
#define SFPB_H_CLK 98
|
||||
#define SFAB_SFPB_M_H_CLK 99
|
||||
@ -252,7 +251,7 @@
|
||||
#define MSS_S_H_CLK 235
|
||||
#define MSS_CXO_SRC_CLK 236
|
||||
#define SATA_H_CLK 237
|
||||
#define SATA_SRC_CLK 238
|
||||
#define SATA_CLK_SRC 238
|
||||
#define SATA_RXOOB_CLK 239
|
||||
#define SATA_PMALIVE_CLK 240
|
||||
#define SATA_PHY_REF_CLK 241
|
||||
|
@ -337,6 +337,7 @@
|
||||
#define TEGRA114_CLK_CLK_OUT_3_MUX 308
|
||||
#define TEGRA114_CLK_DSIA_MUX 309
|
||||
#define TEGRA114_CLK_DSIB_MUX 310
|
||||
#define TEGRA114_CLK_CLK_MAX 311
|
||||
#define TEGRA114_CLK_XUSB_SS_DIV2 311
|
||||
#define TEGRA114_CLK_CLK_MAX 312
|
||||
|
||||
#endif /* _DT_BINDINGS_CLOCK_TEGRA114_CAR_H */
|
||||
|
@ -336,6 +336,7 @@
|
||||
#define TEGRA124_CLK_DSIA_MUX 309
|
||||
#define TEGRA124_CLK_DSIB_MUX 310
|
||||
#define TEGRA124_CLK_SOR0_LVDS 311
|
||||
#define TEGRA124_CLK_CLK_MAX 312
|
||||
#define TEGRA124_CLK_XUSB_SS_DIV2 312
|
||||
#define TEGRA124_CLK_CLK_MAX 313
|
||||
|
||||
#endif /* _DT_BINDINGS_CLOCK_TEGRA124_CAR_H */
|
||||
|
@ -58,7 +58,7 @@
|
||||
#define PPSS_PROC_RESET 41
|
||||
#define PPSS_RESET 42
|
||||
#define DMA_BAM_RESET 43
|
||||
#define SIC_TIC_RESET 44
|
||||
#define SPS_TIC_H_RESET 44
|
||||
#define SLIMBUS_H_RESET 45
|
||||
#define SFAB_CFPB_M_RESET 46
|
||||
#define SFAB_CFPB_S_RESET 47
|
||||
|
@ -40,14 +40,14 @@ struct dentry;
|
||||
* through the clk_* api.
|
||||
*
|
||||
* @prepare: Prepare the clock for enabling. This must not return until
|
||||
* the clock is fully prepared, and it's safe to call clk_enable.
|
||||
* This callback is intended to allow clock implementations to
|
||||
* do any initialisation that may sleep. Called with
|
||||
* prepare_lock held.
|
||||
* the clock is fully prepared, and it's safe to call clk_enable.
|
||||
* This callback is intended to allow clock implementations to
|
||||
* do any initialisation that may sleep. Called with
|
||||
* prepare_lock held.
|
||||
*
|
||||
* @unprepare: Release the clock from its prepared state. This will typically
|
||||
* undo any work done in the @prepare callback. Called with
|
||||
* prepare_lock held.
|
||||
* undo any work done in the @prepare callback. Called with
|
||||
* prepare_lock held.
|
||||
*
|
||||
* @is_prepared: Queries the hardware to determine if the clock is prepared.
|
||||
* This function is allowed to sleep. Optional, if this op is not
|
||||
@ -58,16 +58,16 @@ struct dentry;
|
||||
* Called with prepare mutex held. This function may sleep.
|
||||
*
|
||||
* @enable: Enable the clock atomically. This must not return until the
|
||||
* clock is generating a valid clock signal, usable by consumer
|
||||
* devices. Called with enable_lock held. This function must not
|
||||
* sleep.
|
||||
* clock is generating a valid clock signal, usable by consumer
|
||||
* devices. Called with enable_lock held. This function must not
|
||||
* sleep.
|
||||
*
|
||||
* @disable: Disable the clock atomically. Called with enable_lock held.
|
||||
* This function must not sleep.
|
||||
* This function must not sleep.
|
||||
*
|
||||
* @is_enabled: Queries the hardware to determine if the clock is enabled.
|
||||
* This function must not sleep. Optional, if this op is not
|
||||
* set then the enable count will be used.
|
||||
* This function must not sleep. Optional, if this op is not
|
||||
* set then the enable count will be used.
|
||||
*
|
||||
* @disable_unused: Disable the clock atomically. Only called from
|
||||
* clk_disable_unused for gate clocks with special needs.
|
||||
@ -75,34 +75,35 @@ struct dentry;
|
||||
* sleep.
|
||||
*
|
||||
* @recalc_rate Recalculate the rate of this clock, by querying hardware. The
|
||||
* parent rate is an input parameter. It is up to the caller to
|
||||
* ensure that the prepare_mutex is held across this call.
|
||||
* Returns the calculated rate. Optional, but recommended - if
|
||||
* this op is not set then clock rate will be initialized to 0.
|
||||
* parent rate is an input parameter. It is up to the caller to
|
||||
* ensure that the prepare_mutex is held across this call.
|
||||
* Returns the calculated rate. Optional, but recommended - if
|
||||
* this op is not set then clock rate will be initialized to 0.
|
||||
*
|
||||
* @round_rate: Given a target rate as input, returns the closest rate actually
|
||||
* supported by the clock.
|
||||
* supported by the clock. The parent rate is an input/output
|
||||
* parameter.
|
||||
*
|
||||
* @determine_rate: Given a target rate as input, returns the closest rate
|
||||
* actually supported by the clock, and optionally the parent clock
|
||||
* that should be used to provide the clock rate.
|
||||
*
|
||||
* @get_parent: Queries the hardware to determine the parent of a clock. The
|
||||
* return value is a u8 which specifies the index corresponding to
|
||||
* the parent clock. This index can be applied to either the
|
||||
* .parent_names or .parents arrays. In short, this function
|
||||
* translates the parent value read from hardware into an array
|
||||
* index. Currently only called when the clock is initialized by
|
||||
* __clk_init. This callback is mandatory for clocks with
|
||||
* multiple parents. It is optional (and unnecessary) for clocks
|
||||
* with 0 or 1 parents.
|
||||
*
|
||||
* @set_parent: Change the input source of this clock; for clocks with multiple
|
||||
* possible parents specify a new parent by passing in the index
|
||||
* as a u8 corresponding to the parent in either the .parent_names
|
||||
* or .parents arrays. This function in affect translates an
|
||||
* array index into the value programmed into the hardware.
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
* possible parents specify a new parent by passing in the index
|
||||
* as a u8 corresponding to the parent in either the .parent_names
|
||||
* or .parents arrays. This function in affect translates an
|
||||
* array index into the value programmed into the hardware.
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*
|
||||
* @get_parent: Queries the hardware to determine the parent of a clock. The
|
||||
* return value is a u8 which specifies the index corresponding to
|
||||
* the parent clock. This index can be applied to either the
|
||||
* .parent_names or .parents arrays. In short, this function
|
||||
* translates the parent value read from hardware into an array
|
||||
* index. Currently only called when the clock is initialized by
|
||||
* __clk_init. This callback is mandatory for clocks with
|
||||
* multiple parents. It is optional (and unnecessary) for clocks
|
||||
* with 0 or 1 parents.
|
||||
*
|
||||
* @set_rate: Change the rate of this clock. The requested rate is specified
|
||||
* by the second argument, which should typically be the return
|
||||
@ -110,13 +111,6 @@ struct dentry;
|
||||
* which is likely helpful for most .set_rate implementation.
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*
|
||||
* @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy
|
||||
* is expressed in ppb (parts per billion). The parent accuracy is
|
||||
* an input parameter.
|
||||
* Returns the calculated accuracy. Optional - if this op is not
|
||||
* set then clock accuracy will be initialized to parent accuracy
|
||||
* or 0 (perfect clock) if clock has no parent.
|
||||
*
|
||||
* @set_rate_and_parent: Change the rate and the parent of this clock. The
|
||||
* requested rate is specified by the second argument, which
|
||||
* should typically be the return of .round_rate call. The
|
||||
@ -128,6 +122,18 @@ struct dentry;
|
||||
* separately via calls to .set_parent and .set_rate.
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*
|
||||
* @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy
|
||||
* is expressed in ppb (parts per billion). The parent accuracy is
|
||||
* an input parameter.
|
||||
* Returns the calculated accuracy. Optional - if this op is not
|
||||
* set then clock accuracy will be initialized to parent accuracy
|
||||
* or 0 (perfect clock) if clock has no parent.
|
||||
*
|
||||
* @init: Perform platform-specific initialization magic.
|
||||
* This is not not used by any of the basic clock types.
|
||||
* Please consider other ways of solving initialization problems
|
||||
* before using this callback, as its use is discouraged.
|
||||
*
|
||||
* @debug_init: Set up type-specific debugfs entries for this clock. This
|
||||
* is called once, after the debugfs directory entry for this
|
||||
* clock has been created. The dentry pointer representing that
|
||||
@ -157,15 +163,15 @@ struct clk_ops {
|
||||
void (*disable_unused)(struct clk_hw *hw);
|
||||
unsigned long (*recalc_rate)(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
long (*round_rate)(struct clk_hw *hw, unsigned long,
|
||||
unsigned long *);
|
||||
long (*round_rate)(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate);
|
||||
long (*determine_rate)(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *best_parent_rate,
|
||||
struct clk **best_parent_clk);
|
||||
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,
|
||||
unsigned long);
|
||||
int (*set_rate)(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int (*set_rate_and_parent)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate, u8 index);
|
||||
@ -254,12 +260,12 @@ void of_fixed_clk_setup(struct device_node *np);
|
||||
*
|
||||
* Flags:
|
||||
* CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to
|
||||
* enable the clock. Setting this flag does the opposite: setting the bit
|
||||
* disable the clock and clearing it enables the clock
|
||||
* enable the clock. Setting this flag does the opposite: setting the bit
|
||||
* disable the clock and clearing it enables the clock
|
||||
* CLK_GATE_HIWORD_MASK - The gate settings are only in lower 16-bit
|
||||
* of this register, and mask of gate bits are in higher 16-bit of this
|
||||
* register. While setting the gate bits, higher 16-bit should also be
|
||||
* updated to indicate changing gate bits.
|
||||
* of this register, and mask of gate bits are in higher 16-bit of this
|
||||
* register. While setting the gate bits, higher 16-bit should also be
|
||||
* updated to indicate changing gate bits.
|
||||
*/
|
||||
struct clk_gate {
|
||||
struct clk_hw hw;
|
||||
@ -298,20 +304,24 @@ struct clk_div_table {
|
||||
*
|
||||
* Flags:
|
||||
* CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the
|
||||
* register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is
|
||||
* the raw value read from the register, with the value of zero considered
|
||||
* register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is
|
||||
* the raw value read from the register, with the value of zero considered
|
||||
* invalid, unless CLK_DIVIDER_ALLOW_ZERO is set.
|
||||
* CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from
|
||||
* the hardware register
|
||||
* the hardware register
|
||||
* CLK_DIVIDER_ALLOW_ZERO - Allow zero divisors. For dividers which have
|
||||
* CLK_DIVIDER_ONE_BASED set, it is possible to end up with a zero divisor.
|
||||
* Some hardware implementations gracefully handle this case and allow a
|
||||
* zero divisor by not modifying their input clock
|
||||
* (divide by one / bypass).
|
||||
* CLK_DIVIDER_HIWORD_MASK - The divider settings are only in lower 16-bit
|
||||
* of this register, and mask of divider bits are in higher 16-bit of this
|
||||
* register. While setting the divider bits, higher 16-bit should also be
|
||||
* updated to indicate changing divider bits.
|
||||
* of this register, and mask of divider bits are in higher 16-bit of this
|
||||
* register. While setting the divider bits, higher 16-bit should also be
|
||||
* updated to indicate changing divider bits.
|
||||
* CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded
|
||||
* to the closest integer instead of the up one.
|
||||
* CLK_DIVIDER_READ_ONLY - The divider settings are preconfigured and should
|
||||
* not be changed by the clock framework.
|
||||
*/
|
||||
struct clk_divider {
|
||||
struct clk_hw hw;
|
||||
@ -327,8 +337,11 @@ struct clk_divider {
|
||||
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
|
||||
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
|
||||
#define CLK_DIVIDER_HIWORD_MASK BIT(3)
|
||||
#define CLK_DIVIDER_ROUND_CLOSEST BIT(4)
|
||||
#define CLK_DIVIDER_READ_ONLY BIT(5)
|
||||
|
||||
extern const struct clk_ops clk_divider_ops;
|
||||
extern const struct clk_ops clk_divider_ro_ops;
|
||||
struct clk *clk_register_divider(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
@ -356,9 +369,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
|
||||
* CLK_MUX_INDEX_ONE - register index starts at 1, not 0
|
||||
* CLK_MUX_INDEX_BIT - register index is a single bit (power of two)
|
||||
* CLK_MUX_HIWORD_MASK - The mux settings are only in lower 16-bit of this
|
||||
* register, and mask of mux bits are in higher 16-bit of this register.
|
||||
* While setting the mux bits, higher 16-bit should also be updated to
|
||||
* indicate changing mux bits.
|
||||
* register, and mask of mux bits are in higher 16-bit of this register.
|
||||
* While setting the mux bits, higher 16-bit should also be updated to
|
||||
* indicate changing mux bits.
|
||||
*/
|
||||
struct clk_mux {
|
||||
struct clk_hw hw;
|
||||
|
@ -1,7 +1,9 @@
|
||||
/*
|
||||
* Copyright 2013 Ideas On Board SPRL
|
||||
* Copyright 2013, 2014 Horms Solutions Ltd.
|
||||
*
|
||||
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
* Contact: Simon Horman <horms@verge.net.au>
|
||||
*
|
||||
* 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
|
||||
@ -14,6 +16,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
void r8a7779_clocks_init(u32 mode);
|
||||
void rcar_gen2_clocks_init(u32 mode);
|
||||
|
||||
#endif
|
||||
|
22
include/linux/clk/sunxi.h
Normal file
22
include/linux/clk/sunxi.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2013 - Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_CLK_SUNXI_H_
|
||||
#define __LINUX_CLK_SUNXI_H_
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
||||
void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user