提交 1a5700bc 编写于 作者: L Linus Torvalds

Merge tag 'clk-for-linus-3.16' of git://git.linaro.org/people/mike.turquette/linux into next

Pull clock framework updates from Mike Turquette:
 "The clock framework changes for 3.16 are pretty typical: mostly clock
  driver additions and fixes.  There are additions to the clock core
  code for some of the basic types (e.g. the common divider type has
  some fixes and featured added to it).

  One minor annoyance is a last-minute dependency that wasn't handled
  quite right.  Commit ba0fae3b ("clk: berlin: add core clock driver
  for BG2/BG2CD") in this pull request depends on
  include/dt-bindings/clock/berlin2.h, which is already in your tree via
  the arm-soc pull request.  Building for the berlin platform will break
  when the clk tree is built on it's own, but merged into your master
  branch everything should be fine"

* tag 'clk-for-linus-3.16' of git://git.linaro.org/people/mike.turquette/linux: (75 commits)
  mmc: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs
  clk: export __clk_round_rate for providers
  clk: versatile: free icst on error return
  clk: qcom: Return error pointers for unimplemented clocks
  clk: qcom: Support msm8974pro global clock control hardware
  clk: qcom: Properly support display clocks on msm8974
  clk: qcom: Support display RCG clocks
  clk: qcom: Return highest rate when round_rate() exceeds plan
  clk: qcom: Fix mmcc-8974's PLL configurations
  clk: qcom: Fix clk_rcg2_is_enabled() check
  clk: berlin: add core clock driver for BG2Q
  clk: berlin: add core clock driver for BG2/BG2CD
  clk: berlin: add driver for BG2x complex divider cells
  clk: berlin: add driver for BG2x simple PLLs
  clk: berlin: add driver for BG2x audio/video PLL
  clk: st: Terminate of match table
  clk/exynos4: Fix compilation warning
  ARM: shmobile: r8a7779: Add clock index macros for DT sources
  clk: divider: Fix overflow in clk_divider_bestdiv
  clk: u300: Terminate of match table
  ...
......@@ -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:
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"
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".
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:
......
* 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";
};
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,9 +4,12 @@ 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"
"qcom,gcc-msm8974pro"
"qcom,gcc-msm8974pro-ac"
- reg : shall contain base register location and length
- #clock-cells : shall contain 1
......
......@@ -11,6 +11,7 @@ Required Properties:
- compatible: Must be one of the following
- "renesas,r7s72100-mstp-clocks" for R7S72100 (RZ) MSTP gate clocks
- "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
......
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>;
};
* 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";
};
* Allwinner sunxi MMC controller
The highspeed MMC host controller on Allwinner SoCs provides an interface
for MMC, SD and SDIO types of memory cards.
Supported maximum speeds are the ones of the eMMC standard 4.5 as well
as the speed of SD standard 3.0.
Absolute maximum transfer rate is 200MB/s
Required properties:
- compatible : "allwinner,sun4i-a10-mmc" or "allwinner,sun5i-a13-mmc"
- reg : mmc controller base registers
- clocks : a list with 2 phandle + clock specifier pairs
- clock-names : must contain "ahb" and "mmc"
- interrupts : mmc controller interrupt
Optional properties:
- resets : phandle + reset specifier pair
- reset-names : must contain "ahb"
- for cd, bus-width and additional generic mmc parameters
please refer to mmc.txt within this directory
Examples:
- Within .dtsi:
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mod";
interrupts = <0 32 4>;
status = "disabled";
};
- Within dts:
mmc0: mmc@01c0f000 {
pinctrl-names = "default", "default";
pinctrl-0 = <&mmc0_pins_a>;
pinctrl-1 = <&mmc0_cd_pin_reference_design>;
bus-width = <4>;
cd-gpios = <&pio 7 1 0>; /* PH1 */
cd-inverted;
status = "okay";
};
......@@ -815,6 +815,11 @@ F: arch/arm/boot/dts/at91*.dtsi
F: arch/arm/boot/dts/sama*.dts
F: arch/arm/boot/dts/sama*.dtsi
ARM/ATMEL AT91 Clock Support
M: Boris Brezillon <boris.brezillon@free-electrons.com>
S: Maintained
F: drivers/clk/at91
ARM/CALXEDA HIGHBANK ARCHITECTURE
M: Rob Herring <robh@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
......
......@@ -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>;
/*
* Fixed clocks are defined before CCUs whose
* clocks may depend on them.
*/
ref_32k_clk: ref_32k {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32768>;
};
bsc3_clk: bsc3 {
compatible = "fixed-clock";
clock-frequency = <13000000>;
bbl_32k_clk: bbl_32k {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32768>;
};
bsc4_clk: bsc4 {
ref_13m_clk: ref_13m {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <13000000>;
#clock-cells = <0>;
};
pmu_bsc_clk: pmu_bsc {
var_13m_clk: var_13m {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <13000000>;
#clock-cells = <0>;
};
hub_timer_clk: hub_timer {
compatible = "fixed-clock";
clock-frequency = <32768>;
dft_19_5m_clk: dft_19_5m {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <19500000>;
};
pwm_clk: pwm {
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>;
compatible = "fixed-clock";
clock-frequency = <52000000>;
};
sdio3_clk: sdio3 {
compatible = "fixed-clock";
clock-frequency = <48000000>;
usb_otg_ahb_clk: usb_otg_ahb {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <52000000>;
};
sdio4_clk: sdio4 {
compatible = "fixed-clock";
clock-frequency = <48000000>;
ref_96m_clk: ref_96m {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <96000000>;
};
tmon_1m_clk: tmon_1m {
compatible = "fixed-clock";
clock-frequency = <1000000>;
var_96m_clk: var_96m {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <96000000>;
};
uartb_clk: uartb {
compatible = "fixed-clock";
clock-frequency = <13000000>;
ref_104m_clk: ref_104m {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <104000000>;
};
uartb2_clk: uartb2 {
compatible = "fixed-clock";
clock-frequency = <13000000>;
var_104m_clk: var_104m {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <104000000>;
};
uartb3_clk: uartb3 {
compatible = "fixed-clock";
clock-frequency = <13000000>;
ref_156m_clk: ref_156m {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <156000000>;
};
usb_otg_ahb_clk: usb_otg_ahb {
compatible = "fixed-clock";
clock-frequency = <52000000>;
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";
};
};
......
......@@ -58,12 +58,12 @@ config COMMON_CLK_SI570
clock generators.
config COMMON_CLK_S2MPS11
tristate "Clock driver for S2MPS11/S5M8767 MFD"
tristate "Clock driver for S2MPS1X/S5M8767 MFD"
depends on MFD_SEC_CORE
---help---
This driver supports S2MPS11/S5M8767 crystal oscillator clock. These
multi-function devices have 3 fixed-rate oscillators, clocked at
32KHz each.
This driver supports S2MPS11/S2MPS14/S5M8767 crystal oscillator
clock. These multi-function devices have two (S2MPS14) or three
(S2MPS11, S5M8767) fixed-rate oscillators, clocked at 32KHz each.
config CLK_TWL6040
tristate "External McPDM functional clock from twl6040"
......
......@@ -13,6 +13,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.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
......@@ -32,8 +33,10 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/
obj-$(CONFIG_ARCH_BERLIN) += berlin/
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.
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
/*
* 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)
......@@ -849,6 +1031,58 @@ static long kona_peri_clk_round_rate(struct clk_hw *hw, unsigned long rate,
rate ? rate : 1, *parent_rate, NULL);
}
static long kona_peri_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate, struct clk **best_parent)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
struct clk *clk = hw->clk;
struct clk *current_parent;
unsigned long parent_rate;
unsigned long best_delta;
unsigned long best_rate;
u32 parent_count;
u32 which;
/*
* If there is no other parent to choose, use the current one.
* Note: We don't honor (or use) CLK_SET_RATE_NO_REPARENT.
*/
WARN_ON_ONCE(bcm_clk->init_data.flags & CLK_SET_RATE_NO_REPARENT);
parent_count = (u32)bcm_clk->init_data.num_parents;
if (parent_count < 2)
return kona_peri_clk_round_rate(hw, rate, best_parent_rate);
/* Unless we can do better, stick with current parent */
current_parent = clk_get_parent(clk);
parent_rate = __clk_get_rate(current_parent);
best_rate = kona_peri_clk_round_rate(hw, rate, &parent_rate);
best_delta = abs(best_rate - rate);
/* Check whether any other parent clock can produce a better result */
for (which = 0; which < parent_count; which++) {
struct clk *parent = clk_get_parent_by_index(clk, which);
unsigned long delta;
unsigned long other_rate;
BUG_ON(!parent);
if (parent == current_parent)
continue;
/* We don't support CLK_SET_RATE_PARENT */
parent_rate = __clk_get_rate(parent);
other_rate = kona_peri_clk_round_rate(hw, rate, &parent_rate);
delta = abs(other_rate - rate);
if (delta < best_delta) {
best_delta = delta;
best_rate = other_rate;
*best_parent = parent;
*best_parent_rate = parent_rate;
}
}
return best_rate;
}
static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
......@@ -872,12 +1106,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 +1171,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;
......@@ -950,7 +1187,7 @@ struct clk_ops kona_peri_clk_ops = {
.disable = kona_peri_clk_disable,
.is_enabled = kona_peri_clk_is_enabled,
.recalc_rate = kona_peri_clk_recalc_rate,
.round_rate = kona_peri_clk_round_rate,
.determine_rate = kona_peri_clk_determine_rate,
.set_parent = kona_peri_clk_set_parent,
.get_parent = kona_peri_clk_get_parent,
.set_rate = kona_peri_clk_set_rate,
......@@ -961,15 +1198,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 +1260,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,14 +414,92 @@ struct kona_clk {
#define to_kona_clk(_hw) \
container_of(_hw, struct kona_clk, hw)
/* Exported globals */
/* 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 }
extern struct clk_ops kona_peri_clk_ops;
/*
* 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 */
};
/* Help functions */
/* 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 */
#define PERI_CLK_SETUP(clks, ccu, id, name) \
clks[id] = kona_clk_setup(ccu, #name, bcm_clk_peri, &name ## _data)
extern struct clk_ops kona_peri_clk_ops;
/* Externally visible functions */
......@@ -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 */
obj-y += berlin2-avpll.o berlin2-pll.o berlin2-div.o
obj-$(CONFIG_MACH_BERLIN_BG2) += bg2.o
obj-$(CONFIG_MACH_BERLIN_BG2CD) += bg2.o
obj-$(CONFIG_MACH_BERLIN_BG2Q) += bg2q.o
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include "berlin2-avpll.h"
/*
* Berlin2 SoCs comprise up to two PLLs called AVPLL built upon a
* VCO with 8 channels each, channel 8 is the odd-one-out and does
* not provide mul/div.
*
* Unfortunately, its registers are not named but just numbered. To
* get in at least some kind of structure, we split each AVPLL into
* the VCOs and each channel into separate clock drivers.
*
* Also, here and there the VCO registers are a bit different with
* respect to bit shifts. Make sure to add a comment for those.
*/
#define NUM_CHANNELS 8
#define AVPLL_CTRL(x) ((x) * 0x4)
#define VCO_CTRL0 AVPLL_CTRL(0)
/* BG2/BG2CDs VCO_B has an additional shift of 4 for its VCO_CTRL0 reg */
#define VCO_RESET BIT(0)
#define VCO_POWERUP BIT(1)
#define VCO_INTERPOL_SHIFT 2
#define VCO_INTERPOL_MASK (0xf << VCO_INTERPOL_SHIFT)
#define VCO_REG1V45_SEL_SHIFT 6
#define VCO_REG1V45_SEL(x) ((x) << VCO_REG1V45_SEL_SHIFT)
#define VCO_REG1V45_SEL_1V40 VCO_REG1V45_SEL(0)
#define VCO_REG1V45_SEL_1V45 VCO_REG1V45_SEL(1)
#define VCO_REG1V45_SEL_1V50 VCO_REG1V45_SEL(2)
#define VCO_REG1V45_SEL_1V55 VCO_REG1V45_SEL(3)
#define VCO_REG1V45_SEL_MASK VCO_REG1V45_SEL(3)
#define VCO_REG0V9_SEL_SHIFT 8
#define VCO_REG0V9_SEL_MASK (0xf << VCO_REG0V9_SEL_SHIFT)
#define VCO_VTHCAL_SHIFT 12
#define VCO_VTHCAL(x) ((x) << VCO_VTHCAL_SHIFT)
#define VCO_VTHCAL_0V90 VCO_VTHCAL(0)
#define VCO_VTHCAL_0V95 VCO_VTHCAL(1)
#define VCO_VTHCAL_1V00 VCO_VTHCAL(2)
#define VCO_VTHCAL_1V05 VCO_VTHCAL(3)
#define VCO_VTHCAL_MASK VCO_VTHCAL(3)
#define VCO_KVCOEXT_SHIFT 14
#define VCO_KVCOEXT_MASK (0x3 << VCO_KVCOEXT_SHIFT)
#define VCO_KVCOEXT_ENABLE BIT(17)
#define VCO_V2IEXT_SHIFT 18
#define VCO_V2IEXT_MASK (0xf << VCO_V2IEXT_SHIFT)
#define VCO_V2IEXT_ENABLE BIT(22)
#define VCO_SPEED_SHIFT 23
#define VCO_SPEED(x) ((x) << VCO_SPEED_SHIFT)
#define VCO_SPEED_1G08_1G21 VCO_SPEED(0)
#define VCO_SPEED_1G21_1G40 VCO_SPEED(1)
#define VCO_SPEED_1G40_1G61 VCO_SPEED(2)
#define VCO_SPEED_1G61_1G86 VCO_SPEED(3)
#define VCO_SPEED_1G86_2G00 VCO_SPEED(4)
#define VCO_SPEED_2G00_2G22 VCO_SPEED(5)
#define VCO_SPEED_2G22 VCO_SPEED(6)
#define VCO_SPEED_MASK VCO_SPEED(0x7)
#define VCO_CLKDET_ENABLE BIT(26)
#define VCO_CTRL1 AVPLL_CTRL(1)
#define VCO_REFDIV_SHIFT 0
#define VCO_REFDIV(x) ((x) << VCO_REFDIV_SHIFT)
#define VCO_REFDIV_1 VCO_REFDIV(0)
#define VCO_REFDIV_2 VCO_REFDIV(1)
#define VCO_REFDIV_4 VCO_REFDIV(2)
#define VCO_REFDIV_3 VCO_REFDIV(3)
#define VCO_REFDIV_MASK VCO_REFDIV(0x3f)
#define VCO_FBDIV_SHIFT 6
#define VCO_FBDIV(x) ((x) << VCO_FBDIV_SHIFT)
#define VCO_FBDIV_MASK VCO_FBDIV(0xff)
#define VCO_ICP_SHIFT 14
/* PLL Charge Pump Current = 10uA * (x + 1) */
#define VCO_ICP(x) ((x) << VCO_ICP_SHIFT)
#define VCO_ICP_MASK VCO_ICP(0xf)
#define VCO_LOAD_CAP BIT(18)
#define VCO_CALIBRATION_START BIT(19)
#define VCO_FREQOFFSETn(x) AVPLL_CTRL(3 + (x))
#define VCO_FREQOFFSET_MASK 0x7ffff
#define VCO_CTRL10 AVPLL_CTRL(10)
#define VCO_POWERUP_CH1 BIT(20)
#define VCO_CTRL11 AVPLL_CTRL(11)
#define VCO_CTRL12 AVPLL_CTRL(12)
#define VCO_CTRL13 AVPLL_CTRL(13)
#define VCO_CTRL14 AVPLL_CTRL(14)
#define VCO_CTRL15 AVPLL_CTRL(15)
#define VCO_SYNC1n(x) AVPLL_CTRL(15 + (x))
#define VCO_SYNC1_MASK 0x1ffff
#define VCO_SYNC2n(x) AVPLL_CTRL(23 + (x))
#define VCO_SYNC2_MASK 0x1ffff
#define VCO_CTRL30 AVPLL_CTRL(30)
#define VCO_DPLL_CH1_ENABLE BIT(17)
struct berlin2_avpll_vco {
struct clk_hw hw;
void __iomem *base;
u8 flags;
};
#define to_avpll_vco(hw) container_of(hw, struct berlin2_avpll_vco, hw)
static int berlin2_avpll_vco_is_enabled(struct clk_hw *hw)
{
struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
u32 reg;
reg = readl_relaxed(vco->base + VCO_CTRL0);
if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
reg >>= 4;
return !!(reg & VCO_POWERUP);
}
static int berlin2_avpll_vco_enable(struct clk_hw *hw)
{
struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
u32 reg;
reg = readl_relaxed(vco->base + VCO_CTRL0);
if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
reg |= VCO_POWERUP << 4;
else
reg |= VCO_POWERUP;
writel_relaxed(reg, vco->base + VCO_CTRL0);
return 0;
}
static void berlin2_avpll_vco_disable(struct clk_hw *hw)
{
struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
u32 reg;
reg = readl_relaxed(vco->base + VCO_CTRL0);
if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
reg &= ~(VCO_POWERUP << 4);
else
reg &= ~VCO_POWERUP;
writel_relaxed(reg, vco->base + VCO_CTRL0);
}
static u8 vco_refdiv[] = { 1, 2, 4, 3 };
static unsigned long
berlin2_avpll_vco_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
u32 reg, refdiv, fbdiv;
u64 freq = parent_rate;
/* AVPLL VCO frequency: Fvco = (Fref / refdiv) * fbdiv */
reg = readl_relaxed(vco->base + VCO_CTRL1);
refdiv = (reg & VCO_REFDIV_MASK) >> VCO_REFDIV_SHIFT;
refdiv = vco_refdiv[refdiv];
fbdiv = (reg & VCO_FBDIV_MASK) >> VCO_FBDIV_SHIFT;
freq *= fbdiv;
do_div(freq, refdiv);
return (unsigned long)freq;
}
static const struct clk_ops berlin2_avpll_vco_ops = {
.is_enabled = berlin2_avpll_vco_is_enabled,
.enable = berlin2_avpll_vco_enable,
.disable = berlin2_avpll_vco_disable,
.recalc_rate = berlin2_avpll_vco_recalc_rate,
};
struct clk * __init berlin2_avpll_vco_register(void __iomem *base,
const char *name, const char *parent_name,
u8 vco_flags, unsigned long flags)
{
struct berlin2_avpll_vco *vco;
struct clk_init_data init;
vco = kzalloc(sizeof(*vco), GFP_KERNEL);
if (!vco)
return ERR_PTR(-ENOMEM);
vco->base = base;
vco->flags = vco_flags;
vco->hw.init = &init;
init.name = name;
init.ops = &berlin2_avpll_vco_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = flags;
return clk_register(NULL, &vco->hw);
}
struct berlin2_avpll_channel {
struct clk_hw hw;
void __iomem *base;
u8 flags;
u8 index;
};
#define to_avpll_channel(hw) container_of(hw, struct berlin2_avpll_channel, hw)
static int berlin2_avpll_channel_is_enabled(struct clk_hw *hw)
{
struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
u32 reg;
if (ch->index == 7)
return 1;
reg = readl_relaxed(ch->base + VCO_CTRL10);
reg &= VCO_POWERUP_CH1 << ch->index;
return !!reg;
}
static int berlin2_avpll_channel_enable(struct clk_hw *hw)
{
struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
u32 reg;
reg = readl_relaxed(ch->base + VCO_CTRL10);
reg |= VCO_POWERUP_CH1 << ch->index;
writel_relaxed(reg, ch->base + VCO_CTRL10);
return 0;
}
static void berlin2_avpll_channel_disable(struct clk_hw *hw)
{
struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
u32 reg;
reg = readl_relaxed(ch->base + VCO_CTRL10);
reg &= ~(VCO_POWERUP_CH1 << ch->index);
writel_relaxed(reg, ch->base + VCO_CTRL10);
}
static const u8 div_hdmi[] = { 1, 2, 4, 6 };
static const u8 div_av1[] = { 1, 2, 5, 5 };
static unsigned long
berlin2_avpll_channel_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
u32 reg, div_av2, div_av3, divider = 1;
u64 freq = parent_rate;
reg = readl_relaxed(ch->base + VCO_CTRL30);
if ((reg & (VCO_DPLL_CH1_ENABLE << ch->index)) == 0)
goto skip_div;
/*
* Fch = (Fref * sync2) /
* (sync1 * div_hdmi * div_av1 * div_av2 * div_av3)
*/
reg = readl_relaxed(ch->base + VCO_SYNC1n(ch->index));
/* BG2/BG2CDs SYNC1 reg on AVPLL_B channel 1 is shifted by 4 */
if (ch->flags & BERLIN2_AVPLL_BIT_QUIRK && ch->index == 0)
reg >>= 4;
divider = reg & VCO_SYNC1_MASK;
reg = readl_relaxed(ch->base + VCO_SYNC2n(ch->index));
freq *= reg & VCO_SYNC2_MASK;
/* Channel 8 has no dividers */
if (ch->index == 7)
goto skip_div;
/*
* HDMI divider start at VCO_CTRL11, bit 7; MSB is enable, lower 2 bit
* determine divider.
*/
reg = readl_relaxed(ch->base + VCO_CTRL11) >> 7;
reg = (reg >> (ch->index * 3));
if (reg & BIT(2))
divider *= div_hdmi[reg & 0x3];
/*
* AV1 divider start at VCO_CTRL11, bit 28; MSB is enable, lower 2 bit
* determine divider.
*/
if (ch->index == 0) {
reg = readl_relaxed(ch->base + VCO_CTRL11);
reg >>= 28;
} else {
reg = readl_relaxed(ch->base + VCO_CTRL12);
reg >>= (ch->index-1) * 3;
}
if (reg & BIT(2))
divider *= div_av1[reg & 0x3];
/*
* AV2 divider start at VCO_CTRL12, bit 18; each 7 bits wide,
* zero is not a valid value.
*/
if (ch->index < 2) {
reg = readl_relaxed(ch->base + VCO_CTRL12);
reg >>= 18 + (ch->index * 7);
} else if (ch->index < 7) {
reg = readl_relaxed(ch->base + VCO_CTRL13);
reg >>= (ch->index - 2) * 7;
} else {
reg = readl_relaxed(ch->base + VCO_CTRL14);
}
div_av2 = reg & 0x7f;
if (div_av2)
divider *= div_av2;
/*
* AV3 divider start at VCO_CTRL14, bit 7; each 4 bits wide.
* AV2/AV3 form a fractional divider, where only specfic values for AV3
* are allowed. AV3 != 0 divides by AV2/2, AV3=0 is bypass.
*/
if (ch->index < 6) {
reg = readl_relaxed(ch->base + VCO_CTRL14);
reg >>= 7 + (ch->index * 4);
} else {
reg = readl_relaxed(ch->base + VCO_CTRL15);
}
div_av3 = reg & 0xf;
if (div_av2 && div_av3)
freq *= 2;
skip_div:
do_div(freq, divider);
return (unsigned long)freq;
}
static const struct clk_ops berlin2_avpll_channel_ops = {
.is_enabled = berlin2_avpll_channel_is_enabled,
.enable = berlin2_avpll_channel_enable,
.disable = berlin2_avpll_channel_disable,
.recalc_rate = berlin2_avpll_channel_recalc_rate,
};
/*
* Another nice quirk:
* On some production SoCs, AVPLL channels are scrambled with respect
* to the channel numbering in the registers but still referenced by
* their original channel numbers. We deal with it by having a flag
* and a translation table for the index.
*/
static const u8 quirk_index[] __initconst = { 0, 6, 5, 4, 3, 2, 1, 7 };
struct clk * __init berlin2_avpll_channel_register(void __iomem *base,
const char *name, u8 index, const char *parent_name,
u8 ch_flags, unsigned long flags)
{
struct berlin2_avpll_channel *ch;
struct clk_init_data init;
ch = kzalloc(sizeof(*ch), GFP_KERNEL);
if (!ch)
return ERR_PTR(-ENOMEM);
ch->base = base;
if (ch_flags & BERLIN2_AVPLL_SCRAMBLE_QUIRK)
ch->index = quirk_index[index];
else
ch->index = index;
ch->flags = ch_flags;
ch->hw.init = &init;
init.name = name;
init.ops = &berlin2_avpll_channel_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = flags;
return clk_register(NULL, &ch->hw);
}
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BERLIN2_AVPLL_H
#define __BERLIN2_AVPLL_H
struct clk;
#define BERLIN2_AVPLL_BIT_QUIRK BIT(0)
#define BERLIN2_AVPLL_SCRAMBLE_QUIRK BIT(1)
struct clk * __init
berlin2_avpll_vco_register(void __iomem *base, const char *name,
const char *parent_name, u8 vco_flags, unsigned long flags);
struct clk * __init
berlin2_avpll_channel_register(void __iomem *base, const char *name,
u8 index, const char *parent_name, u8 ch_flags,
unsigned long flags);
#endif /* __BERLIN2_AVPLL_H */
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "berlin2-div.h"
/*
* Clock dividers in Berlin2 SoCs comprise a complex cell to select
* input pll and divider. The virtual structure as it is used in Marvell
* BSP code can be seen as:
*
* +---+
* pll0 --------------->| 0 | +---+
* +---+ |(B)|--+--------------->| 0 | +---+
* pll1.0 -->| 0 | +-->| 1 | | +--------+ |(E)|----->| 0 | +---+
* pll1.1 -->| 1 | | +---+ +-->|(C) 1:M |-->| 1 | |(F)|-->|(G)|->
* ... -->|(A)|--+ | +--------+ +---+ +-->| 1 | +---+
* ... -->| | +-->|(D) 1:3 |----------+ +---+
* pll1.N -->| N | +---------
* +---+
*
* (A) input pll clock mux controlled by <PllSelect[1:n]>
* (B) input pll bypass mux controlled by <PllSwitch>
* (C) programmable clock divider controlled by <Select[1:n]>
* (D) constant div-by-3 clock divider
* (E) programmable clock divider bypass controlled by <Switch>
* (F) constant div-by-3 clock mux controlled by <D3Switch>
* (G) clock gate controlled by <Enable>
*
* For whatever reason, above control signals come in two flavors:
* - single register dividers with all bits in one register
* - shared register dividers with bits spread over multiple registers
* (including signals for the same cell spread over consecutive registers)
*
* Also, clock gate and pll mux is not available on every div cell, so
* we have to deal with those, too. We reuse common clock composite driver
* for it.
*/
#define PLL_SELECT_MASK 0x7
#define DIV_SELECT_MASK 0x7
struct berlin2_div {
struct clk_hw hw;
void __iomem *base;
struct berlin2_div_map map;
spinlock_t *lock;
};
#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw)
static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
static int berlin2_div_is_enabled(struct clk_hw *hw)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
if (div->lock)
spin_lock(div->lock);
reg = readl_relaxed(div->base + map->gate_offs);
reg >>= map->gate_shift;
if (div->lock)
spin_unlock(div->lock);
return (reg & 0x1);
}
static int berlin2_div_enable(struct clk_hw *hw)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
if (div->lock)
spin_lock(div->lock);
reg = readl_relaxed(div->base + map->gate_offs);
reg |= BIT(map->gate_shift);
writel_relaxed(reg, div->base + map->gate_offs);
if (div->lock)
spin_unlock(div->lock);
return 0;
}
static void berlin2_div_disable(struct clk_hw *hw)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
if (div->lock)
spin_lock(div->lock);
reg = readl_relaxed(div->base + map->gate_offs);
reg &= ~BIT(map->gate_shift);
writel_relaxed(reg, div->base + map->gate_offs);
if (div->lock)
spin_unlock(div->lock);
}
static int berlin2_div_set_parent(struct clk_hw *hw, u8 index)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
if (div->lock)
spin_lock(div->lock);
/* index == 0 is PLL_SWITCH */
reg = readl_relaxed(div->base + map->pll_switch_offs);
if (index == 0)
reg &= ~BIT(map->pll_switch_shift);
else
reg |= BIT(map->pll_switch_shift);
writel_relaxed(reg, div->base + map->pll_switch_offs);
/* index > 0 is PLL_SELECT */
if (index > 0) {
reg = readl_relaxed(div->base + map->pll_select_offs);
reg &= ~(PLL_SELECT_MASK << map->pll_select_shift);
reg |= (index - 1) << map->pll_select_shift;
writel_relaxed(reg, div->base + map->pll_select_offs);
}
if (div->lock)
spin_unlock(div->lock);
return 0;
}
static u8 berlin2_div_get_parent(struct clk_hw *hw)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
u8 index = 0;
if (div->lock)
spin_lock(div->lock);
/* PLL_SWITCH == 0 is index 0 */
reg = readl_relaxed(div->base + map->pll_switch_offs);
reg &= BIT(map->pll_switch_shift);
if (reg) {
reg = readl_relaxed(div->base + map->pll_select_offs);
reg >>= map->pll_select_shift;
reg &= PLL_SELECT_MASK;
index = 1 + reg;
}
if (div->lock)
spin_unlock(div->lock);
return index;
}
static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 divsw, div3sw, divider = 1;
if (div->lock)
spin_lock(div->lock);
divsw = readl_relaxed(div->base + map->div_switch_offs) &
(1 << map->div_switch_shift);
div3sw = readl_relaxed(div->base + map->div3_switch_offs) &
(1 << map->div3_switch_shift);
/* constant divide-by-3 (dominant) */
if (div3sw != 0) {
divider = 3;
/* divider can be bypassed with DIV_SWITCH == 0 */
} else if (divsw == 0) {
divider = 1;
/* clock divider determined by DIV_SELECT */
} else {
u32 reg;
reg = readl_relaxed(div->base + map->div_select_offs);
reg >>= map->div_select_shift;
reg &= DIV_SELECT_MASK;
divider = clk_div[reg];
}
if (div->lock)
spin_unlock(div->lock);
return parent_rate / divider;
}
static const struct clk_ops berlin2_div_rate_ops = {
.recalc_rate = berlin2_div_recalc_rate,
};
static const struct clk_ops berlin2_div_gate_ops = {
.is_enabled = berlin2_div_is_enabled,
.enable = berlin2_div_enable,
.disable = berlin2_div_disable,
};
static const struct clk_ops berlin2_div_mux_ops = {
.set_parent = berlin2_div_set_parent,
.get_parent = berlin2_div_get_parent,
};
struct clk * __init
berlin2_div_register(const struct berlin2_div_map *map,
void __iomem *base, const char *name, u8 div_flags,
const char **parent_names, int num_parents,
unsigned long flags, spinlock_t *lock)
{
const struct clk_ops *mux_ops = &berlin2_div_mux_ops;
const struct clk_ops *rate_ops = &berlin2_div_rate_ops;
const struct clk_ops *gate_ops = &berlin2_div_gate_ops;
struct berlin2_div *div;
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
/* copy div_map to allow __initconst */
memcpy(&div->map, map, sizeof(*map));
div->base = base;
div->lock = lock;
if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0)
gate_ops = NULL;
if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
mux_ops = NULL;
return clk_register_composite(NULL, name, parent_names, num_parents,
&div->hw, mux_ops, &div->hw, rate_ops,
&div->hw, gate_ops, flags);
}
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BERLIN2_DIV_H
#define __BERLIN2_DIV_H
struct clk;
#define BERLIN2_DIV_HAS_GATE BIT(0)
#define BERLIN2_DIV_HAS_MUX BIT(1)
#define BERLIN2_PLL_SELECT(_off, _sh) \
.pll_select_offs = _off, \
.pll_select_shift = _sh
#define BERLIN2_PLL_SWITCH(_off, _sh) \
.pll_switch_offs = _off, \
.pll_switch_shift = _sh
#define BERLIN2_DIV_SELECT(_off, _sh) \
.div_select_offs = _off, \
.div_select_shift = _sh
#define BERLIN2_DIV_SWITCH(_off, _sh) \
.div_switch_offs = _off, \
.div_switch_shift = _sh
#define BERLIN2_DIV_D3SWITCH(_off, _sh) \
.div3_switch_offs = _off, \
.div3_switch_shift = _sh
#define BERLIN2_DIV_GATE(_off, _sh) \
.gate_offs = _off, \
.gate_shift = _sh
#define BERLIN2_SINGLE_DIV(_off) \
BERLIN2_DIV_GATE(_off, 0), \
BERLIN2_PLL_SELECT(_off, 1), \
BERLIN2_PLL_SWITCH(_off, 4), \
BERLIN2_DIV_SWITCH(_off, 5), \
BERLIN2_DIV_D3SWITCH(_off, 6), \
BERLIN2_DIV_SELECT(_off, 7)
struct berlin2_div_map {
u16 pll_select_offs;
u16 pll_switch_offs;
u16 div_select_offs;
u16 div_switch_offs;
u16 div3_switch_offs;
u16 gate_offs;
u8 pll_select_shift;
u8 pll_switch_shift;
u8 div_select_shift;
u8 div_switch_shift;
u8 div3_switch_shift;
u8 gate_shift;
};
struct berlin2_div_data {
const char *name;
const u8 *parent_ids;
int num_parents;
unsigned long flags;
struct berlin2_div_map map;
u8 div_flags;
};
struct clk * __init
berlin2_div_register(const struct berlin2_div_map *map,
void __iomem *base, const char *name, u8 div_flags,
const char **parent_names, int num_parents,
unsigned long flags, spinlock_t *lock);
#endif /* __BERLIN2_DIV_H */
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <asm/div64.h>
#include "berlin2-div.h"
struct berlin2_pll_map {
const u8 vcodiv[16];
u8 mult;
u8 fbdiv_shift;
u8 rfdiv_shift;
u8 divsel_shift;
};
struct berlin2_pll {
struct clk_hw hw;
void __iomem *base;
struct berlin2_pll_map map;
};
#define to_berlin2_pll(hw) container_of(hw, struct berlin2_pll, hw)
#define SPLL_CTRL0 0x00
#define SPLL_CTRL1 0x04
#define SPLL_CTRL2 0x08
#define SPLL_CTRL3 0x0c
#define SPLL_CTRL4 0x10
#define FBDIV_MASK 0x1ff
#define RFDIV_MASK 0x1f
#define DIVSEL_MASK 0xf
/*
* The output frequency formula for the pll is:
* clkout = fbdiv / refdiv * parent / vcodiv
*/
static unsigned long
berlin2_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct berlin2_pll *pll = to_berlin2_pll(hw);
struct berlin2_pll_map *map = &pll->map;
u32 val, fbdiv, rfdiv, vcodivsel, vcodiv;
u64 rate = parent_rate;
val = readl_relaxed(pll->base + SPLL_CTRL0);
fbdiv = (val >> map->fbdiv_shift) & FBDIV_MASK;
rfdiv = (val >> map->rfdiv_shift) & RFDIV_MASK;
if (rfdiv == 0) {
pr_warn("%s has zero rfdiv\n", __clk_get_name(hw->clk));
rfdiv = 1;
}
val = readl_relaxed(pll->base + SPLL_CTRL1);
vcodivsel = (val >> map->divsel_shift) & DIVSEL_MASK;
vcodiv = map->vcodiv[vcodivsel];
if (vcodiv == 0) {
pr_warn("%s has zero vcodiv (index %d)\n",
__clk_get_name(hw->clk), vcodivsel);
vcodiv = 1;
}
rate *= fbdiv * map->mult;
do_div(rate, rfdiv * vcodiv);
return (unsigned long)rate;
}
static const struct clk_ops berlin2_pll_ops = {
.recalc_rate = berlin2_pll_recalc_rate,
};
struct clk * __init
berlin2_pll_register(const struct berlin2_pll_map *map,
void __iomem *base, const char *name,
const char *parent_name, unsigned long flags)
{
struct clk_init_data init;
struct berlin2_pll *pll;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
/* copy pll_map to allow __initconst */
memcpy(&pll->map, map, sizeof(*map));
pll->base = base;
pll->hw.init = &init;
init.name = name;
init.ops = &berlin2_pll_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = flags;
return clk_register(NULL, &pll->hw);
}
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BERLIN2_PLL_H
#define __BERLIN2_PLL_H
struct clk;
struct berlin2_pll_map {
const u8 vcodiv[16];
u8 mult;
u8 fbdiv_shift;
u8 rfdiv_shift;
u8 divsel_shift;
};
struct clk * __init
berlin2_pll_register(const struct berlin2_pll_map *map,
void __iomem *base, const char *name,
const char *parent_name, unsigned long flags);
#endif /* __BERLIN2_PLL_H */
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <dt-bindings/clock/berlin2.h>
#include "berlin2-avpll.h"
#include "berlin2-div.h"
#include "berlin2-pll.h"
#include "common.h"
#define REG_PINMUX0 0x0000
#define REG_PINMUX1 0x0004
#define REG_SYSPLLCTL0 0x0014
#define REG_SYSPLLCTL4 0x0024
#define REG_MEMPLLCTL0 0x0028
#define REG_MEMPLLCTL4 0x0038
#define REG_CPUPLLCTL0 0x003c
#define REG_CPUPLLCTL4 0x004c
#define REG_AVPLLCTL0 0x0050
#define REG_AVPLLCTL31 0x00cc
#define REG_AVPLLCTL62 0x0148
#define REG_PLLSTATUS 0x014c
#define REG_CLKENABLE 0x0150
#define REG_CLKSELECT0 0x0154
#define REG_CLKSELECT1 0x0158
#define REG_CLKSELECT2 0x015c
#define REG_CLKSELECT3 0x0160
#define REG_CLKSWITCH0 0x0164
#define REG_CLKSWITCH1 0x0168
#define REG_RESET_TRIGGER 0x0178
#define REG_RESET_STATUS0 0x017c
#define REG_RESET_STATUS1 0x0180
#define REG_SW_GENERIC0 0x0184
#define REG_SW_GENERIC3 0x0190
#define REG_PRODUCTID 0x01cc
#define REG_PRODUCTID_EXT 0x01d0
#define REG_GFX3DCORE_CLKCTL 0x022c
#define REG_GFX3DSYS_CLKCTL 0x0230
#define REG_ARC_CLKCTL 0x0234
#define REG_VIP_CLKCTL 0x0238
#define REG_SDIO0XIN_CLKCTL 0x023c
#define REG_SDIO1XIN_CLKCTL 0x0240
#define REG_GFX3DEXTRA_CLKCTL 0x0244
#define REG_GFX3D_RESET 0x0248
#define REG_GC360_CLKCTL 0x024c
#define REG_SDIO_DLLMST_CLKCTL 0x0250
/*
* BG2/BG2CD SoCs have the following audio/video I/O units:
*
* audiohd: HDMI TX audio
* audio0: 7.1ch TX
* audio1: 2ch TX
* audio2: 2ch RX
* audio3: SPDIF TX
* video0: HDMI video
* video1: Secondary video
* video2: SD auxiliary video
*
* There are no external audio clocks (ACLKI0, ACLKI1) and
* only one external video clock (VCLKI0).
*
* Currently missing bits and pieces:
* - audio_fast_pll is unknown
* - audiohd_pll is unknown
* - video0_pll is unknown
* - audio[023], audiohd parent pll is assumed to be audio_fast_pll
*
*/
#define MAX_CLKS 41
static struct clk *clks[MAX_CLKS];
static struct clk_onecell_data clk_data;
static DEFINE_SPINLOCK(lock);
static void __iomem *gbase;
enum {
REFCLK, VIDEO_EXT0,
SYSPLL, MEMPLL, CPUPLL,
AVPLL_A1, AVPLL_A2, AVPLL_A3, AVPLL_A4,
AVPLL_A5, AVPLL_A6, AVPLL_A7, AVPLL_A8,
AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4,
AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8,
AUDIO1_PLL, AUDIO_FAST_PLL,
VIDEO0_PLL, VIDEO0_IN,
VIDEO1_PLL, VIDEO1_IN,
VIDEO2_PLL, VIDEO2_IN,
};
static const char *clk_names[] = {
[REFCLK] = "refclk",
[VIDEO_EXT0] = "video_ext0",
[SYSPLL] = "syspll",
[MEMPLL] = "mempll",
[CPUPLL] = "cpupll",
[AVPLL_A1] = "avpll_a1",
[AVPLL_A2] = "avpll_a2",
[AVPLL_A3] = "avpll_a3",
[AVPLL_A4] = "avpll_a4",
[AVPLL_A5] = "avpll_a5",
[AVPLL_A6] = "avpll_a6",
[AVPLL_A7] = "avpll_a7",
[AVPLL_A8] = "avpll_a8",
[AVPLL_B1] = "avpll_b1",
[AVPLL_B2] = "avpll_b2",
[AVPLL_B3] = "avpll_b3",
[AVPLL_B4] = "avpll_b4",
[AVPLL_B5] = "avpll_b5",
[AVPLL_B6] = "avpll_b6",
[AVPLL_B7] = "avpll_b7",
[AVPLL_B8] = "avpll_b8",
[AUDIO1_PLL] = "audio1_pll",
[AUDIO_FAST_PLL] = "audio_fast_pll",
[VIDEO0_PLL] = "video0_pll",
[VIDEO0_IN] = "video0_in",
[VIDEO1_PLL] = "video1_pll",
[VIDEO1_IN] = "video1_in",
[VIDEO2_PLL] = "video2_pll",
[VIDEO2_IN] = "video2_in",
};
static const struct berlin2_pll_map bg2_pll_map __initconst = {
.vcodiv = {10, 15, 20, 25, 30, 40, 50, 60, 80},
.mult = 10,
.fbdiv_shift = 6,
.rfdiv_shift = 1,
.divsel_shift = 7,
};
static const u8 default_parent_ids[] = {
SYSPLL, AVPLL_B4, AVPLL_A5, AVPLL_B6, AVPLL_B7, SYSPLL
};
static const struct berlin2_div_data bg2_divs[] __initconst = {
{
.name = "sys",
.parent_ids = (const u8 []){
SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL
},
.num_parents = 6,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = CLK_IGNORE_UNUSED,
},
{
.name = "cpu",
.parent_ids = (const u8 []){
CPUPLL, MEMPLL, MEMPLL, MEMPLL, MEMPLL
},
.num_parents = 5,
.map = {
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
},
.div_flags = BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "drmfigo",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 16),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 17),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 20),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "cfg",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 23),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 26),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gfx",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 29),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 0),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "zsp",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 5),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 3),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 6),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "perif",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 9),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 12),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = CLK_IGNORE_UNUSED,
},
{
.name = "pcube",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 15),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 18),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vscope",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 21),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 24),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "nfc_ecc",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 18),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 27),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 0),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vpp",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
BERLIN2_PLL_SELECT(REG_CLKSELECT2, 3),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 6),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 4),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 5),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 6),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "app",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
BERLIN2_PLL_SELECT(REG_CLKSELECT2, 9),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 12),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 7),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 8),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 9),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "audio0",
.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
.num_parents = 1,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 22),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 17),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 10),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 11),
},
.div_flags = BERLIN2_DIV_HAS_GATE,
.flags = 0,
},
{
.name = "audio2",
.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
.num_parents = 1,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 24),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 20),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 14),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 15),
},
.div_flags = BERLIN2_DIV_HAS_GATE,
.flags = 0,
},
{
.name = "audio3",
.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
.num_parents = 1,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 25),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 23),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 16),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 17),
},
.div_flags = BERLIN2_DIV_HAS_GATE,
.flags = 0,
},
{
.name = "audio1",
.parent_ids = (const u8 []){ AUDIO1_PLL },
.num_parents = 1,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 23),
BERLIN2_DIV_SELECT(REG_CLKSELECT3, 0),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 12),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 13),
},
.div_flags = BERLIN2_DIV_HAS_GATE,
.flags = 0,
},
{
.name = "gfx3d_core",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_GFX3DCORE_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gfx3d_sys",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_GFX3DSYS_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "arc",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_ARC_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vip",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_VIP_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio0xin",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio1xin",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gfx3d_extra",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_GFX3DEXTRA_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gc360",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_GC360_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio_dllmst",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO_DLLMST_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
};
static const struct berlin2_gate_data bg2_gates[] __initconst = {
{ "geth0", "perif", 7 },
{ "geth1", "perif", 8 },
{ "sata", "perif", 9 },
{ "ahbapb", "perif", 10, CLK_IGNORE_UNUSED },
{ "usb0", "perif", 11 },
{ "usb1", "perif", 12 },
{ "pbridge", "perif", 13, CLK_IGNORE_UNUSED },
{ "sdio0", "perif", 14, CLK_IGNORE_UNUSED },
{ "sdio1", "perif", 15, CLK_IGNORE_UNUSED },
{ "nfc", "perif", 17 },
{ "smemc", "perif", 19 },
{ "audiohd", "audiohd_pll", 26 },
{ "video0", "video0_in", 27 },
{ "video1", "video1_in", 28 },
{ "video2", "video2_in", 29 },
};
static void __init berlin2_clock_setup(struct device_node *np)
{
const char *parent_names[9];
struct clk *clk;
u8 avpll_flags = 0;
int n;
gbase = of_iomap(np, 0);
if (!gbase)
return;
/* overwrite default clock names with DT provided ones */
clk = of_clk_get_by_name(np, clk_names[REFCLK]);
if (!IS_ERR(clk)) {
clk_names[REFCLK] = __clk_get_name(clk);
clk_put(clk);
}
clk = of_clk_get_by_name(np, clk_names[VIDEO_EXT0]);
if (!IS_ERR(clk)) {
clk_names[VIDEO_EXT0] = __clk_get_name(clk);
clk_put(clk);
}
/* simple register PLLs */
clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_SYSPLLCTL0,
clk_names[SYSPLL], clk_names[REFCLK], 0);
if (IS_ERR(clk))
goto bg2_fail;
clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_MEMPLLCTL0,
clk_names[MEMPLL], clk_names[REFCLK], 0);
if (IS_ERR(clk))
goto bg2_fail;
clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_CPUPLLCTL0,
clk_names[CPUPLL], clk_names[REFCLK], 0);
if (IS_ERR(clk))
goto bg2_fail;
if (of_device_is_compatible(np, "marvell,berlin2-global-register"))
avpll_flags |= BERLIN2_AVPLL_SCRAMBLE_QUIRK;
/* audio/video VCOs */
clk = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL0, "avpll_vcoA",
clk_names[REFCLK], avpll_flags, 0);
if (IS_ERR(clk))
goto bg2_fail;
for (n = 0; n < 8; n++) {
clk = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL0,
clk_names[AVPLL_A1 + n], n, "avpll_vcoA",
avpll_flags, 0);
if (IS_ERR(clk))
goto bg2_fail;
}
clk = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL31, "avpll_vcoB",
clk_names[REFCLK], BERLIN2_AVPLL_BIT_QUIRK |
avpll_flags, 0);
if (IS_ERR(clk))
goto bg2_fail;
for (n = 0; n < 8; n++) {
clk = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL31,
clk_names[AVPLL_B1 + n], n, "avpll_vcoB",
BERLIN2_AVPLL_BIT_QUIRK | avpll_flags, 0);
if (IS_ERR(clk))
goto bg2_fail;
}
/* reference clock bypass switches */
parent_names[0] = clk_names[SYSPLL];
parent_names[1] = clk_names[REFCLK];
clk = clk_register_mux(NULL, "syspll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 0, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
clk_names[SYSPLL] = __clk_get_name(clk);
parent_names[0] = clk_names[MEMPLL];
parent_names[1] = clk_names[REFCLK];
clk = clk_register_mux(NULL, "mempll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 1, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
clk_names[MEMPLL] = __clk_get_name(clk);
parent_names[0] = clk_names[CPUPLL];
parent_names[1] = clk_names[REFCLK];
clk = clk_register_mux(NULL, "cpupll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 2, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
clk_names[CPUPLL] = __clk_get_name(clk);
/* clock muxes */
parent_names[0] = clk_names[AVPLL_B3];
parent_names[1] = clk_names[AVPLL_A3];
clk = clk_register_mux(NULL, clk_names[AUDIO1_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT2, 29, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO0_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
clk = clk_register_mux(NULL, clk_names[VIDEO0_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 4, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO1_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
clk = clk_register_mux(NULL, clk_names[VIDEO1_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 6, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
parent_names[0] = clk_names[AVPLL_A2];
parent_names[1] = clk_names[AVPLL_B2];
clk = clk_register_mux(NULL, clk_names[VIDEO1_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT3, 7, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO2_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
clk = clk_register_mux(NULL, clk_names[VIDEO2_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 9, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
parent_names[0] = clk_names[AVPLL_B1];
parent_names[1] = clk_names[AVPLL_A5];
clk = clk_register_mux(NULL, clk_names[VIDEO2_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT3, 10, 1, 0, &lock);
if (IS_ERR(clk))
goto bg2_fail;
/* clock divider cells */
for (n = 0; n < ARRAY_SIZE(bg2_divs); n++) {
const struct berlin2_div_data *dd = &bg2_divs[n];
int k;
for (k = 0; k < dd->num_parents; k++)
parent_names[k] = clk_names[dd->parent_ids[k]];
clks[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
dd->name, dd->div_flags, parent_names,
dd->num_parents, dd->flags, &lock);
}
/* clock gate cells */
for (n = 0; n < ARRAY_SIZE(bg2_gates); n++) {
const struct berlin2_gate_data *gd = &bg2_gates[n];
clks[CLKID_GETH0 + n] = clk_register_gate(NULL, gd->name,
gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
gd->bit_idx, 0, &lock);
}
/* twdclk is derived from cpu/3 */
clks[CLKID_TWD] =
clk_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
if (!IS_ERR(clks[n]))
continue;
pr_err("%s: Unable to register leaf clock %d\n",
np->full_name, n);
goto bg2_fail;
}
/* register clk-provider */
clk_data.clks = clks;
clk_data.clk_num = MAX_CLKS;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
return;
bg2_fail:
iounmap(gbase);
}
CLK_OF_DECLARE(berlin2_clock, "marvell,berlin2-chip-ctrl",
berlin2_clock_setup);
CLK_OF_DECLARE(berlin2cd_clock, "marvell,berlin2cd-chip-ctrl",
berlin2_clock_setup);
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <dt-bindings/clock/berlin2q.h>
#include "berlin2-div.h"
#include "berlin2-pll.h"
#include "common.h"
#define REG_PINMUX0 0x0018
#define REG_PINMUX5 0x002c
#define REG_SYSPLLCTL0 0x0030
#define REG_SYSPLLCTL4 0x0040
#define REG_CLKENABLE 0x00e8
#define REG_CLKSELECT0 0x00ec
#define REG_CLKSELECT1 0x00f0
#define REG_CLKSELECT2 0x00f4
#define REG_CLKSWITCH0 0x00f8
#define REG_CLKSWITCH1 0x00fc
#define REG_SW_GENERIC0 0x0110
#define REG_SW_GENERIC3 0x011c
#define REG_SDIO0XIN_CLKCTL 0x0158
#define REG_SDIO1XIN_CLKCTL 0x015c
#define MAX_CLKS 27
static struct clk *clks[MAX_CLKS];
static struct clk_onecell_data clk_data;
static DEFINE_SPINLOCK(lock);
static void __iomem *gbase;
static void __iomem *cpupll_base;
enum {
REFCLK,
SYSPLL, CPUPLL,
AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4,
AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8,
};
static const char *clk_names[] = {
[REFCLK] = "refclk",
[SYSPLL] = "syspll",
[CPUPLL] = "cpupll",
[AVPLL_B1] = "avpll_b1",
[AVPLL_B2] = "avpll_b2",
[AVPLL_B3] = "avpll_b3",
[AVPLL_B4] = "avpll_b4",
[AVPLL_B5] = "avpll_b5",
[AVPLL_B6] = "avpll_b6",
[AVPLL_B7] = "avpll_b7",
[AVPLL_B8] = "avpll_b8",
};
static const struct berlin2_pll_map bg2q_pll_map __initconst = {
.vcodiv = {1, 0, 2, 0, 3, 4, 0, 6, 8},
.mult = 1,
.fbdiv_shift = 7,
.rfdiv_shift = 2,
.divsel_shift = 9,
};
static const u8 default_parent_ids[] = {
SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL
};
static const struct berlin2_div_data bg2q_divs[] __initconst = {
{
.name = "sys",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = CLK_IGNORE_UNUSED,
},
{
.name = "drmfigo",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 17),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "cfg",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 12),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 15),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 9),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 10),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 11),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gfx2d",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 18),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 21),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "zsp",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 24),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 27),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "perif",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 7),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 0),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 3),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = CLK_IGNORE_UNUSED,
},
{
.name = "pcube",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 6),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 9),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vscope",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 12),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 15),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "nfc_ecc",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 19),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 18),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 21),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vpp",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 24),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 27),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "app",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
BERLIN2_PLL_SELECT(REG_CLKSELECT2, 0),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 3),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio0xin",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio1xin",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
};
static const struct berlin2_gate_data bg2q_gates[] __initconst = {
{ "gfx2daxi", "perif", 5 },
{ "geth0", "perif", 8 },
{ "sata", "perif", 9 },
{ "ahbapb", "perif", 10, CLK_IGNORE_UNUSED },
{ "usb0", "perif", 11 },
{ "usb1", "perif", 12 },
{ "usb2", "perif", 13 },
{ "usb3", "perif", 14 },
{ "pbridge", "perif", 15, CLK_IGNORE_UNUSED },
{ "sdio", "perif", 16, CLK_IGNORE_UNUSED },
{ "nfc", "perif", 18 },
{ "smemc", "perif", 19 },
{ "pcie", "perif", 22 },
};
static void __init berlin2q_clock_setup(struct device_node *np)
{
const char *parent_names[9];
struct clk *clk;
int n;
gbase = of_iomap(np, 0);
if (!gbase) {
pr_err("%s: Unable to map global base\n", np->full_name);
return;
}
/* BG2Q CPU PLL is not part of global registers */
cpupll_base = of_iomap(np, 1);
if (!cpupll_base) {
pr_err("%s: Unable to map cpupll base\n", np->full_name);
iounmap(gbase);
return;
}
/* overwrite default clock names with DT provided ones */
clk = of_clk_get_by_name(np, clk_names[REFCLK]);
if (!IS_ERR(clk)) {
clk_names[REFCLK] = __clk_get_name(clk);
clk_put(clk);
}
/* simple register PLLs */
clk = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0,
clk_names[SYSPLL], clk_names[REFCLK], 0);
if (IS_ERR(clk))
goto bg2q_fail;
clk = berlin2_pll_register(&bg2q_pll_map, cpupll_base,
clk_names[CPUPLL], clk_names[REFCLK], 0);
if (IS_ERR(clk))
goto bg2q_fail;
/* TODO: add BG2Q AVPLL */
/*
* TODO: add reference clock bypass switches:
* memPLLSWBypass, cpuPLLSWBypass, and sysPLLSWBypass
*/
/* clock divider cells */
for (n = 0; n < ARRAY_SIZE(bg2q_divs); n++) {
const struct berlin2_div_data *dd = &bg2q_divs[n];
int k;
for (k = 0; k < dd->num_parents; k++)
parent_names[k] = clk_names[dd->parent_ids[k]];
clks[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
dd->name, dd->div_flags, parent_names,
dd->num_parents, dd->flags, &lock);
}
/* clock gate cells */
for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) {
const struct berlin2_gate_data *gd = &bg2q_gates[n];
clks[CLKID_GFX2DAXI + n] = clk_register_gate(NULL, gd->name,
gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
gd->bit_idx, 0, &lock);
}
/*
* twdclk is derived from cpu/3
* TODO: use cpupll until cpuclk is not available
*/
clks[CLKID_TWD] =
clk_register_fixed_factor(NULL, "twd", clk_names[CPUPLL],
0, 1, 3);
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
if (!IS_ERR(clks[n]))
continue;
pr_err("%s: Unable to register leaf clock %d\n",
np->full_name, n);
goto bg2q_fail;
}
/* register clk-provider */
clk_data.clks = clks;
clk_data.clk_num = MAX_CLKS;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
return;
bg2q_fail:
iounmap(cpupll_base);
iounmap(gbase);
}
CLK_OF_DECLARE(berlin2q_clock, "marvell,berlin2q-chip-ctrl",
berlin2q_clock_setup);
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BERLIN2_COMMON_H
#define __BERLIN2_COMMON_H
struct berlin2_gate_data {
const char *name;
const char *parent_name;
u8 bit_idx;
unsigned long flags;
};
#endif /* BERLIN2_COMMON_H */
/*
* 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");
此差异已折叠。
此差异已折叠。
......@@ -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");
......@@ -1168,6 +1168,7 @@ static const struct of_device_id u300_clk_match[] __initconst = {
.compatible = "stericsson,u300-syscon-mclk",
.data = of_u300_syscon_mclk_init,
},
{}
};
......
此差异已折叠。
......@@ -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);
......
此差异已折叠。
......@@ -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
此差异已折叠。
此差异已折叠。
......@@ -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
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册