diff options
34 files changed, 1696 insertions, 38 deletions
diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt index 8b6ea2267c94..64830118b013 100644 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt @@ -5,6 +5,7 @@ Required properties: - compatible: Should be "marvell,armada-370-xp-timer" - interrupts: Should contain the list of Global Timer interrupts - reg: Should contain the base address of the Global Timer registers +- clocks: clock driving the timer hardware Optional properties: - marvell,timer-25Mhz: Tells whether the Global timer supports the 25 diff --git a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt new file mode 100644 index 000000000000..1e662948661e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt @@ -0,0 +1,47 @@ +* Core Clock bindings for Marvell MVEBU SoCs + +Marvell MVEBU SoCs usually allow to determine core clock frequencies by +reading the Sample-At-Reset (SAR) register. The core clock consumer should +specify the desired clock by having the clock ID in its "clocks" phandle cell. + +The following is a list of provided IDs and clock names on Armada 370/XP: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = nbclk (L2 Cache clock) + 3 = hclk (DRAM control clock) + 4 = dramclk (DDR clock) + +The following is a list of provided IDs and clock names on Kirkwood and Dove: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU0 clock) + 2 = l2clk (L2 Cache clock derived from CPU0 clock) + 3 = 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 + "marvell,armada-xp-core-clock" - For Armada XP SoC core clocks + "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 +- reg : shall be the register address of the Sample-At-Reset (SAR) register +- #clock-cells : from common clock binding; shall be set to 1 + +Optional properties: +- clock-output-names : from common clock binding; allows overwrite default clock + output names ("tclk", "cpuclk", "l2clk", "ddrclk") + +Example: + +core_clk: core-clocks@d0214 { + compatible = "marvell,dove-core-clock"; + reg = <0xd0214 0x4>; + #clock-cells = <1>; +}; + +spi0: spi@10600 { + compatible = "marvell,orion-spi"; + /* ... */ + /* get tclk from core clock provider */ + clocks = <&core_clk 0>; +}; diff --git a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt new file mode 100644 index 000000000000..feb830130714 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt @@ -0,0 +1,21 @@ +Device Tree Clock bindings for cpu clock of Marvell EBU platforms + +Required properties: +- compatible : shall be one of the following: + "marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP +- reg : Address and length of the clock complex register set +- #clock-cells : should be set to 1. +- clocks : shall be the input parent clock phandle for the clock. + +cpuclk: clock-complex@d0018700 { + #clock-cells = <1>; + compatible = "marvell,armada-xp-cpu-clock"; + reg = <0xd0018700 0xA0>; + clocks = <&coreclk 1>; +} + +cpu@0 { + compatible = "marvell,sheeva-v7"; + reg = <0>; + clocks = <&cpuclk 0>; +}; diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt new file mode 100644 index 000000000000..7337005ef5e1 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt @@ -0,0 +1,119 @@ +* Gated Clock bindings for Marvell Orion SoCs + +Marvell Dove and Kirkwood allow some peripheral clocks to be gated to save +some power. The clock consumer should specify the desired clock by having +the clock ID in its "clocks" phandle cell. The clock ID is directly mapped to +the corresponding clock gating control bit in HW to ease manual clock lookup +in datasheet. + +The following is a list of provided IDs for Armada 370: +ID Clock Peripheral +----------------------------------- +0 Audio AC97 Cntrl +1 pex0_en PCIe 0 Clock out +2 pex1_en PCIe 1 Clock out +3 ge1 Gigabit Ethernet 1 +4 ge0 Gigabit Ethernet 0 +5 pex0 PCIe Cntrl 0 +9 pex1 PCIe Cntrl 1 +15 sata0 SATA Host 0 +17 sdio SDHCI Host +25 tdm Time Division Mplx +28 ddr DDR Cntrl +30 sata1 SATA Host 0 + +The following is a list of provided IDs for Armada XP: +ID Clock Peripheral +----------------------------------- +0 audio Audio Cntrl +1 ge3 Gigabit Ethernet 3 +2 ge2 Gigabit Ethernet 2 +3 ge1 Gigabit Ethernet 1 +4 ge0 Gigabit Ethernet 0 +5 pex0 PCIe Cntrl 0 +6 pex1 PCIe Cntrl 1 +7 pex2 PCIe Cntrl 2 +8 pex3 PCIe Cntrl 3 +13 bp +14 sata0lnk +15 sata0 SATA Host 0 +16 lcd LCD Cntrl +17 sdio SDHCI Host +18 usb0 USB Host 0 +19 usb1 USB Host 1 +20 usb2 USB Host 2 +22 xor0 XOR DMA 0 +23 crypto CESA engine +25 tdm Time Division Mplx +28 xor1 XOR DMA 1 +29 sata1lnk +30 sata1 SATA Host 0 + +The following is a list of provided IDs for Dove: +ID Clock Peripheral +----------------------------------- +0 usb0 USB Host 0 +1 usb1 USB Host 1 +2 ge Gigabit Ethernet +3 sata SATA Host +4 pex0 PCIe Cntrl 0 +5 pex1 PCIe Cntrl 1 +8 sdio0 SDHCI Host 0 +9 sdio1 SDHCI Host 1 +10 nand NAND Cntrl +11 camera Camera Cntrl +12 i2s0 I2S Cntrl 0 +13 i2s1 I2S Cntrl 1 +15 crypto CESA engine +21 ac97 AC97 Cntrl +22 pdma Peripheral DMA +23 xor0 XOR DMA 0 +24 xor1 XOR DMA 1 +30 gephy Gigabit Ethernel PHY +Note: gephy(30) is implemented as a parent clock of ge(2) + +The following is a list of provided IDs for Kirkwood: +ID Clock Peripheral +----------------------------------- +0 ge0 Gigabit Ethernet 0 +2 pex0 PCIe Cntrl 0 +3 usb0 USB Host 0 +4 sdio SDIO Cntrl +5 tsu Transp. Stream Unit +6 dunit SDRAM Cntrl +7 runit Runit +8 xor0 XOR DMA 0 +9 audio I2S Cntrl 0 +14 sata0 SATA Host 0 +15 sata1 SATA Host 1 +16 xor1 XOR DMA 1 +17 crypto CESA engine +18 pex1 PCIe Cntrl 1 +19 ge1 Gigabit Ethernet 0 +20 tdm Time Division Mplx + +Required properties: +- compatible : shall be one of the following: + "marvell,dove-gating-clock" - for Dove SoC clock gating + "marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating +- reg : shall be the register address of the Clock Gating Control register +- #clock-cells : from common clock binding; shall be set to 1 + +Optional properties: +- clocks : default parent clock phandle (e.g. tclk) + +Example: + +gate_clk: clock-gating-control@d0038 { + compatible = "marvell,dove-gating-clock"; + reg = <0xd0038 0x4>; + /* default parent clock is tclk */ + clocks = <&core_clk 0>; + #clock-cells = <1>; +}; + +sdio0: sdio@92000 { + compatible = "marvell,dove-sdhci"; + /* get clk gate bit 8 (sdio0) */ + clocks = <&gate_clk 8>; +}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ade7e924bef5..05900997af90 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -533,6 +533,7 @@ config ARCH_IXP4XX config ARCH_DOVE bool "Marvell Dove" select ARCH_REQUIRE_GPIOLIB + select COMMON_CLK_DOVE select CPU_V7 select GENERIC_CLOCKEVENTS select MIGHT_HAVE_PCI diff --git a/arch/arm/boot/dts/armada-370-db.dts b/arch/arm/boot/dts/armada-370-db.dts index fffd5c2a3041..4a31b0396623 100644 --- a/arch/arm/boot/dts/armada-370-db.dts +++ b/arch/arm/boot/dts/armada-370-db.dts @@ -34,9 +34,5 @@ clock-frequency = <200000000>; status = "okay"; }; - timer@d0020300 { - clock-frequency = <600000000>; - status = "okay"; - }; }; }; diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi index 16cc82cdaa81..94b4b9e03571 100644 --- a/arch/arm/boot/dts/armada-370-xp.dtsi +++ b/arch/arm/boot/dts/armada-370-xp.dtsi @@ -62,6 +62,7 @@ compatible = "marvell,armada-370-xp-timer"; reg = <0xd0020300 0x30>; interrupts = <37>, <38>, <39>, <40>; + clocks = <&coreclk 2>; }; addr-decoding@d0020000 { diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi index 2069151afe01..175df2887076 100644 --- a/arch/arm/boot/dts/armada-370.dtsi +++ b/arch/arm/boot/dts/armada-370.dtsi @@ -75,5 +75,20 @@ #interrupts-cells = <2>; interrupts = <91>; }; + + coreclk: mvebu-sar@d0018230 { + compatible = "marvell,armada-370-core-clock"; + reg = <0xd0018230 0x08>; + #clock-cells = <1>; + }; + + gateclk: clock-gating-control@d0018220 { + compatible = "marvell,armada-370-gating-clock"; + reg = <0xd0018220 0x4>; + clocks = <&coreclk 0>; + #clock-cells = <1>; + }; + + }; }; diff --git a/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/arch/arm/boot/dts/armada-xp-mv78230.dtsi index ea355192be6f..c45c7b4dc352 100644 --- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi +++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi @@ -24,6 +24,18 @@ gpio1 = &gpio1; }; + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "marvell,sheeva-v7"; + reg = <0>; + clocks = <&cpuclk 0>; + }; + } + soc { pinctrl { compatible = "marvell,mv78230-pinctrl"; diff --git a/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/arch/arm/boot/dts/armada-xp-mv78260.dtsi index 2057863f3dfa..a2aee5707377 100644 --- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi +++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi @@ -25,6 +25,25 @@ gpio2 = &gpio2; }; + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "marvell,sheeva-v7"; + reg = <0>; + clocks = <&cpuclk 0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "marvell,sheeva-v7"; + reg = <1>; + clocks = <&cpuclk 1>; + }; + }; + soc { pinctrl { compatible = "marvell,mv78260-pinctrl"; diff --git a/arch/arm/boot/dts/armada-xp-mv78460.dtsi b/arch/arm/boot/dts/armada-xp-mv78460.dtsi index ffac98373792..da03a129243a 100644 --- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi +++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi @@ -25,6 +25,40 @@ gpio2 = &gpio2; }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "marvell,sheeva-v7"; + reg = <0>; + clocks = <&cpuclk 0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "marvell,sheeva-v7"; + reg = <1>; + clocks = <&cpuclk 1>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "marvell,sheeva-v7"; + reg = <2>; + clocks = <&cpuclk 2>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "marvell,sheeva-v7"; + reg = <3>; + clocks = <&cpuclk 3>; + }; + }; + soc { pinctrl { compatible = "marvell,mv78460-pinctrl"; diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi index 71d6b5d0daf1..f51554e80009 100644 --- a/arch/arm/boot/dts/armada-xp.dtsi +++ b/arch/arm/boot/dts/armada-xp.dtsi @@ -47,6 +47,26 @@ marvell,timer-25Mhz; }; + coreclk: mvebu-sar@d0018230 { + compatible = "marvell,armada-xp-core-clock"; + reg = <0xd0018230 0x08>; + #clock-cells = <1>; + }; + + cpuclk: clock-complex@d0018700 { + #clock-cells = <1>; + compatible = "marvell,armada-xp-cpu-clock"; + reg = <0xd0018700 0xA0>; + clocks = <&coreclk 1>; + }; + + gateclk: clock-gating-control@d0018220 { + compatible = "marvell,armada-xp-gating-clock"; + reg = <0xd0018220 0x4>; + clocks = <&coreclk 0>; + #clock-cells = <1>; + }; + system-controller@d0018200 { compatible = "marvell,armada-370-xp-system-controller"; reg = <0xd0018200 0x500>; diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi index 5a00022383e7..b524ee377f83 100644 --- a/arch/arm/boot/dts/dove.dtsi +++ b/arch/arm/boot/dts/dove.dtsi @@ -31,6 +31,19 @@ reg = <0x20204 0x04>, <0x20214 0x04>; }; + core_clk: core-clocks@d0214 { + compatible = "marvell,dove-core-clock"; + reg = <0xd0214 0x4>; + #clock-cells = <1>; + }; + + gate_clk: clock-gating-control@d0038 { + compatible = "marvell,dove-gating-clock"; + reg = <0xd0038 0x4>; + clocks = <&core_clk 0>; + #clock-cells = <1>; + }; + uart0: serial@12000 { compatible = "ns16550a"; reg = <0x12000 0x100>; @@ -100,6 +113,7 @@ cell-index = <0>; interrupts = <6>; reg = <0x10600 0x28>; + clocks = <&core_clk 0>; status = "disabled"; }; @@ -110,6 +124,7 @@ cell-index = <1>; interrupts = <5>; reg = <0x14600 0x28>; + clocks = <&core_clk 0>; status = "disabled"; }; @@ -121,6 +136,7 @@ interrupts = <11>; clock-frequency = <400000>; timeout-ms = <1000>; + clocks = <&core_clk 0>; status = "disabled"; }; @@ -128,6 +144,7 @@ compatible = "marvell,dove-sdhci"; reg = <0x92000 0x100>; interrupts = <35>, <37>; + clocks = <&gate_clk 8>; status = "disabled"; }; @@ -135,6 +152,7 @@ compatible = "marvell,dove-sdhci"; reg = <0x90000 0x100>; interrupts = <36>, <38>; + clocks = <&gate_clk 9>; status = "disabled"; }; @@ -142,6 +160,7 @@ compatible = "marvell,orion-sata"; reg = <0xa0000 0x2400>; interrupts = <62>; + clocks = <&gate_clk 3>; nr-ports = <1>; status = "disabled"; }; @@ -152,6 +171,7 @@ <0xc8000000 0x800>; reg-names = "regs", "sram"; interrupts = <31>; + clocks = <&gate_clk 15>; status = "okay"; }; }; diff --git a/arch/arm/boot/dts/kirkwood.dtsi b/arch/arm/boot/dts/kirkwood.dtsi index 4e5b8154a5be..7a9fac0d4251 100644 --- a/arch/arm/boot/dts/kirkwood.dtsi +++ b/arch/arm/boot/dts/kirkwood.dtsi @@ -19,6 +19,12 @@ #address-cells = <1>; #size-cells = <1>; + core_clk: core-clocks@10030 { + compatible = "marvell,kirkwood-core-clock"; + reg = <0x10030 0x4>; + #clock-cells = <1>; + }; + gpio0: gpio@10100 { compatible = "marvell,orion-gpio"; #gpio-cells = <2>; @@ -42,6 +48,7 @@ reg = <0x12000 0x100>; reg-shift = <2>; interrupts = <33>; + clocks = <&gate_clk 7>; /* set clock-frequency in board dts */ status = "disabled"; }; @@ -51,6 +58,7 @@ reg = <0x12100 0x100>; reg-shift = <2>; interrupts = <34>; + clocks = <&gate_clk 7>; /* set clock-frequency in board dts */ status = "disabled"; }; @@ -68,12 +76,21 @@ cell-index = <0>; interrupts = <23>; reg = <0x10600 0x28>; + clocks = <&gate_clk 7>; status = "disabled"; }; + gate_clk: clock-gating-control@2011c { + compatible = "marvell,kirkwood-gating-clock"; + reg = <0x2011c 0x4>; + clocks = <&core_clk 0>; + #clock-cells = <1>; + }; + wdt@20300 { compatible = "marvell,orion-wdt"; reg = <0x20300 0x28>; + clocks = <&gate_clk 7>; status = "okay"; }; @@ -81,6 +98,8 @@ compatible = "marvell,orion-sata"; reg = <0x80000 0x5000>; interrupts = <21>; + clocks = <&gate_clk 14>, <&gate_clk 15>; + clock-names = "0", "1"; status = "disabled"; }; @@ -94,6 +113,7 @@ reg = <0x3000000 0x400>; chip-delay = <25>; /* set partition map and/or chip-delay in board dts */ + clocks = <&gate_clk 7>; status = "disabled"; }; @@ -104,6 +124,7 @@ #size-cells = <0>; interrupts = <29>; clock-frequency = <100000>; + clocks = <&gate_clk 7>; status = "disabled"; }; @@ -113,6 +134,7 @@ <0xf5000000 0x800>; reg-names = "regs", "sram"; interrupts = <22>; + clocks = <&gate_clk 17>; status = "okay"; }; }; diff --git a/arch/arm/mach-dove/Kconfig b/arch/arm/mach-dove/Kconfig index 00154e74ce6b..603c5fd99e8a 100644 --- a/arch/arm/mach-dove/Kconfig +++ b/arch/arm/mach-dove/Kconfig @@ -17,6 +17,8 @@ config MACH_CM_A510 config MACH_DOVE_DT bool "Marvell Dove Flattened Device Tree" + select MVEBU_CLK_CORE + select MVEBU_CLK_GATING select USE_OF help Say 'Y' here if you want your kernel to support the diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c index f723fe13d0f0..6a2c4dc413a8 100644 --- a/arch/arm/mach-dove/common.c +++ b/arch/arm/mach-dove/common.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/pci.h> #include <linux/clk-provider.h> +#include <linux/clk/mvebu.h> #include <linux/ata_platform.h> #include <linux/gpio.h> #include <linux/of.h> @@ -376,19 +377,52 @@ void dove_restart(char mode, const char *cmd) #if defined(CONFIG_MACH_DOVE_DT) /* - * Auxdata required until real OF clock provider + * There are still devices that doesn't even know about DT, + * get clock gates here and add a clock lookup. */ -struct of_dev_auxdata dove_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("marvell,orion-spi", 0xf1010600, "orion_spi.0", NULL), - OF_DEV_AUXDATA("marvell,orion-spi", 0xf1014600, "orion_spi.1", NULL), - OF_DEV_AUXDATA("marvell,orion-wdt", 0xf1020300, "orion_wdt", NULL), - OF_DEV_AUXDATA("marvell,mv64xxx-i2c", 0xf1011000, "mv64xxx_i2c.0", - NULL), - OF_DEV_AUXDATA("marvell,orion-sata", 0xf10a0000, "sata_mv.0", NULL), - OF_DEV_AUXDATA("marvell,dove-sdhci", 0xf1092000, "sdhci-dove.0", NULL), - OF_DEV_AUXDATA("marvell,dove-sdhci", 0xf1090000, "sdhci-dove.1", NULL), - {}, -}; +static void __init dove_legacy_clk_init(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, + "marvell,dove-gating-clock"); + struct of_phandle_args clkspec; + + clkspec.np = np; + clkspec.args_count = 1; + + clkspec.args[0] = CLOCK_GATING_BIT_USB0; + orion_clkdev_add(NULL, "orion-ehci.0", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CLOCK_GATING_BIT_USB1; + orion_clkdev_add(NULL, "orion-ehci.1", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CLOCK_GATING_BIT_GBE; + orion_clkdev_add(NULL, "mv643xx_eth_port.0", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CLOCK_GATING_BIT_PCIE0; + orion_clkdev_add("0", "pcie", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CLOCK_GATING_BIT_PCIE1; + orion_clkdev_add("1", "pcie", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CLOCK_GATING_BIT_XOR0; + orion_clkdev_add(NULL, "mv_xor_shared.0", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CLOCK_GATING_BIT_XOR1; + orion_clkdev_add(NULL, "mv_xor_shared.1", + of_clk_get_from_provider(&clkspec)); +} + +static void __init dove_of_clk_init(void) +{ + mvebu_clocks_init(); + dove_legacy_clk_init(); +} static struct mv643xx_eth_platform_data dove_dt_ge00_data = { .phy_addr = MV643XX_ETH_PHY_ADDR_DEFAULT, @@ -405,7 +439,7 @@ static void __init dove_dt_init(void) dove_setup_cpu_mbus(); /* Setup root of clk tree */ - dove_clk_init(); + dove_of_clk_init(); /* Internal devices not ported to DT yet */ dove_rtc_init(); @@ -417,8 +451,7 @@ static void __init dove_dt_init(void) dove_ehci1_init(); dove_pcie_init(1, 1); - of_platform_populate(NULL, of_default_bus_match_table, - dove_auxdata_lookup, NULL); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } static const char * const dove_dt_board_compat[] = { diff --git a/arch/arm/mach-kirkwood/Kconfig b/arch/arm/mach-kirkwood/Kconfig index 50bca5032b7e..2833492eb273 100644 --- a/arch/arm/mach-kirkwood/Kconfig +++ b/arch/arm/mach-kirkwood/Kconfig @@ -46,6 +46,8 @@ config MACH_GURUPLUG config ARCH_KIRKWOOD_DT bool "Marvell Kirkwood Flattened Device Tree" + select MVEBU_CLK_CORE + select MVEBU_CLK_GATING select USE_OF help Say 'Y' here if you want your kernel to support the diff --git a/arch/arm/mach-kirkwood/board-dt.c b/arch/arm/mach-kirkwood/board-dt.c index d94872fed8c0..8bdfaa4db091 100644 --- a/arch/arm/mach-kirkwood/board-dt.c +++ b/arch/arm/mach-kirkwood/board-dt.c @@ -14,11 +14,15 @@ #include <linux/init.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/clk-provider.h> +#include <linux/clk/mvebu.h> #include <linux/kexec.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #include <mach/bridge-regs.h> +#include <linux/platform_data/usb-ehci-orion.h> #include <plat/irq.h> +#include <plat/common.h> #include "common.h" static struct of_device_id kirkwood_dt_match_table[] __initdata = { @@ -26,16 +30,58 @@ static struct of_device_id kirkwood_dt_match_table[] __initdata = { { } }; -struct of_dev_auxdata kirkwood_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("marvell,orion-spi", 0xf1010600, "orion_spi.0", NULL), - OF_DEV_AUXDATA("marvell,mv64xxx-i2c", 0xf1011000, "mv64xxx_i2c.0", - NULL), - OF_DEV_AUXDATA("marvell,orion-wdt", 0xf1020300, "orion_wdt", NULL), - OF_DEV_AUXDATA("marvell,orion-sata", 0xf1080000, "sata_mv.0", NULL), - OF_DEV_AUXDATA("marvell,orion-nand", 0xf4000000, "orion_nand", NULL), - OF_DEV_AUXDATA("marvell,orion-crypto", 0xf1030000, "mv_crypto", NULL), - {}, -}; +/* + * There are still devices that doesn't know about DT yet. Get clock + * gates here and add a clock lookup alias, so that old platform + * devices still work. +*/ + +static void __init kirkwood_legacy_clk_init(void) +{ + + struct device_node *np = of_find_compatible_node( + NULL, NULL, "marvell,kirkwood-gating-clock"); + + struct of_phandle_args clkspec; + + clkspec.np = np; + clkspec.args_count = 1; + + clkspec.args[0] = CGC_BIT_GE0; + orion_clkdev_add(NULL, "mv643xx_eth_port.0", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CGC_BIT_PEX0; + orion_clkdev_add("0", "pcie", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CGC_BIT_USB0; + orion_clkdev_add(NULL, "orion-ehci.0", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CGC_BIT_XOR0; + orion_clkdev_add(NULL, "mv_xor_shared.0", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CGC_BIT_XOR1; + orion_clkdev_add(NULL, "mv_xor_shared.1", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CGC_BIT_PEX1; + orion_clkdev_add("1", "pcie", + of_clk_get_from_provider(&clkspec)); + + clkspec.args[0] = CGC_BIT_GE1; + orion_clkdev_add(NULL, "mv643xx_eth_port.1", + of_clk_get_from_provider(&clkspec)); + +} + +static void __init kirkwood_of_clk_init(void) +{ + mvebu_clocks_init(); + kirkwood_legacy_clk_init(); +} static void __init kirkwood_dt_init(void) { @@ -54,7 +100,7 @@ static void __init kirkwood_dt_init(void) kirkwood_l2_init(); /* Setup root of clk tree */ - kirkwood_clk_init(); + kirkwood_of_clk_init(); /* internal devices that every board has */ kirkwood_xor0_init(); @@ -94,8 +140,7 @@ static void __init kirkwood_dt_init(void) if (of_machine_is_compatible("keymile,km_kirkwood")) km_kirkwood_init(); - of_platform_populate(NULL, kirkwood_dt_match_table, - kirkwood_auxdata_lookup, NULL); + of_platform_populate(NULL, kirkwood_dt_match_table, NULL, NULL); } static const char *kirkwood_dt_board_compat[] = { diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 416d46ef7ebd..79299cd94f0f 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -9,6 +9,10 @@ config ARCH_MVEBU select PINCTRL select PLAT_ORION select SPARSE_IRQ + select CLKDEV_LOOKUP + select MVEBU_CLK_CORE + select MVEBU_CLK_CPU + select MVEBU_CLK_GATING if ARCH_MVEBU diff --git a/arch/arm/mach-mvebu/armada-370-xp.c b/arch/arm/mach-mvebu/armada-370-xp.c index 49d791548ad6..3292d6da5dc7 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.c +++ b/arch/arm/mach-mvebu/armada-370-xp.c @@ -17,6 +17,7 @@ #include <linux/of_platform.h> #include <linux/io.h> #include <linux/time-armada-370-xp.h> +#include <linux/clk/mvebu.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #include <asm/mach/time.h> @@ -37,8 +38,14 @@ void __init armada_370_xp_map_io(void) iotable_init(armada_370_xp_io_desc, ARRAY_SIZE(armada_370_xp_io_desc)); } +void __init armada_370_xp_timer_and_clk_init(void) +{ + mvebu_clocks_init(); + armada_370_xp_timer_init(); +} + struct sys_timer armada_370_xp_timer = { - .init = armada_370_xp_timer_init, + .init = armada_370_xp_timer_and_clk_init, }; static void __init armada_370_xp_dt_init(void) diff --git a/arch/arm/plat-orion/include/plat/common.h b/arch/arm/plat-orion/include/plat/common.h index 6bbc3fe5f58e..e06fc5fefa14 100644 --- a/arch/arm/plat-orion/include/plat/common.h +++ b/arch/arm/plat-orion/include/plat/common.h @@ -12,6 +12,7 @@ #include <linux/mv643xx_eth.h> struct dsa_platform_data; +struct mv_sata_platform_data; void __init orion_uart0_init(void __iomem *membase, resource_size_t mapbase, diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index bace9e98f75d..60427c0d23e6 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -54,3 +54,5 @@ config COMMON_CLK_MAX77686 This driver supports Maxim 77686 crystal oscillator clock. endmenu + +source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 71a25b91de00..d0a14ae8d49c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o +obj-$(CONFIG_PLAT_ORION) += mvebu/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig new file mode 100644 index 000000000000..57323fd15ec9 --- /dev/null +++ b/drivers/clk/mvebu/Kconfig @@ -0,0 +1,8 @@ +config MVEBU_CLK_CORE + bool + +config MVEBU_CLK_CPU + bool + +config MVEBU_CLK_GATING + bool diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile new file mode 100644 index 000000000000..58df3dc49363 --- /dev/null +++ b/drivers/clk/mvebu/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o +obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o +obj-$(CONFIG_MVEBU_CLK_GATING) += clk-gating-ctrl.o diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c new file mode 100644 index 000000000000..69056a7479e8 --- /dev/null +++ b/drivers/clk/mvebu/clk-core.c @@ -0,0 +1,675 @@ +/* + * Marvell EBU clock core handling defined at reset + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <[email protected]> + * Sebastian Hesselbarth <[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/io.h> +#include <linux/of.h> +#include "clk-core.h" + +struct core_ratio { + int id; + const char *name; +}; + +struct core_clocks { + u32 (*get_tclk_freq)(void __iomem *sar); + u32 (*get_cpu_freq)(void __iomem *sar); + void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); + const struct core_ratio *ratios; + int num_ratios; +}; + +static struct clk_onecell_data clk_data; + +static void __init mvebu_clk_core_setup(struct device_node *np, + struct core_clocks *coreclk) +{ + const char *tclk_name = "tclk"; + const char *cpuclk_name = "cpuclk"; + void __iomem *base; + unsigned long rate; + int n; + + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return; + + /* + * Allocate struct for TCLK, cpu clk, and core ratio clocks + */ + clk_data.clk_num = 2 + coreclk->num_ratios; + clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!clk_data.clks)) + return; + + /* + * Register TCLK + */ + of_property_read_string_index(np, "clock-output-names", 0, + &tclk_name); + rate = coreclk->get_tclk_freq(base); + clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, + CLK_IS_ROOT, rate); + WARN_ON(IS_ERR(clk_data.clks[0])); + + /* + * Register CPU clock + */ + of_property_read_string_index(np, "clock-output-names", 1, + &cpuclk_name); + rate = coreclk->get_cpu_freq(base); + clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, + CLK_IS_ROOT, rate); + WARN_ON(IS_ERR(clk_data.clks[1])); + + /* + * Register fixed-factor clocks derived from CPU clock + */ + for (n = 0; n < coreclk->num_ratios; n++) { + const char *rclk_name = coreclk->ratios[n].name; + int mult, div; + + of_property_read_string_index(np, "clock-output-names", + 2+n, &rclk_name); + coreclk->get_clk_ratio(base, coreclk->ratios[n].id, + &mult, &div); + clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, + cpuclk_name, 0, mult, div); + WARN_ON(IS_ERR(clk_data.clks[2+n])); + }; + + /* + * SAR register isn't needed anymore + */ + iounmap(base); + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} + +#ifdef CONFIG_MACH_ARMADA_370_XP +/* + * Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two + * register of 32 bits + */ + +#define SARL 0 /* Low part [0:31] */ +#define SARL_AXP_PCLK_FREQ_OPT 21 +#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 +#define SARL_A370_PCLK_FREQ_OPT 11 +#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF +#define SARL_AXP_FAB_FREQ_OPT 24 +#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF +#define SARL_A370_FAB_FREQ_OPT 15 +#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F +#define SARL_A370_TCLK_FREQ_OPT 20 +#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 +#define SARH 4 /* High part [32:63] */ +#define SARH_AXP_PCLK_FREQ_OPT (52-32) +#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 +#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 +#define SARH_AXP_FAB_FREQ_OPT (51-32) +#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 +#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 + +static const u32 __initconst armada_370_tclk_frequencies[] = { + 16600000, + 20000000, +}; + +static u32 __init armada_370_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select = 0; + + tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) & + SARL_A370_TCLK_FREQ_OPT_MASK); + return armada_370_tclk_frequencies[tclk_freq_select]; +} + +static const u32 __initconst armada_370_cpu_frequencies[] = { + 400000000, + 533000000, + 667000000, + 800000000, + 1000000000, + 1067000000, + 1200000000, +}; + +static u32 __init armada_370_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & + SARL_A370_PCLK_FREQ_OPT_MASK); + if (cpu_freq_select > ARRAY_SIZE(armada_370_cpu_frequencies)) { + pr_err("CPU freq select unsuported %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = armada_370_cpu_frequencies[cpu_freq_select]; + + return cpu_freq; +} + +enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK }; + +static const struct core_ratio __initconst armada_370_xp_core_ratios[] = { + { .id = A370_XP_NBCLK, .name = "nbclk" }, + { .id = A370_XP_HCLK, .name = "hclk" }, + { .id = A370_XP_DRAMCLK, .name = "dramclk" }, +}; + +static const int __initconst armada_370_xp_nbclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 2}, {2, 2}, + {1, 2}, {1, 2}, {1, 1}, {2, 3}, + {0, 1}, {1, 2}, {2, 4}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {2, 2}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int __initconst armada_370_xp_hclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 6}, {2, 3}, + {1, 3}, {1, 4}, {1, 2}, {2, 6}, + {0, 1}, {1, 6}, {2, 10}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 2}, + {2, 6}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int __initconst armada_370_xp_dramclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 3}, {2, 3}, + {1, 3}, {1, 2}, {1, 2}, {2, 6}, + {0, 1}, {1, 3}, {2, 5}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_370_xp_get_clk_ratio(u32 opt, + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case A370_XP_NBCLK: + *mult = armada_370_xp_nbclk_ratios[opt][0]; + *div = armada_370_xp_nbclk_ratios[opt][1]; + break; + case A370_XP_HCLK: + *mult = armada_370_xp_hclk_ratios[opt][0]; + *div = armada_370_xp_hclk_ratios[opt][1]; + break; + case A370_XP_DRAMCLK: + *mult = armada_370_xp_dramclk_ratios[opt][0]; + *div = armada_370_xp_dramclk_ratios[opt][1]; + break; + } +} + +static void __init armada_370_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) & + SARL_A370_FAB_FREQ_OPT_MASK); + + armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); +} + + +static const struct core_clocks armada_370_core_clocks = { + .get_tclk_freq = armada_370_get_tclk_freq, + .get_cpu_freq = armada_370_get_cpu_freq, + .get_clk_ratio = armada_370_get_clk_ratio, + .ratios = armada_370_xp_core_ratios, + .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), +}; + +static const u32 __initconst armada_xp_cpu_frequencies[] = { + 1000000000, + 1066000000, + 1200000000, + 1333000000, + 1500000000, + 1666000000, + 1800000000, + 2000000000, + 667000000, + 0, + 800000000, + 1600000000, +}; + +/* For Armada XP TCLK frequency is fix: 250MHz */ +static u32 __init armada_xp_get_tclk_freq(void __iomem *sar) +{ + return 250 * 1000 * 1000; +} + +static u32 __init armada_xp_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) & + SARL_AXP_PCLK_FREQ_OPT_MASK); + /* + * The upper bit is not contiguous to the other ones and + * located in the high part of the SAR registers + */ + cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) & + SARH_AXP_PCLK_FREQ_OPT_MASK) + << SARH_AXP_PCLK_FREQ_OPT_SHIFT); + if (cpu_freq_select > ARRAY_SIZE(armada_xp_cpu_frequencies)) { + pr_err("CPU freq select unsuported: %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select]; + + return cpu_freq; +} + +static void __init armada_xp_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + + u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) & + SARL_AXP_FAB_FREQ_OPT_MASK); + /* + * The upper bit is not contiguous to the other ones and + * located in the high part of the SAR registers + */ + opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) & + SARH_AXP_FAB_FREQ_OPT_MASK) + << SARH_AXP_FAB_FREQ_OPT_SHIFT); + + armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); +} + +static const struct core_clocks armada_xp_core_clocks = { + .get_tclk_freq = armada_xp_get_tclk_freq, + .get_cpu_freq = armada_xp_get_cpu_freq, + .get_clk_ratio = armada_xp_get_clk_ratio, + .ratios = armada_370_xp_core_ratios, + .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), +}; + +#endif /* CONFIG_MACH_ARMADA_370_XP */ + +/* + * Dove PLL sample-at-reset configuration + * + * SAR0[8:5] : CPU frequency + * 5 = 1000 MHz + * 6 = 933 MHz + * 7 = 933 MHz + * 8 = 800 MHz + * 9 = 800 MHz + * 10 = 800 MHz + * 11 = 1067 MHz + * 12 = 667 MHz + * 13 = 533 MHz + * 14 = 400 MHz + * 15 = 333 MHz + * others reserved. + * + * SAR0[11:9] : CPU to L2 Clock divider ratio + * 0 = (1/1) * CPU + * 2 = (1/2) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * others reserved. + * + * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio + * 0 = (1/1) * CPU + * 2 = (1/2) * CPU + * 3 = (2/5) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * 8 = (1/5) * CPU + * 10 = (1/6) * CPU + * 12 = (1/7) * CPU + * 14 = (1/8) * CPU + * 15 = (1/10) * CPU + * others reserved. + * + * SAR0[24:23] : TCLK frequency + * 0 = 166 MHz + * 1 = 125 MHz + * others reserved. + */ +#ifdef CONFIG_ARCH_DOVE +#define SAR_DOVE_CPU_FREQ 5 +#define SAR_DOVE_CPU_FREQ_MASK 0xf +#define SAR_DOVE_L2_RATIO 9 +#define SAR_DOVE_L2_RATIO_MASK 0x7 +#define SAR_DOVE_DDR_RATIO 12 +#define SAR_DOVE_DDR_RATIO_MASK 0xf +#define SAR_DOVE_TCLK_FREQ 23 +#define SAR_DOVE_TCLK_FREQ_MASK 0x3 + +static const u32 __initconst dove_tclk_frequencies[] = { + 166666667, + 125000000, + 0, 0 +}; + +static u32 __init dove_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) & + SAR_DOVE_TCLK_FREQ_MASK; + return dove_tclk_frequencies[opt]; +} + +static const u32 __initconst dove_cpu_frequencies[] = { + 0, 0, 0, 0, 0, + 1000000000, + 933333333, 933333333, + 800000000, 800000000, 800000000, + 1066666667, + 666666667, + 533333333, + 400000000, + 333333333 +}; + +static u32 __init dove_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) & + SAR_DOVE_CPU_FREQ_MASK; + return dove_cpu_frequencies[opt]; +} + +enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR }; + +static const struct core_ratio __initconst dove_core_ratios[] = { + { .id = DOVE_CPU_TO_L2, .name = "l2clk", }, + { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", } +}; + +static const int __initconst dove_cpu_l2_ratios[8][2] = { + { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 } +}; + +static const int __initconst dove_cpu_ddr_ratios[16][2] = { + { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }, + { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 }, + { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 } +}; + +static void __init dove_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case DOVE_CPU_TO_L2: + { + u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) & + SAR_DOVE_L2_RATIO_MASK; + *mult = dove_cpu_l2_ratios[opt][0]; + *div = dove_cpu_l2_ratios[opt][1]; + break; + } + case DOVE_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) & + SAR_DOVE_DDR_RATIO_MASK; + *mult = dove_cpu_ddr_ratios[opt][0]; + *div = dove_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks dove_core_clocks = { + .get_tclk_freq = dove_get_tclk_freq, + .get_cpu_freq = dove_get_cpu_freq, + .get_clk_ratio = dove_get_clk_ratio, + .ratios = dove_core_ratios, + .num_ratios = ARRAY_SIZE(dove_core_ratios), +}; +#endif /* CONFIG_ARCH_DOVE */ + +/* + * Kirkwood PLL sample-at-reset configuration + * (6180 has different SAR layout than other Kirkwood SoCs) + * + * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282) + * 4 = 600 MHz + * 6 = 800 MHz + * 7 = 1000 MHz + * 9 = 1200 MHz + * 12 = 1500 MHz + * 13 = 1600 MHz + * 14 = 1800 MHz + * 15 = 2000 MHz + * others reserved. + * + * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282) + * 1 = (1/2) * CPU + * 3 = (1/3) * CPU + * 5 = (1/4) * CPU + * others reserved. + * + * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282) + * 2 = (1/2) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * 7 = (2/9) * CPU + * 8 = (1/5) * CPU + * 9 = (1/6) * CPU + * others reserved. + * + * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only) + * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU] + * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU] + * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU] + * others reserved. + * + * SAR0[21] : TCLK frequency + * 0 = 200 MHz + * 1 = 166 MHz + * others reserved. + */ +#ifdef CONFIG_ARCH_KIRKWOOD +#define SAR_KIRKWOOD_CPU_FREQ(x) \ + (((x & (1 << 1)) >> 1) | \ + ((x & (1 << 22)) >> 21) | \ + ((x & (3 << 3)) >> 1)) +#define SAR_KIRKWOOD_L2_RATIO(x) \ + (((x & (3 << 9)) >> 9) | \ + (((x & (1 << 19)) >> 17))) +#define SAR_KIRKWOOD_DDR_RATIO 5 +#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf +#define SAR_MV88F6180_CLK 2 +#define SAR_MV88F6180_CLK_MASK 0x7 +#define SAR_KIRKWOOD_TCLK_FREQ 21 +#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1 + +enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR }; + +static const struct core_ratio __initconst kirkwood_core_ratios[] = { + { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", }, + { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", } +}; + +static u32 __init kirkwood_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) & + SAR_KIRKWOOD_TCLK_FREQ_MASK; + return (opt) ? 166666667 : 200000000; +} + +static const u32 __initconst kirkwood_cpu_frequencies[] = { + 0, 0, 0, 0, + 600000000, + 0, + 800000000, + 1000000000, + 0, + 1200000000, + 0, 0, + 1500000000, + 1600000000, + 1800000000, + 2000000000 +}; + +static u32 __init kirkwood_get_cpu_freq(void __iomem *sar) +{ + u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar)); + return kirkwood_cpu_frequencies[opt]; +} + +static const int __initconst kirkwood_cpu_l2_ratios[8][2] = { + { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, + { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 } +}; + +static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = { + { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 }, + { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 }, + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } +}; + +static void __init kirkwood_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case KIRKWOOD_CPU_TO_L2: + { + u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar)); + *mult = kirkwood_cpu_l2_ratios[opt][0]; + *div = kirkwood_cpu_l2_ratios[opt][1]; + break; + } + case KIRKWOOD_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) & + SAR_KIRKWOOD_DDR_RATIO_MASK; + *mult = kirkwood_cpu_ddr_ratios[opt][0]; + *div = kirkwood_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks kirkwood_core_clocks = { + .get_tclk_freq = kirkwood_get_tclk_freq, + .get_cpu_freq = kirkwood_get_cpu_freq, + .get_clk_ratio = kirkwood_get_clk_ratio, + .ratios = kirkwood_core_ratios, + .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), +}; + +static const u32 __initconst mv88f6180_cpu_frequencies[] = { + 0, 0, 0, 0, 0, + 600000000, + 800000000, + 1000000000 +}; + +static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; + return mv88f6180_cpu_frequencies[opt]; +} + +static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = { + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, + { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 } +}; + +static void __init mv88f6180_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case KIRKWOOD_CPU_TO_L2: + { + /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */ + *mult = 1; + *div = 2; + break; + } + case KIRKWOOD_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & + SAR_MV88F6180_CLK_MASK; + *mult = mv88f6180_cpu_ddr_ratios[opt][0]; + *div = mv88f6180_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks mv88f6180_core_clocks = { + .get_tclk_freq = kirkwood_get_tclk_freq, + .get_cpu_freq = mv88f6180_get_cpu_freq, + .get_clk_ratio = mv88f6180_get_clk_ratio, + .ratios = kirkwood_core_ratios, + .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), +}; +#endif /* CONFIG_ARCH_KIRKWOOD */ + +static const __initdata struct of_device_id clk_core_match[] = { +#ifdef CONFIG_MACH_ARMADA_370_XP + { + .compatible = "marvell,armada-370-core-clock", + .data = &armada_370_core_clocks, + }, + { + .compatible = "marvell,armada-xp-core-clock", + .data = &armada_xp_core_clocks, + }, +#endif +#ifdef CONFIG_ARCH_DOVE + { + .compatible = "marvell,dove-core-clock", + .data = &dove_core_clocks, + }, +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD + { + .compatible = "marvell,kirkwood-core-clock", + .data = &kirkwood_core_clocks, + }, + { + .compatible = "marvell,mv88f6180-core-clock", + .data = &mv88f6180_core_clocks, + }, +#endif + + { } +}; + +void __init mvebu_core_clk_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, clk_core_match) { + const struct of_device_id *match = + of_match_node(clk_core_match, np); + mvebu_clk_core_setup(np, (struct core_clocks *)match->data); + } +} diff --git a/drivers/clk/mvebu/clk-core.h b/drivers/clk/mvebu/clk-core.h new file mode 100644 index 000000000000..28b5e02e9885 --- /dev/null +++ b/drivers/clk/mvebu/clk-core.h @@ -0,0 +1,18 @@ +/* + * * Marvell EBU clock core handling defined at reset + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_CORE_H +#define __MVEBU_CLK_CORE_H + +void __init mvebu_core_clk_init(void); + +#endif diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c new file mode 100644 index 000000000000..ff004578a119 --- /dev/null +++ b/drivers/clk/mvebu/clk-cpu.c @@ -0,0 +1,186 @@ +/* + * Marvell MVEBU CPU clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include <linux/kernel.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/delay.h> +#include "clk-cpu.h" + +#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 +#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC +#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F + +#define MAX_CPU 4 +struct cpu_clk { + struct clk_hw hw; + int cpu; + const char *clk_name; + const char *parent_name; + void __iomem *reg_base; +}; + +static struct clk **clks; + +static struct clk_onecell_data clk_data; + +#define to_cpu_clk(p) container_of(p, struct cpu_clk, hw) + +static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK; + return parent_rate / div; +} + +static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long *parent_rate) +{ + /* Valid ratio are 1:1, 1:2 and 1:3 */ + u32 div; + + div = *parent_rate / rate; + if (div == 0) + div = 1; + else if (div > 3) + div = 3; + + return *parent_rate / div; +} + +static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + u32 reload_mask; + + div = parent_rate / rate; + reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET) + & (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8)))) + | (div << (cpuclk->cpu * 8)); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + /* Set clock divider reload smooth bit mask */ + reload_mask = 1 << (20 + cpuclk->cpu); + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET) + | reload_mask; + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + /* Now trigger the clock update */ + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET) + | 1 << 24; + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + /* Wait for clocks to settle down then clear reload request */ + udelay(1000); + reg &= ~(reload_mask | 1 << 24); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + udelay(1000); + + return 0; +} + +static const struct clk_ops cpu_ops = { + .recalc_rate = clk_cpu_recalc_rate, + .round_rate = clk_cpu_round_rate, + .set_rate = clk_cpu_set_rate, +}; + +void __init of_cpu_clk_setup(struct device_node *node) +{ + struct cpu_clk *cpuclk; + void __iomem *clock_complex_base = of_iomap(node, 0); + int ncpus = 0; + struct device_node *dn; + + if (clock_complex_base == NULL) { + pr_err("%s: clock-complex base register not set\n", + __func__); + return; + } + + for_each_node_by_type(dn, "cpu") + ncpus++; + + cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL); + if (WARN_ON(!cpuclk)) + return; + + clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL); + if (WARN_ON(!clks)) + return; + + for_each_node_by_type(dn, "cpu") { + struct clk_init_data init; + struct clk *clk; + struct clk *parent_clk; + char *clk_name = kzalloc(5, GFP_KERNEL); + int cpu, err; + + if (WARN_ON(!clk_name)) + return; + + err = of_property_read_u32(dn, "reg", &cpu); + if (WARN_ON(err)) + return; + + sprintf(clk_name, "cpu%d", cpu); + parent_clk = of_clk_get(node, 0); + + cpuclk[cpu].parent_name = __clk_get_name(parent_clk); + cpuclk[cpu].clk_name = clk_name; + cpuclk[cpu].cpu = cpu; + cpuclk[cpu].reg_base = clock_complex_base; + cpuclk[cpu].hw.init = &init; + + init.name = cpuclk[cpu].clk_name; + init.ops = &cpu_ops; + init.flags = 0; + init.parent_names = &cpuclk[cpu].parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &cpuclk[cpu].hw); + if (WARN_ON(IS_ERR(clk))) + goto bail_out; + clks[cpu] = clk; + } + clk_data.clk_num = MAX_CPU; + clk_data.clks = clks; + of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data); + + return; +bail_out: + kfree(clks); + kfree(cpuclk); +} + +static const __initconst struct of_device_id clk_cpu_match[] = { + { + .compatible = "marvell,armada-xp-cpu-clock", + .data = of_cpu_clk_setup, + }, + { + /* sentinel */ + }, +}; + +void __init mvebu_cpu_clk_init(void) +{ + of_clk_init(clk_cpu_match); +} diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h new file mode 100644 index 000000000000..08e2affba4e6 --- /dev/null +++ b/drivers/clk/mvebu/clk-cpu.h @@ -0,0 +1,22 @@ +/* + * Marvell MVEBU CPU clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_CPU_H +#define __MVEBU_CLK_CPU_H + +#ifdef CONFIG_MVEBU_CLK_CPU +void __init mvebu_cpu_clk_init(void); +#else +static inline void mvebu_cpu_clk_init(void) {} +#endif + +#endif diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c new file mode 100644 index 000000000000..c6d3c263b070 --- /dev/null +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -0,0 +1,249 @@ +/* + * Marvell MVEBU clock gating control. + * + * Sebastian Hesselbarth <[email protected]> + * Andrew Lunn <[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/clk/mvebu.h> +#include <linux/of.h> +#include <linux/of_address.h> + +struct mvebu_gating_ctrl { + spinlock_t lock; + struct clk **gates; + int num_gates; +}; + +struct mvebu_soc_descr { + const char *name; + const char *parent; + int bit_idx; +}; + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +static struct clk __init *mvebu_clk_gating_get_src( + struct of_phandle_args *clkspec, void *data) +{ + struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data; + int n; + + if (clkspec->args_count < 1) + return ERR_PTR(-EINVAL); + + for (n = 0; n < ctrl->num_gates; n++) { + struct clk_gate *gate = + to_clk_gate(__clk_get_hw(ctrl->gates[n])); + if (clkspec->args[0] == gate->bit_idx) + return ctrl->gates[n]; + } + return ERR_PTR(-ENODEV); +} + +static void __init mvebu_clk_gating_setup( + struct device_node *np, const struct mvebu_soc_descr *descr) +{ + struct mvebu_gating_ctrl *ctrl; + struct clk *clk; + void __iomem *base; + const char *default_parent = NULL; + int n; + + base = of_iomap(np, 0); + + clk = of_clk_get(np, 0); + if (!IS_ERR(clk)) { + default_parent = __clk_get_name(clk); + clk_put(clk); + } + + ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL); + if (WARN_ON(!ctrl)) + return; + + spin_lock_init(&ctrl->lock); + + /* + * Count, allocate, and register clock gates + */ + for (n = 0; descr[n].name;) + n++; + + ctrl->num_gates = n; + ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!ctrl->gates)) { + kfree(ctrl); + return; + } + + for (n = 0; n < ctrl->num_gates; n++) { + u8 flags = 0; + const char *parent = + (descr[n].parent) ? descr[n].parent : default_parent; + + /* + * On Armada 370, the DDR clock is a special case: it + * isn't taken by any driver, but should anyway be + * kept enabled, so we mark it as IGNORE_UNUSED for + * now. + */ + if (!strcmp(descr[n].name, "ddr")) + flags |= CLK_IGNORE_UNUSED; + + ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, + flags, base, descr[n].bit_idx, 0, &ctrl->lock); + WARN_ON(IS_ERR(ctrl->gates[n])); + } + of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); +} + +/* + * SoC specific clock gating control + */ + +#ifdef CONFIG_MACH_ARMADA_370 +static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = { + { "audio", NULL, 0 }, + { "pex0_en", NULL, 1 }, + { "pex1_en", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 9 }, + { "sata0", NULL, 15 }, + { "sdio", NULL, 17 }, + { "tdm", NULL, 25 }, + { "ddr", NULL, 28 }, + { "sata1", NULL, 30 }, + { } +}; +#endif + +#ifdef CONFIG_MACH_ARMADA_XP +static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = { + { "audio", NULL, 0 }, + { "ge3", NULL, 1 }, + { "ge2", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 6 }, + { "pex2", NULL, 7 }, + { "pex3", NULL, 8 }, + { "bp", NULL, 13 }, + { "sata0lnk", NULL, 14 }, + { "sata0", "sata0lnk", 15 }, + { "lcd", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb0", NULL, 18 }, + { "usb1", NULL, 19 }, + { "usb2", NULL, 20 }, + { "xor0", NULL, 22 }, + { "crypto", NULL, 23 }, + { "tdm", NULL, 25 }, + { "xor1", NULL, 28 }, + { "sata1lnk", NULL, 29 }, + { "sata1", "sata1lnk", 30 }, + { } +}; +#endif + +#ifdef CONFIG_ARCH_DOVE +static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { + { "usb0", NULL, 0 }, + { "usb1", NULL, 1 }, + { "ge", "gephy", 2 }, + { "sata", NULL, 3 }, + { "pex0", NULL, 4 }, + { "pex1", NULL, 5 }, + { "sdio0", NULL, 8 }, + { "sdio1", NULL, 9 }, + { "nand", NULL, 10 }, + { "camera", NULL, 11 }, + { "i2s0", NULL, 12 }, + { "i2s1", NULL, 13 }, + { "crypto", NULL, 15 }, + { "ac97", NULL, 21 }, + { "pdma", NULL, 22 }, + { "xor0", NULL, 23 }, + { "xor1", NULL, 24 }, + { "gephy", NULL, 30 }, + { } +}; +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD +static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { + { "ge0", NULL, 0 }, + { "pex0", NULL, 2 }, + { "usb0", NULL, 3 }, + { "sdio", NULL, 4 }, + { "tsu", NULL, 5 }, + { "runit", NULL, 7 }, + { "xor0", NULL, 8 }, + { "audio", NULL, 9 }, + { "sata0", NULL, 14 }, + { "sata1", NULL, 15 }, + { "xor1", NULL, 16 }, + { "crypto", NULL, 17 }, + { "pex1", NULL, 18 }, + { "ge1", NULL, 19 }, + { "tdm", NULL, 20 }, + { } +}; +#endif + +static const __initdata struct of_device_id clk_gating_match[] = { +#ifdef CONFIG_MACH_ARMADA_370 + { + .compatible = "marvell,armada-370-gating-clock", + .data = armada_370_gating_descr, + }, +#endif + +#ifdef CONFIG_MACH_ARMADA_XP + { + .compatible = "marvell,armada-xp-gating-clock", + .data = armada_xp_gating_descr, + }, +#endif + +#ifdef CONFIG_ARCH_DOVE + { + .compatible = "marvell,dove-gating-clock", + .data = dove_gating_descr, + }, +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD + { + .compatible = "marvell,kirkwood-gating-clock", + .data = kirkwood_gating_descr, + }, +#endif + + { } +}; + +void __init mvebu_gating_clk_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, clk_gating_match) { + const struct of_device_id *match = + of_match_node(clk_gating_match, np); + mvebu_clk_gating_setup(np, + (const struct mvebu_soc_descr *)match->data); + } +} diff --git a/drivers/clk/mvebu/clk-gating-ctrl.h b/drivers/clk/mvebu/clk-gating-ctrl.h new file mode 100644 index 000000000000..9275d1e51f1b --- /dev/null +++ b/drivers/clk/mvebu/clk-gating-ctrl.h @@ -0,0 +1,22 @@ +/* + * Marvell EBU gating clock handling + * + * Copyright (C) 2012 Marvell + * + * Thomas Petazzoni <[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_GATING_H +#define __MVEBU_CLK_GATING_H + +#ifdef CONFIG_MVEBU_CLK_GATING +void __init mvebu_gating_clk_init(void); +#else +void mvebu_gating_clk_init(void) {} +#endif + +#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c new file mode 100644 index 000000000000..855681b8a9dc --- /dev/null +++ b/drivers/clk/mvebu/clk.c @@ -0,0 +1,27 @@ +/* + * Marvell EBU SoC clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/clk/mvebu.h> +#include <linux/of.h> +#include "clk-core.h" +#include "clk-cpu.h" +#include "clk-gating-ctrl.h" + +void __init mvebu_clocks_init(void) +{ + mvebu_core_clk_init(); + mvebu_gating_clk_init(); + mvebu_cpu_clk_init(); +} diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c index 4674f94957cd..a4605fd7e303 100644 --- a/drivers/clocksource/time-armada-370-xp.c +++ b/drivers/clocksource/time-armada-370-xp.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/kernel.h> +#include <linux/clk.h> #include <linux/timer.h> #include <linux/clockchips.h> #include <linux/interrupt.h> @@ -167,7 +168,6 @@ void __init armada_370_xp_timer_init(void) u32 u; struct device_node *np; unsigned int timer_clk; - int ret; np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer"); timer_base = of_iomap(np, 0); WARN_ON(!timer_base); @@ -179,13 +179,14 @@ void __init armada_370_xp_timer_init(void) timer_base + TIMER_CTRL_OFF); timer_clk = 25000000; } else { - u32 clk = 0; - ret = of_property_read_u32(np, "clock-frequency", &clk); - WARN_ON(!clk || ret < 0); + unsigned long rate = 0; + struct clk *clk = of_clk_get(np, 0); + WARN_ON(IS_ERR(clk)); + rate = clk_get_rate(clk); u = readl(timer_base + TIMER_CTRL_OFF); writel(u & ~(TIMER0_25MHZ | TIMER1_25MHZ), timer_base + TIMER_CTRL_OFF); - timer_clk = clk / TIMER_DIVIDER; + timer_clk = rate / TIMER_DIVIDER; } /* We use timer 0 as clocksource, and timer 1 for diff --git a/include/linux/clk/mvebu.h b/include/linux/clk/mvebu.h new file mode 100644 index 000000000000..8c4ae713b063 --- /dev/null +++ b/include/linux/clk/mvebu.h @@ -0,0 +1,22 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CLK_MVEBU_H_ +#define __CLK_MVEBU_H_ + +void __init mvebu_clocks_init(void); + +#endif |