diff options
23 files changed, 1145 insertions, 372 deletions
diff --git a/Documentation/devicetree/bindings/clock/altr_socfpga.txt b/Documentation/devicetree/bindings/clock/altr_socfpga.txt index 0045433eae1..5dfd145d3cc 100644 --- a/Documentation/devicetree/bindings/clock/altr_socfpga.txt +++ b/Documentation/devicetree/bindings/clock/altr_socfpga.txt @@ -23,3 +23,8 @@ Optional properties: and the bit index. - div-reg : For "socfpga-gate-clk", div-reg contains the divider register, bit shift, and width. +- clk-phase : For the sdmmc_clk, contains the value of the clock phase that controls + the SDMMC CIU clock. The first value is the clk_sample(smpsel), and the second + value is the cclk_in_drv(drvsel). The clk-phase is used to enable the correct + hold/delay times that is needed for the SD/MMC CIU clock. The values of both + can be 0-315 degrees, in 45 degree increments. diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt index 7c52c29d99f..700e7aac371 100644 --- a/Documentation/devicetree/bindings/clock/clock-bindings.txt +++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt @@ -44,6 +44,23 @@ 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. + +For example, if we have two clocks <&oscillator 1> and <&oscillator 3>: + + oscillator { + compatible = "myclocktype"; + #clock-cells = <1>; + clock-indices = <1>, <3>; + clock-output-names = "clka", "clkb"; + } + + This ensures we do not have any empty nodes in clock-output-names + + ==Clock consumers== Required properties: diff --git a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt index 1e662948661..307a503c5db 100644 --- a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt +++ b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt @@ -11,6 +11,18 @@ The following is a list of provided IDs and clock names on Armada 370/XP: 3 = hclk (DRAM control clock) 4 = dramclk (DDR clock) +The following is a list of provided IDs and clock names on Armada 375: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = l2clk (L2 Cache clock) + 3 = ddrclk (DDR clock) + +The following is a list of provided IDs and clock names on Armada 380/385: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = l2clk (L2 Cache clock) + 3 = ddrclk (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) @@ -20,6 +32,8 @@ The following is a list of provided IDs and clock names on Kirkwood and Dove: Required properties: - compatible : shall be one of the following: "marvell,armada-370-core-clock" - For Armada 370 SoC core clocks + "marvell,armada-375-core-clock" - For Armada 375 SoC core clocks + "marvell,armada-380-core-clock" - For Armada 380/385 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) diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt index fc2910fa7e4..76477be742b 100644 --- a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt +++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt @@ -1,9 +1,10 @@ * Gated Clock bindings for Marvell EBU SoCs -Marvell Armada 370/XP, 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 +Marvell Armada 370/375/380/385/XP, 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: @@ -22,6 +23,60 @@ ID Clock Peripheral 28 ddr DDR Cntrl 30 sata1 SATA Host 0 +The following is a list of provided IDs for Armada 375: +ID Clock Peripheral +----------------------------------- +2 mu Management Unit +3 pp Packet Processor +4 ptp PTP +5 pex0 PCIe 0 Clock out +6 pex1 PCIe 1 Clock out +8 audio Audio Cntrl +11 nd_clk Nand Flash Cntrl +14 sata0_link SATA 0 Link +15 sata0_core SATA 0 Core +16 usb3 USB3 Host +17 sdio SDHCI Host +18 usb USB Host +19 gop Gigabit Ethernet MAC +20 sata1_link SATA 1 Link +21 sata1_core SATA 1 Core +22 xor0 XOR DMA 0 +23 xor1 XOR DMA 0 +24 copro Coprocessor +25 tdm Time Division Mplx +28 crypto0_enc Cryptographic Unit Port 0 Encryption +29 crypto0_core Cryptographic Unit Port 0 Core +30 crypto1_enc Cryptographic Unit Port 1 Encryption +31 crypto1_core Cryptographic Unit Port 1 Core + +The following is a list of provided IDs for Armada 380/385: +ID Clock Peripheral +----------------------------------- +0 audio Audio +2 ge2 Gigabit Ethernet 2 +3 ge1 Gigabit Ethernet 1 +4 ge0 Gigabit Ethernet 0 +5 pex1 PCIe 1 +6 pex2 PCIe 2 +7 pex3 PCIe 3 +8 pex0 PCIe 0 +9 usb3h0 USB3 Host 0 +10 usb3h1 USB3 Host 1 +11 usb3d USB3 Device +13 bm Buffer Management +14 crypto0z Cryptographic 0 Z +15 sata0 SATA 0 +16 crypto1z Cryptographic 1 Z +17 sdio SDIO +18 usb2 USB 2 +21 crypto1 Cryptographic 1 +22 xor0 XOR 0 +23 crypto0 Cryptographic 0 +25 tdm Time Division Multiplexing +28 xor1 XOR 1 +30 sata1 SATA 1 + The following is a list of provided IDs for Armada XP: ID Clock Peripheral ----------------------------------- @@ -95,6 +150,8 @@ ID Clock Peripheral Required properties: - compatible : shall be one of the following: "marvell,armada-370-gating-clock" - for Armada 370 SoC clock gating + "marvell,armada-375-gating-clock" - for Armada 375 SoC clock gating + "marvell,armada-380-gating-clock" - for Armada 380/385 SoC clock gating "marvell,armada-xp-gating-clock" - for Armada XP SoC clock gating "marvell,dove-gating-clock" - for Dove SoC clock gating "marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 537f1a5c07f..3d62f47bead 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -415,6 +415,7 @@ compatible = "altr,socfpga-gate-clk"; clocks = <&f2s_periph_ref_clk>, <&main_nand_sdmmc_clk>, <&per_nand_mmc_clk>; clk-gate = <0xa0 8>; + clk-phase = <0 135>; }; nand_x_clk: nand_x_clk { diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index dd0d49cdbe0..d86231e11b3 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -29,7 +29,6 @@ void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE)); void __iomem *sys_manager_base_addr; void __iomem *rst_manager_base_addr; -void __iomem *clk_mgr_base_addr; unsigned long cpu1start_addr; static struct map_desc scu_io_desc __initdata = { @@ -78,9 +77,6 @@ void __init socfpga_sysmgr_init(void) np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr"); rst_manager_base_addr = of_iomap(np, 0); - - np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); - clk_mgr_base_addr = of_iomap(np, 0); } static void __init socfpga_init_irq(void) @@ -106,7 +102,6 @@ static void __init socfpga_cyclone5_init(void) { l2x0_of_init(0, ~0UL); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - socfpga_init_clocks(); } static const char *altera_dt_match[] = { diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c42e608af6b..895b3d204e2 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1339,8 +1339,11 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) if (clk->notifier_count) ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); - if (ret & NOTIFY_STOP_MASK) + if (ret & NOTIFY_STOP_MASK) { + pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n", + __func__, clk->name, ret); goto out; + } hlist_for_each_entry(child, &clk->children, child_node) { ret = __clk_speculate_rates(child, new_rate); @@ -2260,20 +2263,11 @@ void __clk_put(struct clk *clk) * re-enter into the clk framework by calling any top-level clk APIs; * this will cause a nested prepare_lock mutex. * - * Pre-change notifier callbacks will be passed the current, pre-change - * rate of the clk via struct clk_notifier_data.old_rate. The new, - * post-change rate of the clk is passed via struct - * clk_notifier_data.new_rate. - * - * Post-change notifiers will pass the now-current, post-change rate of - * the clk in both struct clk_notifier_data.old_rate and struct + * In all notification cases cases (pre, post and abort rate change) the + * original clock rate is passed to the callback via struct + * clk_notifier_data.old_rate and the new frequency is passed via struct * clk_notifier_data.new_rate. * - * Abort-change notifiers are effectively the opposite of pre-change - * notifiers: the original pre-change clk rate is passed in via struct - * clk_notifier_data.new_rate and the failed post-change rate is passed - * in via struct clk_notifier_data.old_rate. - * * clk_notifier_register() must be called from non-atomic context. * Returns -EINVAL if called with null arguments, -ENOMEM upon * allocation failure; otherwise, passes along the return value of @@ -2473,7 +2467,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider); struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec) { struct of_clk_provider *provider; - struct clk *clk = ERR_PTR(-ENOENT); + struct clk *clk = ERR_PTR(-EPROBE_DEFER); /* Check if we have such a provider in our array */ list_for_each_entry(provider, &of_clk_providers, link) { @@ -2506,8 +2500,12 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_count); const char *of_clk_get_parent_name(struct device_node *np, int index) { struct of_phandle_args clkspec; + struct property *prop; const char *clk_name; + const __be32 *vp; + u32 pv; int rc; + int count; if (index < 0) return NULL; @@ -2517,8 +2515,22 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) if (rc) return NULL; + index = clkspec.args_count ? clkspec.args[0] : 0; + count = 0; + + /* if there is an indices property, use it to transfer the index + * specified into an array offset for the clock-output-names property. + */ + of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) { + if (index == pv) { + index = count; + break; + } + count++; + } + if (of_property_read_string_index(clkspec.np, "clock-output-names", - clkspec.args_count ? clkspec.args[0] : 0, + index, &clk_name) < 0) clk_name = clkspec.np->name; diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 48f67218247..a360b2eca5c 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id) clk = of_clk_get_by_name(dev->of_node, con_id); if (!IS_ERR(clk)) return clk; + if (PTR_ERR(clk) == -EPROBE_DEFER) + return clk; } return clk_get_sys(dev_id, con_id); diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index c339b829d3e..693f7be129f 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -13,6 +13,14 @@ config ARMADA_370_CLK select MVEBU_CLK_CPU select MVEBU_CLK_COREDIV +config ARMADA_375_CLK + bool + select MVEBU_CLK_COMMON + +config ARMADA_38X_CLK + bool + select MVEBU_CLK_COMMON + config ARMADA_XP_CLK bool select MVEBU_CLK_COMMON diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 21bbfb4a9f4..4c66162fb0b 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -3,6 +3,8 @@ obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o +obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o +obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o obj-$(CONFIG_DOVE_CLK) += dove.o obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c new file mode 100644 index 00000000000..c991a4d95e1 --- /dev/null +++ b/drivers/clk/mvebu/armada-375.c @@ -0,0 +1,184 @@ +/* + * Marvell Armada 375 SoC clocks + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + +/* + * Core Clocks + */ + +/* + * For the Armada 375 SoCs, the CPU, DDR and L2 clocks frequencies are + * all modified at the same time, and not separately as for the Armada + * 370 or the Armada XP SoCs. + * + * SAR0[21:17] : CPU frequency DDR frequency L2 frequency + * 6 = 400 MHz 400 MHz 200 MHz + * 15 = 600 MHz 600 MHz 300 MHz + * 21 = 800 MHz 534 MHz 400 MHz + * 25 = 1000 MHz 500 MHz 500 MHz + * others reserved. + * + * SAR0[22] : TCLK frequency + * 0 = 166 MHz + * 1 = 200 MHz + */ + +#define SAR1_A375_TCLK_FREQ_OPT 22 +#define SAR1_A375_TCLK_FREQ_OPT_MASK 0x1 +#define SAR1_A375_CPU_DDR_L2_FREQ_OPT 17 +#define SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK 0x1F + +static const u32 armada_375_tclk_frequencies[] __initconst = { + 166000000, + 200000000, +}; + +static u32 __init armada_375_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select; + + tclk_freq_select = ((readl(sar) >> SAR1_A375_TCLK_FREQ_OPT) & + SAR1_A375_TCLK_FREQ_OPT_MASK); + return armada_375_tclk_frequencies[tclk_freq_select]; +} + + +static const u32 armada_375_cpu_frequencies[] __initconst = { + 0, 0, 0, 0, 0, 0, + 400000000, + 0, 0, 0, 0, 0, 0, 0, 0, + 600000000, + 0, 0, 0, 0, 0, + 800000000, + 0, 0, 0, + 1000000000, +}; + +static u32 __init armada_375_get_cpu_freq(void __iomem *sar) +{ + u8 cpu_freq_select; + + cpu_freq_select = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) & + SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK); + if (cpu_freq_select >= ARRAY_SIZE(armada_375_cpu_frequencies)) { + pr_err("Selected CPU frequency (%d) unsupported\n", + cpu_freq_select); + return 0; + } else + return armada_375_cpu_frequencies[cpu_freq_select]; +} + +enum { A375_CPU_TO_DDR, A375_CPU_TO_L2 }; + +static const struct coreclk_ratio armada_375_coreclk_ratios[] __initconst = { + { .id = A375_CPU_TO_L2, .name = "l2clk" }, + { .id = A375_CPU_TO_DDR, .name = "ddrclk" }, +}; + +static const int armada_375_cpu_l2_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {1, 2}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 2}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int armada_375_cpu_ddr_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {1, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {2, 3}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {2, 3}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_375_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) & + SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK); + + switch (id) { + case A375_CPU_TO_L2: + *mult = armada_375_cpu_l2_ratios[opt][0]; + *div = armada_375_cpu_l2_ratios[opt][1]; + break; + case A375_CPU_TO_DDR: + *mult = armada_375_cpu_ddr_ratios[opt][0]; + *div = armada_375_cpu_ddr_ratios[opt][1]; + break; + } +} + +static const struct coreclk_soc_desc armada_375_coreclks = { + .get_tclk_freq = armada_375_get_tclk_freq, + .get_cpu_freq = armada_375_get_cpu_freq, + .get_clk_ratio = armada_375_get_clk_ratio, + .ratios = armada_375_coreclk_ratios, + .num_ratios = ARRAY_SIZE(armada_375_coreclk_ratios), +}; + +static void __init armada_375_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &armada_375_coreclks); +} +CLK_OF_DECLARE(armada_375_core_clk, "marvell,armada-375-core-clock", + armada_375_coreclk_init); + +/* + * Clock Gating Control + */ +static const struct clk_gating_soc_desc armada_375_gating_desc[] __initconst = { + { "mu", NULL, 2 }, + { "pp", NULL, 3 }, + { "ptp", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 6 }, + { "audio", NULL, 8 }, + { "nd_clk", "nand", 11 }, + { "sata0_link", "sata0_core", 14 }, + { "sata0_core", NULL, 15 }, + { "usb3", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb", NULL, 18 }, + { "gop", NULL, 19 }, + { "sata1_link", "sata1_core", 20 }, + { "sata1_core", NULL, 21 }, + { "xor0", NULL, 22 }, + { "xor1", NULL, 23 }, + { "copro", NULL, 24 }, + { "tdm", NULL, 25 }, + { "crypto0_enc", NULL, 28 }, + { "crypto0_core", NULL, 29 }, + { "crypto1_enc", NULL, 30 }, + { "crypto1_core", NULL, 31 }, + { } +}; + +static void __init armada_375_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, armada_375_gating_desc); +} +CLK_OF_DECLARE(armada_375_clk_gating, "marvell,armada-375-gating-clock", + armada_375_clk_gating_init); diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c new file mode 100644 index 00000000000..8bccf4ecdab --- /dev/null +++ b/drivers/clk/mvebu/armada-38x.c @@ -0,0 +1,167 @@ +/* + * Marvell Armada 380/385 SoC clocks + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + +/* + * SAR[14:10] : Ratios between PCLK0, NBCLK, HCLK and DRAM clocks + * + * SAR[15] : TCLK frequency + * 0 = 250 MHz + * 1 = 200 MHz + */ + +#define SAR_A380_TCLK_FREQ_OPT 15 +#define SAR_A380_TCLK_FREQ_OPT_MASK 0x1 +#define SAR_A380_CPU_DDR_L2_FREQ_OPT 10 +#define SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK 0x1F + +static const u32 armada_38x_tclk_frequencies[] __initconst = { + 250000000, + 200000000, +}; + +static u32 __init armada_38x_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select; + + tclk_freq_select = ((readl(sar) >> SAR_A380_TCLK_FREQ_OPT) & + SAR_A380_TCLK_FREQ_OPT_MASK); + return armada_38x_tclk_frequencies[tclk_freq_select]; +} + +static const u32 armada_38x_cpu_frequencies[] __initconst = { + 0, 0, 0, 0, + 1066 * 1000 * 1000, 0, 0, 0, + 1332 * 1000 * 1000, 0, 0, 0, + 1600 * 1000 * 1000, +}; + +static u32 __init armada_38x_get_cpu_freq(void __iomem *sar) +{ + u8 cpu_freq_select; + + cpu_freq_select = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & + SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); + if (cpu_freq_select >= ARRAY_SIZE(armada_38x_cpu_frequencies)) { + pr_err("Selected CPU frequency (%d) unsupported\n", + cpu_freq_select); + return 0; + } + + return armada_38x_cpu_frequencies[cpu_freq_select]; +} + +enum { A380_CPU_TO_DDR, A380_CPU_TO_L2 }; + +static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = { + { .id = A380_CPU_TO_L2, .name = "l2clk" }, + { .id = A380_CPU_TO_DDR, .name = "ddrclk" }, +}; + +static const int armada_38x_cpu_l2_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_38x_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & + SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); + + switch (id) { + case A380_CPU_TO_L2: + *mult = armada_38x_cpu_l2_ratios[opt][0]; + *div = armada_38x_cpu_l2_ratios[opt][1]; + break; + case A380_CPU_TO_DDR: + *mult = armada_38x_cpu_ddr_ratios[opt][0]; + *div = armada_38x_cpu_ddr_ratios[opt][1]; + break; + } +} + +static const struct coreclk_soc_desc armada_38x_coreclks = { + .get_tclk_freq = armada_38x_get_tclk_freq, + .get_cpu_freq = armada_38x_get_cpu_freq, + .get_clk_ratio = armada_38x_get_clk_ratio, + .ratios = armada_38x_coreclk_ratios, + .num_ratios = ARRAY_SIZE(armada_38x_coreclk_ratios), +}; + +static void __init armada_38x_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &armada_38x_coreclks); +} +CLK_OF_DECLARE(armada_38x_core_clk, "marvell,armada-380-core-clock", + armada_38x_coreclk_init); + +/* + * Clock Gating Control + */ +static const struct clk_gating_soc_desc armada_38x_gating_desc[] __initconst = { + { "audio", NULL, 0 }, + { "ge2", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex1", NULL, 5 }, + { "pex2", NULL, 6 }, + { "pex3", NULL, 7 }, + { "pex0", NULL, 8 }, + { "usb3h0", NULL, 9 }, + { "usb3h1", NULL, 10 }, + { "usb3d", NULL, 11 }, + { "bm", NULL, 13 }, + { "crypto0z", NULL, 14 }, + { "sata0", NULL, 15 }, + { "crypto1z", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb2", NULL, 18 }, + { "crypto1", NULL, 21 }, + { "xor0", NULL, 22 }, + { "crypto0", NULL, 23 }, + { "tdm", NULL, 25 }, + { "xor1", NULL, 28 }, + { "sata1", NULL, 30 }, + { } +}; + +static void __init armada_38x_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, armada_38x_gating_desc); +} +CLK_OF_DECLARE(armada_38x_clk_gating, "marvell,armada-380-gating-clock", + armada_38x_clk_gating_init); diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index 7162615bcdc..4da60760be1 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -18,26 +18,56 @@ #include "common.h" #define CORE_CLK_DIV_RATIO_MASK 0xff -#define CORE_CLK_DIV_RATIO_RELOAD BIT(8) -#define CORE_CLK_DIV_ENABLE_OFFSET 24 -#define CORE_CLK_DIV_RATIO_OFFSET 0x8 +/* + * This structure describes the hardware details (bit offset and mask) + * to configure one particular core divider clock. Those hardware + * details may differ from one SoC to another. This structure is + * therefore typically instantiated statically to describe the + * hardware details. + */ struct clk_corediv_desc { unsigned int mask; unsigned int offset; unsigned int fieldbit; }; +/* + * This structure describes the hardware details to configure the core + * divider clocks on a given SoC. Amongst others, it points to the + * array of core divider clock descriptors for this SoC, as well as + * the corresponding operations to manipulate them. + */ +struct clk_corediv_soc_desc { + const struct clk_corediv_desc *descs; + unsigned int ndescs; + const struct clk_ops ops; + u32 ratio_reload; + u32 enable_bit_offset; + u32 ratio_offset; +}; + +/* + * This structure represents one core divider clock for the clock + * framework, and is dynamically allocated for each core divider clock + * existing in the current SoC. + */ struct clk_corediv { struct clk_hw hw; void __iomem *reg; - struct clk_corediv_desc desc; + const struct clk_corediv_desc *desc; + const struct clk_corediv_soc_desc *soc_desc; spinlock_t lock; }; static struct clk_onecell_data clk_data; -static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = { +/* + * Description of the core divider clocks available. For now, we + * support only NAND, and it is available at the same register + * locations regardless of the SoC. + */ +static const struct clk_corediv_desc mvebu_corediv_desc[] = { { .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */ }; @@ -46,8 +76,9 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = { static int clk_corediv_is_enabled(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; - u32 enable_mask = BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; + u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset; return !!(readl(corediv->reg) & enable_mask); } @@ -55,14 +86,15 @@ static int clk_corediv_is_enabled(struct clk_hw *hwclk) static int clk_corediv_enable(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg; spin_lock_irqsave(&corediv->lock, flags); reg = readl(corediv->reg); - reg |= (BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET); + reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset); writel(reg, corediv->reg); spin_unlock_irqrestore(&corediv->lock, flags); @@ -73,14 +105,15 @@ static int clk_corediv_enable(struct clk_hw *hwclk) static void clk_corediv_disable(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg; spin_lock_irqsave(&corediv->lock, flags); reg = readl(corediv->reg); - reg &= ~(BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET); + reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset); writel(reg, corediv->reg); spin_unlock_irqrestore(&corediv->lock, flags); @@ -90,10 +123,11 @@ static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk, unsigned long parent_rate) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; u32 reg, div; - reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + reg = readl(corediv->reg + soc_desc->ratio_offset); div = (reg >> desc->offset) & desc->mask; return parent_rate / div; } @@ -117,7 +151,8 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long parent_rate) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg, div; @@ -126,17 +161,17 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, spin_lock_irqsave(&corediv->lock, flags); /* Write new divider to the divider ratio register */ - reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + reg = readl(corediv->reg + soc_desc->ratio_offset); reg &= ~(desc->mask << desc->offset); reg |= (div & desc->mask) << desc->offset; - writel(reg, corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + writel(reg, corediv->reg + soc_desc->ratio_offset); /* Set reload-force for this clock */ reg = readl(corediv->reg) | BIT(desc->fieldbit); writel(reg, corediv->reg); /* Now trigger the clock update */ - reg = readl(corediv->reg) | CORE_CLK_DIV_RATIO_RELOAD; + reg = readl(corediv->reg) | soc_desc->ratio_reload; writel(reg, corediv->reg); /* @@ -144,7 +179,7 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, * ratios request and the reload request. */ udelay(1000); |