diff options
Diffstat (limited to 'drivers/clk')
63 files changed, 10649 insertions, 1088 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a47e6ee98b8..0357ac44638 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -55,6 +55,16 @@ config COMMON_CLK_MAX77686 ---help--- This driver supports Maxim 77686 crystal oscillator clock. +config COMMON_CLK_SI5351 + tristate "Clock driver for SiLabs 5351A/B/C" + depends on I2C + depends on OF + select REGMAP_I2C + select RATIONAL + ---help--- + This driver supports Silicon Labs 5351A/B/C programmable clock + generators. + config CLK_TWL6040 tristate "External McPDM functional clock from twl6040" depends on TWL6040_CORE @@ -63,6 +73,14 @@ config CLK_TWL6040 McPDM. McPDM module is using the external bit clock on the McPDM bus as functional clock. +config COMMON_CLK_AXI_CLKGEN + tristate "AXI clkgen driver" + depends on ARCH_ZYNQ || MICROBLAZE + help + ---help--- + Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx + FPGAs. It is commonly used in Analog Devices' reference designs. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 300d4775d92..137d3e730f8 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o +obj-$(CONFIG_COMMON_CLK) += clk-composite.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o @@ -23,14 +24,18 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ obj-$(CONFIG_X86) += x86/ # Chip specific +obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c new file mode 100644 index 00000000000..8137327847c --- /dev/null +++ b/drivers/clk/clk-axi-clkgen.c @@ -0,0 +1,331 @@ +/* + * AXI clkgen driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + * + */ + +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/err.h> + +#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 +#define AXI_CLKGEN_REG_CLK_OUT1 0x08 +#define AXI_CLKGEN_REG_CLK_OUT2 0x0c +#define AXI_CLKGEN_REG_CLK_DIV 0x10 +#define AXI_CLKGEN_REG_CLK_FB1 0x14 +#define AXI_CLKGEN_REG_CLK_FB2 0x18 +#define AXI_CLKGEN_REG_LOCK1 0x1c +#define AXI_CLKGEN_REG_LOCK2 0x20 +#define AXI_CLKGEN_REG_LOCK3 0x24 +#define AXI_CLKGEN_REG_FILTER1 0x28 +#define AXI_CLKGEN_REG_FILTER2 0x2c + +struct axi_clkgen { + void __iomem *base; + struct clk_hw clk_hw; +}; + +static uint32_t axi_clkgen_lookup_filter(unsigned int m) +{ + switch (m) { + case 0: + return 0x01001990; + case 1: + return 0x01001190; + case 2: + return 0x01009890; + case 3: + return 0x01001890; + case 4: + return 0x01008890; + case 5 ... 8: + return 0x01009090; + case 9 ... 11: + return 0x01000890; + case 12: + return 0x08009090; + case 13 ... 22: + return 0x01001090; + case 23 ... 36: + return 0x01008090; + case 37 ... 46: + return 0x08001090; + default: + return 0x08008090; + } +} + +static const uint32_t axi_clkgen_lock_table[] = { + 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, + 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, + 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, + 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271, + 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4, + 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190, + 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e, + 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c, + 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, +}; + +static uint32_t axi_clkgen_lookup_lock(unsigned int m) +{ + if (m < ARRAY_SIZE(axi_clkgen_lock_table)) + return axi_clkgen_lock_table[m]; + return 0x1f1f00fa; +} + +static const unsigned int fpfd_min = 10000; +static const unsigned int fpfd_max = 300000; +static const unsigned int fvco_min = 600000; +static const unsigned int fvco_max = 1200000; + +static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, + unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) +{ + unsigned long d, d_min, d_max, _d_min, _d_max; + unsigned long m, m_min, m_max; + unsigned long f, dout, best_f, fvco; + + fin /= 1000; + fout /= 1000; + + best_f = ULONG_MAX; + *best_d = 0; + *best_m = 0; + *best_dout = 0; + + d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1); + d_max = min_t(unsigned long, fin / fpfd_min, 80); + + m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1); + m_max = min_t(unsigned long, fvco_max * d_max / fin, 64); + + for (m = m_min; m <= m_max; m++) { + _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max)); + _d_max = min(d_max, fin * m / fvco_min); + + for (d = _d_min; d <= _d_max; d++) { + fvco = fin * m / d; + + dout = DIV_ROUND_CLOSEST(fvco, fout); + dout = clamp_t(unsigned long, dout, 1, 128); + f = fvco / dout; + if (abs(f - fout) < abs(best_f - fout)) { + best_f = f; + *best_d = d; + *best_m = m; + *best_dout = dout; + if (best_f == fout) + return; + } + } + } +} + +static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low, + unsigned int *high, unsigned int *edge, unsigned int *nocount) +{ + if (divider == 1) + *nocount = 1; + else + *nocount = 0; + + *high = divider / 2; + *edge = divider % 2; + *low = divider - *high; +} + +static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val) +{ + writel(val, axi_clkgen->base + reg); +} + +static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + *val = readl(axi_clkgen->base + reg); +} + +static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) +{ + return container_of(clk_hw, struct axi_clkgen, clk_hw); +} + +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, + unsigned long rate, unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int nocount; + unsigned int high; + unsigned int edge; + unsigned int low; + uint32_t filter; + uint32_t lock; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + filter = axi_clkgen_lookup_filter(m - 1); + lock = axi_clkgen_lookup_lock(m - 1); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); + + axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, + (edge << 13) | (nocount << 12) | (high << 6) | low); + + axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, + (((lock >> 16) & 0x1f) << 10) | 0x1); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, + (((lock >> 24) & 0x1f) << 10) | 0x3e9); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + + return 0; +} + +static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int d, m, dout; + + axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + return *parent_rate / d * m / dout; +} + +static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, + unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int reg; + unsigned long long tmp; + + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); + dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); + d = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); + m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + + if (d == 0 || dout == 0) + return 0; + + tmp = (unsigned long long)(parent_rate / d) * m; + do_div(tmp, dout); + + if (tmp > ULONG_MAX) + return ULONG_MAX; + + return tmp; +} + +static const struct clk_ops axi_clkgen_ops = { + .recalc_rate = axi_clkgen_recalc_rate, + .round_rate = axi_clkgen_round_rate, + .set_rate = axi_clkgen_set_rate, +}; + +static int axi_clkgen_probe(struct platform_device *pdev) +{ + struct axi_clkgen *axi_clkgen; + struct clk_init_data init; + const char *parent_name; + const char *clk_name; + struct resource *mem; + struct clk *clk; + + axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); + if (!axi_clkgen) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(axi_clkgen->base)) + return PTR_ERR(axi_clkgen->base); + + parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + if (!parent_name) + return -EINVAL; + + clk_name = pdev->dev.of_node->name; + of_property_read_string(pdev->dev.of_node, "clock-output-names", + &clk_name); + + init.name = clk_name; + init.ops = &axi_clkgen_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + axi_clkgen->clk_hw.init = &init; + clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, + clk); +} + +static int axi_clkgen_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id axi_clkgen_ids[] = { + { .compatible = "adi,axi-clkgen-1.00.a" }, + { }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + +static struct platform_driver axi_clkgen_driver = { + .driver = { + .name = "adi-axi-clkgen", + .owner = THIS_MODULE, + .of_match_table = axi_clkgen_ids, + }, + .probe = axi_clkgen_probe, + .remove = axi_clkgen_remove, +}; +module_platform_driver(axi_clkgen_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator"); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c new file mode 100644 index 00000000000..a33f46f20a4 --- /dev/null +++ b/drivers/clk/clk-composite.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. + * + * 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/err.h> +#include <linux/slab.h> + +#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + +static u8 clk_composite_get_parent(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->get_parent(mux_hw); +} + +static int clk_composite_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->set_parent(mux_hw, index); +} + +static unsigned long clk_composite_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *rate_ops = composite->rate_ops; + struct clk_hw *rate_hw = composite->rate_hw; + + rate_hw->clk = hw->clk; + + return rate_ops->recalc_rate(rate_hw, parent_rate); +} + +static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *rate_ops = composite->rate_ops; + struct clk_hw *rate_hw = composite->rate_hw; + + rate_hw->clk = hw->clk; + + return rate_ops->round_rate(rate_hw, rate, prate); +} + +static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *rate_ops = composite->rate_ops; + struct clk_hw *rate_hw = composite->rate_hw; + + rate_hw->clk = hw->clk; + + return rate_ops->set_rate(rate_hw, rate, parent_rate); +} + +static int clk_composite_is_enabled(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->is_enabled(gate_hw); +} + +static int clk_composite_enable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->enable(gate_hw); +} + +static void clk_composite_disable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + gate_ops->disable(gate_hw); +} + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char **parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk *clk; + struct clk_init_data init; + struct clk_composite *composite; + struct clk_ops *clk_composite_ops; + + composite = kzalloc(sizeof(*composite), GFP_KERNEL); + if (!composite) { + pr_err("%s: could not allocate composite clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = num_parents; + + clk_composite_ops = &composite->ops; + + if (mux_hw && mux_ops) { + if (!mux_ops->get_parent || !mux_ops->set_parent) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->mux_hw = mux_hw; + composite->mux_ops = mux_ops; + clk_composite_ops->get_parent = clk_composite_get_parent; + clk_composite_ops->set_parent = clk_composite_set_parent; + } + + if (rate_hw && rate_ops) { + if (!rate_ops->recalc_rate) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + /* .round_rate is a prerequisite for .set_rate */ + if (rate_ops->round_rate) { + clk_composite_ops->round_rate = clk_composite_round_rate; + if (rate_ops->set_rate) { + clk_composite_ops->set_rate = clk_composite_set_rate; + } + } else { + WARN(rate_ops->set_rate, + "%s: missing round_rate op is required\n", + __func__); + } + + composite->rate_hw = rate_hw; + composite->rate_ops = rate_ops; + clk_composite_ops->recalc_rate = clk_composite_recalc_rate; + } + + if (gate_hw && gate_ops) { + if (!gate_ops->is_enabled || !gate_ops->enable || + !gate_ops->disable) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->gate_hw = gate_hw; + composite->gate_ops = gate_ops; + clk_composite_ops->is_enabled = clk_composite_is_enabled; + clk_composite_ops->enable = clk_composite_enable; + clk_composite_ops->disable = clk_composite_disable; + } + + init.ops = clk_composite_ops; + composite->hw.init = &init; + + clk = clk_register(dev, &composite->hw); + if (IS_ERR(clk)) + goto err; + + if (composite->mux_hw) + composite->mux_hw->clk = clk; + + if (composite->rate_hw) + composite->rate_hw->clk = clk; + + if (composite->gate_hw) + composite->gate_hw->clk = clk; + + return clk; + +err: + kfree(composite); + return clk; +} diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 68b40210117..6d967416043 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -109,8 +109,9 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, div = _get_div(divider, val); if (!div) { - WARN(1, "%s: Invalid divisor for clock %s\n", __func__, - __clk_get_name(hw->clk)); + WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), + "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", + __clk_get_name(hw->clk)); return parent_rate; } diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 1ef271e4759..9ff7d510faa 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -11,6 +11,7 @@ #include <linux/clk-provider.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/of.h> /* * DOC: basic fixed multiplier and divider clock that cannot gate @@ -96,3 +97,38 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, return clk; } +#ifdef CONFIG_OF +/** + * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock + */ +void __init of_fixed_factor_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent_name; + u32 div, mult; + + if (of_property_read_u32(node, "clock-div", &div)) { + pr_err("%s Fixed factor clock <%s> must have a clock-div property\n", + __func__, node->name); + return; + } + + if (of_property_read_u32(node, "clock-mult", &mult)) { + pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n", + __func__, node->name); + return; + } + + of_property_read_string(node, "clock-output-names", &clk_name); + parent_name = of_clk_get_parent_name(node, 0); + + clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, + mult, div); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup); +CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock", + of_fixed_factor_clk_setup); +#endif diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 508c032edce..25b1734560d 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -32,6 +32,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) { struct clk_mux *mux = to_clk_mux(hw); + int num_parents = __clk_get_num_parents(hw->clk); u32 val; /* @@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) * val = 0x4 really means "bit 2, index starts at bit 0" */ val = readl(mux->reg) >> mux->shift; - val &= (1 << mux->width) - 1; + val &= mux->mask; + + if (mux->table) { + int i; + + for (i = 0; i < num_parents; i++) + if (mux->table[i] == val) + return i; + return -EINVAL; + } if (val && (mux->flags & CLK_MUX_INDEX_BIT)) val = ffs(val) - 1; @@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) if (val && (mux->flags & CLK_MUX_INDEX_ONE)) val--; - if (val >= __clk_get_num_parents(hw->clk)) + if (val >= num_parents) return -EINVAL; return val; @@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) u32 val; unsigned long flags = 0; - if (mux->flags & CLK_MUX_INDEX_BIT) - index = (1 << ffs(index)); + if (mux->table) + index = mux->table[index]; - if (mux->flags & CLK_MUX_INDEX_ONE) - index++; + else { + if (mux->flags & CLK_MUX_INDEX_BIT) + index = (1 << ffs(index)); + + if (mux->flags & CLK_MUX_INDEX_ONE) + index++; + } if (mux->lock) spin_lock_irqsave(mux->lock, flags); val = readl(mux->reg); - val &= ~(((1 << mux->width) - 1) << mux->shift); + val &= ~(mux->mask << mux->shift); val |= index << mux->shift; writel(val, mux->reg); @@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = { }; EXPORT_SYMBOL_GPL(clk_mux_ops); -struct clk *clk_register_mux(struct device *dev, const char *name, +struct clk *clk_register_mux_table(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags, spinlock_t *lock) + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_mux *mux; struct clk *clk; @@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name, /* struct clk_mux assignments */ mux->reg = reg; mux->shift = shift; - mux->width = width; + mux->mask = mask; mux->flags = clk_mux_flags; mux->lock = lock; + mux->table = table; mux->hw.init = &init; clk = clk_register(dev, &mux->hw); @@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name, return clk; } + +struct clk *clk_register_mux(struct device *dev, const char *name, + const char **parent_names, u8 num_parents, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags, spinlock_t *lock) +{ + u32 mask = BIT(width) - 1; + + return clk_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + NULL, lock); +} diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index f8e9d0c27be..643ca653fef 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void) for (i = pll1; i < maxclk; i++) { prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); - BUG_ON(!prima2_clks[i]); + BUG_ON(IS_ERR(prima2_clks[i])); } clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); clk_register_clkdev(prima2_clks[io], NULL, "io"); diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c new file mode 100644 index 00000000000..24f553673b7 --- /dev/null +++ b/drivers/clk/clk-si5351.c @@ -0,0 +1,1520 @@ +/* + * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator + * + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Rabeeh Khoury <rabeeh@solid-run.com> + * + * References: + * [1] "Si5351A/B/C Data Sheet" + * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf + * [2] "Manually Generating an Si5351 Register Map" + * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/rational.h> +#include <linux/i2c.h> +#include <linux/of_platform.h> +#include <linux/platform_data/si5351.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <asm/div64.h> + +#include "clk-si5351.h" + +struct si5351_driver_data; + +struct si5351_parameters { + unsigned long p1; + unsigned long p2; + unsigned long p3; + int valid; +}; + +struct si5351_hw_data { + struct clk_hw hw; + struct si5351_driver_data *drvdata; + struct si5351_parameters params; + unsigned char num; +}; + +struct si5351_driver_data { + enum si5351_variant variant; + struct i2c_client *client; + struct regmap *regmap; + struct clk_onecell_data onecell; + + struct clk *pxtal; + const char *pxtal_name; + struct clk_hw xtal; + struct clk *pclkin; + const char *pclkin_name; + struct clk_hw clkin; + + struct si5351_hw_data pll[2]; + struct si5351_hw_data *msynth; + struct si5351_hw_data *clkout; +}; + +static const char const *si5351_input_names[] = { + "xtal", "clkin" +}; +static const char const *si5351_pll_names[] = { + "plla", "pllb", "vxco" +}; +static const char const *si5351_msynth_names[] = { + "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7" +}; +static const char const *si5351_clkout_names[] = { + "clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7" +}; + +/* + * Si5351 i2c regmap + */ +static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg) +{ + u32 val; + int ret; + + ret = regmap_read(drvdata->regmap, reg, &val); + if (ret) { + dev_err(&drvdata->client->dev, + "unable to read from reg%02x\n", reg); + return 0; + } + + return (u8)val; +} + +static inline int si5351_bulk_read(struct si5351_driver_data *drvdata, + u8 reg, u8 count, u8 *buf) +{ + return regmap_bulk_read(drvdata->regmap, reg, buf, count); +} + +static inline int si5351_reg_write(struct si5351_driver_data *drvdata, + u8 reg, u8 val) +{ + return regmap_write(drvdata->regmap, reg, val); +} + +static inline int si5351_bulk_write(struct si5351_driver_data *drvdata, + u8 reg, u8 count, const u8 *buf) +{ + return regmap_raw_write(drvdata->regmap, reg, buf, count); +} + +static inline int si5351_set_bits(struct si5351_driver_data *drvdata, + u8 reg, u8 mask, u8 val) +{ + return regmap_update_bits(drvdata->regmap, reg, mask, val); +} + +static inline u8 si5351_msynth_params_address(int num) +{ + if (num > 5) + return SI5351_CLK6_PARAMETERS + (num - 6); + return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num); +} + +static void si5351_read_parameters(struct si5351_driver_data *drvdata, + u8 reg, struct si5351_parameters *params) +{ + u8 buf[SI5351_PARAMETERS_LENGTH]; + + switch (reg) { + case SI5351_CLK6_PARAMETERS: + case SI5351_CLK7_PARAMETERS: + buf[0] = si5351_reg_read(drvdata, reg); + params->p1 = buf[0]; + params->p2 = 0; + params->p3 = 1; + break; + default: + si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf); + params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4]; + params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7]; + params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1]; + } + params->valid = 1; +} + +static void si5351_write_parameters(struct si5351_driver_data *drvdata, + u8 reg, struct si5351_parameters *params) +{ + u8 buf[SI5351_PARAMETERS_LENGTH]; + + switch (reg) { + case SI5351_CLK6_PARAMETERS: + case SI5351_CLK7_PARAMETERS: + buf[0] = params->p1 & 0xff; + si5351_reg_write(drvdata, reg, buf[0]); + break; + default: + buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff; + buf[1] = params->p3 & 0xff; + /* save rdiv and divby4 */ + buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03; + buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03; + buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff; + buf[4] = params->p1 & 0xff; + buf[5] = ((params->p3 & 0xf0000) >> 12) | + ((params->p2 & 0xf0000) >> 16); + buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff; + buf[7] = params->p2 & 0xff; + si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf); + } +} + +static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SI5351_DEVICE_STATUS: + case SI5351_INTERRUPT_STATUS: + case SI5351_PLL_RESET: + return true; + } + return false; +} + +static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + /* reserved registers */ + if (reg >= 4 && reg <= 8) + return false; + if (reg >= 10 && reg <= 14) + return false; + if (reg >= 173 && reg <= 176) + return false; + if (reg >= 178 && reg <= 182) + return false; + /* read-only */ + if (reg == SI5351_DEVICE_STATUS) + return false; + return true; +} + +static struct regmap_config si5351_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 187, + .writeable_reg = si5351_regmap_is_writeable, + .volatile_reg = si5351_regmap_is_volatile, +}; + +/* + * Si5351 xtal clock input + */ +static int si5351_xtal_prepare(struct clk_hw *hw) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, xtal); + si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, + SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE); + return 0; +} + +static void si5351_xtal_unprepare(struct clk_hw *hw) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, xtal); + si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, + SI5351_XTAL_ENABLE, 0); +} + +static const struct clk_ops si5351_xtal_ops = { + .prepare = si5351_xtal_prepare, + .unprepare = si5351_xtal_unprepare, +}; + +/* + * Si5351 clkin clock input (Si5351C only) + */ +static int si5351_clkin_prepare(struct clk_hw *hw) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, clkin); + si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, + SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE); + return 0; +} + +static void si5351_clkin_unprepare(struct clk_hw *hw) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, clkin); + si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, + SI5351_CLKIN_ENABLE, 0); +} + +/* + * CMOS clock source constraints: + * The input frequency range of the PLL is 10Mhz to 40MHz. + * If CLKIN is >40MHz, the input divider must be used. + */ +static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_driver_data *drvdata = + container_of(hw, struct si5351_driver_data, clkin); + unsigned long rate; + unsigned char idiv; + + rate = parent_rate; + if (parent_rate > 160000000) { + idiv = SI5351_CLKIN_DIV_8; + rate /= 8; + } else if (parent_rate > 80000000) { + idiv = SI5351_CLKIN_DIV_4; + rate /= 4; + } else if (parent_rate > 40000000) { + idiv = SI5351_CLKIN_DIV_2; + rate /= 2; + } else { + idiv = SI5351_CLKIN_DIV_1; + } + + si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, + SI5351_CLKIN_DIV_MASK, idiv); + + dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n", + __func__, (1 << (idiv >> 6)), rate); + + return rate; +} + +static const struct clk_ops si5351_clkin_ops = { + .prepare = si5351_clkin_prepare, + .unprepare = si5351_clkin_unprepare, + .recalc_rate = si5351_clkin_recalc_rate, +}; + +/* + * Si5351 vxco clock input (Si5351B only) + */ + +static int si5351_vxco_prepare(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n"); + + return 0; +} + +static void si5351_vxco_unprepare(struct clk_hw *hw) +{ +} + +static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 0; +} + +static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent) +{ + return 0; +} + +static const struct clk_ops si5351_vxco_ops = { + .prepare = si5351_vxco_prepare, + .unprepare = si5351_vxco_unprepare, + .recalc_rate = si5351_vxco_recalc_rate, + .set_rate = si5351_vxco_set_rate, +}; + +/* + * Si5351 pll a/b + * + * Feedback Multisynth Divider Equations [2] + * + * fVCO = fIN * (a + b/c) + * + * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and + * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV + * + * Feedback Multisynth Register Equations + * + * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512 + * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c + * (3) MSNx_P3[19:0] = c + * + * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c + * + * Using (4) on (1) yields: + * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512 + * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c + * + * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128 + * = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3) + * + */ +static int _si5351_pll_reparent(struct si5351_driver_data *drvdata, + int num, enum si5351_pll_src parent) +{ + u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE; + + if (parent == SI5351_PLL_SRC_DEFAULT) + return 0; + + if (num > 2) + return -EINVAL; + + if (drvdata->variant != SI5351_VARIANT_C && + parent != SI5351_PLL_SRC_XTAL) + return -EINVAL; + + si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask, + (parent == SI5351_PLL_SRC_XTAL) ? 0 : mask); + return 0; +} + +static unsigned char si5351_pll_get_parent(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE; + u8 val; + + val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE); + + return (val & mask) ? 1 : 0; +} + +static int si5351_pll_set_parent(struct clk_hw *hw, u8 index) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + if (hwdata->drvdata->variant != SI5351_VARIANT_C && + index > 0) + return -EPERM; + + if (index > 1) + return -EINVAL; + + return _si5351_pll_reparent(hwdata->drvdata, hwdata->num, + (index == 0) ? SI5351_PLL_SRC_XTAL : + SI5351_PLL_SRC_CLKIN); +} + +static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS : + SI5351_PLLB_PARAMETERS; + unsigned long long rate; + + if (!hwdata->params.valid) + si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); + + if (hwdata->params.p3 == 0) + return parent_rate; + + /* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */ + rate = hwdata->params.p1 * hwdata->params.p3; + rate += 512 * hwdata->params.p3; + rate += hwdata->params.p2; + rate *= parent_rate; + do_div(rate, 128 * hwdata->params.p3); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), + hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, + parent_rate, (unsigned long)rate); + + return (unsigned long)rate; +} + +static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned long rfrac, denom, a, b, c; + unsigned long long lltmp; + + if (rate < SI5351_PLL_VCO_MIN) + rate = SI5351_PLL_VCO_MIN; + if (rate > SI5351_PLL_VCO_MAX) + rate = SI5351_PLL_VCO_MAX; + + /* determine integer part of feedback equation */ + a = rate / *parent_rate; + + if (a < SI5351_PLL_A_MIN) + rate = *parent_rate * SI5351_PLL_A_MIN; + if (a > SI5351_PLL_A_MAX) + rate = *parent_rate * SI5351_PLL_A_MAX; + + /* find best approximation for b/c = fVCO mod fIN */ + denom = 1000 * 1000; + lltmp = rate % (*parent_rate); + lltmp *= denom; + do_div(lltmp, *parent_rate); + rfrac = (unsigned long)lltmp; + + b = 0; + c = 1; + if (rfrac) + rational_best_approximation(rfrac, denom, + SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c); + + /* calculate parameters */ + hwdata->params.p3 = c; + hwdata->params.p2 = (128 * b) % c; + hwdata->params.p1 = 128 * a; + hwdata->params.p1 += (128 * b / c); + hwdata->params.p1 -= 512; + + /* recalculate rate by fIN * (a + b/c) */ + lltmp = *parent_rate; + lltmp *= b; + do_div(lltmp, c); + + rate = (unsigned long)lltmp; + rate += *parent_rate * a; + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), a, b, c, + *parent_rate, rate); + + return rate; +} + +static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS : + SI5351_PLLB_PARAMETERS; + + /* write multisynth parameters */ + si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params); + + /* plla/pllb ctrl is in clk6/clk7 ctrl registers */ + si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num, + SI5351_CLK_INTEGER_MODE, + (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), + hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, + parent_rate, rate); + + return 0; +} + +static const struct clk_ops si5351_pll_ops = { + .set_parent = si5351_pll_set_parent, + .get_parent = si5351_pll_get_parent, + .recalc_rate = si5351_pll_recalc_rate, + .round_rate = si5351_pll_round_rate, + .set_rate = si5351_pll_set_rate, +}; + +/* + * Si5351 multisync divider + * + * for fOUT <= 150 MHz: + * + * fOUT = (fIN * (a + b/c)) / CLKOUTDIV + * + * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and + * fIN = fVCO0, fVCO1 + * + * Output Clock Multisynth Register Equations + * + * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512 + * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c + * MSx_P3[19:0] = c + * + * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0 + * + * for 150MHz < fOUT <= 160MHz: + * + * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b + */ +static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata, + int num, enum si5351_multisynth_src parent) +{ + if (parent == SI5351_MULTISYNTH_SRC_DEFAULT) + return 0; + + if (num > 8) + return -EINVAL; + + si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT, + (parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 : + SI5351_CLK_PLL_SELECT); + return 0; +} + +static unsigned char si5351_msynth_get_parent(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 val; + + val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num); + + return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0; +} + +static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num, + (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 : + SI5351_MULTISYNTH_SRC_VCO1); +} + +static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 reg = si5351_msynth_params_address(hwdata->num); + unsigned long long rate; + unsigned long m; + + if (!hwdata->params.valid) + si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); + + if (hwdata->params.p3 == 0) + return parent_rate; + + /* + * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3) + * multisync6-7: fOUT = fIN / P1 + */ + rate = parent_rate; + if (hwdata->num > 5) { + m = hwdata->params.p1; + } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) & + SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) { + m = 4; + } else { + rate *= 128 * hwdata->params.p3; + m = hwdata->params.p1 * hwdata->params.p3; + m += hwdata->params.p2; + m += 512 * hwdata->params.p3; + } + + if (m == 0) + return 0; + do_div(rate, m); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), + hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, + m, parent_rate, (unsigned long)rate); + + return (unsigned long)rate; +} + +static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned long long lltmp; + unsigned long a, b, c; + int divby4; + + /* multisync6-7 can only handle freqencies < 150MHz */ + if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ) + rate = SI5351_MULTISYNTH67_MAX_FREQ; + + /* multisync frequency is 1MHz .. 160MHz */ + if (rate > SI5351_MULTISYNTH_MAX_FREQ) + rate = SI5351_MULTISYNTH_MAX_FREQ; + if (rate < SI5351_MULTISYNTH_MIN_FREQ) + rate = SI5351_MULTISYNTH_MIN_FREQ; + + divby4 = 0; + if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ) + divby4 = 1; + + /* multisync can set pll */ + if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) { + /* + * find largest integer divider for max + * vco frequency and given target rate + */ + if (divby4 == 0) { + lltmp = SI5351_PLL_VCO_MAX; + do_div(lltmp, rate); + a = (unsigned long)lltmp; + } else + a = 4; + + b = 0; + c = 1; + + *parent_rate = a * rate; + } else { + unsigned long rfrac, denom; + + /* disable divby4 */ + if (divby4) { + rate = SI5351_MULTISYNTH_DIVBY4_FREQ; + divby4 = 0; + } + + /* determine integer part of divider equation */ + a = *parent_rate / rate; + if (a < SI5351_MULTISYNTH_A_MIN) + a = SI5351_MULTISYNTH_A_MIN; + if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX) + a = SI5351_MULTISYNTH67_A_MAX; + else if (a > SI5351_MULTISYNTH_A_MAX) + a = SI5351_MULTISYNTH_A_MAX; + + /* find best approximation for b/c = fVCO mod fOUT */ + denom = 1000 * 1000; + lltmp = (*parent_rate) % rate; + lltmp *= denom; + do_div(lltmp, rate); + rfrac = (unsigned long)lltmp; + + b = 0; + c = 1; + if (rfrac) + rational_best_approximation(rfrac, denom, + SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX, + &b, &c); + } + + /* recalculate rate by fOUT = fIN / (a + b/c) */ + lltmp = *parent_rate; + lltmp *= c; + do_div(lltmp, a * c + b); + rate = (unsigned long)lltmp; + + /* calculate parameters */ + if (divby4) { + hwdata->params.p3 = 1; + hwdata->params.p2 = 0; + hwdata->params.p1 = 0; + } else { + hwdata->params.p3 = c; + hwdata->params.p2 = (128 * b) % c; + hwdata->params.p1 = 128 * a; + hwdata->params.p1 += (128 * b / c); + hwdata->params.p1 -= 512; + } + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4, + *parent_rate, rate); + + return rate; +} + +static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + u8 reg = si5351_msynth_params_address(hwdata->num); + int divby4 = 0; + + /* write multisynth parameters */ + si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params); + + if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ) + divby4 = 1; + + /* enable/disable integer mode and divby4 on multisynth0-5 */ + if (hwdata->num < 6) { + si5351_set_bits(hwdata->drvdata, reg + 2, + SI5351_OUTPUT_CLK_DIVBY4, + (divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0); + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_INTEGER_MODE, + (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0); + } + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), + hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, + divby4, parent_rate, rate); + + return 0; +} + +static const struct clk_ops si5351_msynth_ops = { + .set_parent = si5351_msynth_set_parent, + .get_parent = si5351_msynth_get_parent, + .recalc_rate = si5351_msynth_recalc_rate, + .round_rate = si5351_msynth_round_rate, + .set_rate = si5351_msynth_set_rate, +}; + +/* + * Si5351 clkout divider + */ +static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata, + int num, enum si5351_clkout_src parent) +{ + u8 val; + + if (num > 8) + return -EINVAL; + + switch (parent) { + case SI5351_CLKOUT_SRC_MSYNTH_N: + val = SI5351_CLK_INPUT_MULTISYNTH_N; + break; + case SI5351_CLKOUT_SRC_MSYNTH_0_4: + /* clk0/clk4 can only connect to its own multisync */ + if (num == 0 || num == 4) + val = SI5351_CLK_INPUT_MULTISYNTH_N; + else + val = SI5351_CLK_INPUT_MULTISYNTH_0_4; + break; + case SI5351_CLKOUT_SRC_XTAL: + val = SI5351_CLK_INPUT_XTAL; + break; + case SI5351_CLKOUT_SRC_CLKIN: + if (drvdata->variant != SI5351_VARIANT_C) + return -EINVAL; + + val = SI5351_CLK_INPUT_CLKIN; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, + SI5351_CLK_INPUT_MASK, val); + return 0; +} + +static int _si5351_clkout_set_drive_strength( + struct si5351_driver_data *drvdata, int num, + enum si5351_drive_strength drive) +{ + u8 mask; + + if (num > 8) + return -EINVAL; + + switch (drive) { + case SI5351_DRIVE_2MA: + mask = SI5351_CLK_DRIVE_STRENGTH_2MA; + break; + case SI5351_DRIVE_4MA: + mask = SI5351_CLK_DRIVE_STRENGTH_4MA; + break; + case SI5351_DRIVE_6MA: + mask = SI5351_CLK_DRIVE_STRENGTH_6MA; + break; + case SI5351_DRIVE_8MA: + mask = SI5351_CLK_DRIVE_STRENGTH_8MA; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, + SI5351_CLK_DRIVE_STRENGTH_MASK, mask); + return 0; +} + +static int si5351_clkout_prepare(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_POWERDOWN, 0); + si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, + (1 << hwdata->num), 0); + return 0; +} + +static void si5351_clkout_unprepare(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN); + si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, + (1 << hwdata->num), (1 << hwdata->num)); +} + +static u8 si5351_clkout_get_parent(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + int index = 0; + unsigned char val; + + val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num); + switch (val & SI5351_CLK_INPUT_MASK) { + case SI5351_CLK_INPUT_MULTISYNTH_N: + index = 0; + break; + case SI5351_CLK_INPUT_MULTISYNTH_0_4: + index = 1; + break; + case SI5351_CLK_INPUT_XTAL: + index = 2; + break; + case SI5351_CLK_INPUT_CLKIN: + index = 3; + break; + } + + return index; +} + +static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT; + + switch (index) { + case 0: + parent = SI5351_CLKOUT_SRC_MSYNTH_N; + break; + case 1: + parent = SI5351_CLKOUT_SRC_MSYNTH_0_4; + break; + case 2: + parent = SI5351_CLKOUT_SRC_XTAL; + break; + case 3: + parent = SI5351_CLKOUT_SRC_CLKIN; + break; + } + + return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent); +} + +static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned char reg; + unsigned char rdiv; + + if (hwdata->num <= 5) + reg = si5351_msynth_params_address(hwdata->num) + 2; + else + reg = SI5351_CLK6_7_OUTPUT_DIVIDER; + + rdiv = si5351_reg_read(hwdata->drvdata, reg); + if (hwdata->num == 6) { + rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK; + } else { + rdiv &= SI5351_OUTPUT_CLK_DIV_MASK; + rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT; + } + + return parent_rate >> rdiv; +} + +static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned char rdiv; + + /* clkout6/7 can only handle output freqencies < 150MHz */ + if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ) + rate = SI5351_CLKOUT67_MAX_FREQ; + + /* clkout freqency is 8kHz - 160MHz */ + if (rate > SI5351_CLKOUT_MAX_FREQ) + rate = SI5351_CLKOUT_MAX_FREQ; + if (rate < SI5351_CLKOUT_MIN_FREQ) + rate = SI5351_CLKOUT_MIN_FREQ; + + /* request frequency if multisync master */ + if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) { + /* use r divider for frequencies below 1MHz */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + while (rate < SI5351_MULTISYNTH_MIN_FREQ && + rdiv < SI5351_OUTPUT_CLK_DIV_128) { + rdiv += 1; + rate *= 2; + } + *parent_rate = rate; + } else { + unsigned long new_rate, new_err, err; + + /* round to closed rdiv */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + new_rate = *parent_rate; + err = abs(new_rate - rate); + do { + new_rate >>= 1; + new_err = abs(new_rate - rate); + if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128) + break; + rdiv++; + err = new_err; + } while (1); + } + rate = *parent_rate >> rdiv; + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv), + *parent_rate, rate); + + return rate; +} + +static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + unsigned long new_rate, new_err, err; + unsigned char rdiv; + + /* round to closed rdiv */ + rdiv = SI5351_OUTPUT_CLK_DIV_1; + new_rate = parent_rate; + err = abs(new_rate - rate); + do { + new_rate >>= 1; + new_err = abs(new_rate - rate); + if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128) + break; + rdiv++; + err = new_err; + } while (1); + + /* write output divider */ + switch (hwdata->num) { + case 6: + si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER, + SI5351_OUTPUT_CLK6_DIV_MASK, rdiv); + break; + case 7: + si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER, + SI5351_OUTPUT_CLK_DIV_MASK, + rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT); + break; + default: + si5351_set_bits(hwdata->drvdata, + si5351_msynth_params_address(hwdata->num) + 2, + SI5351_OUTPUT_CLK_DIV_MASK, + rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT); + } + + /* powerup clkout */ + si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, + SI5351_CLK_POWERDOWN, 0); + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", + __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv), + parent_rate, rate); + + return 0; +} + +static const struct clk_ops si5351_clkout_ops = { + .prepare = si5351_clkout_prepare, + .unprepare = si5351_clkout_unprepare, + .set_parent = si5351_clkout_set_parent, + .get_parent = si5351_clkout_get_parent, + .recalc_rate = si5351_clkout_recalc_rate, + .round_rate = si5351_clkout_round_rate, + .set_rate = si5351_clkout_set_rate, +}; + +/* + * Si5351 i2c probe and DT + */ +#ifdef CONFIG_OF +static const struct of_device_id si5351_dt_ids[] = { + { .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, }, + { .compatible = "silabs,si5351a-msop", + .data = (void *)SI5351_VARIANT_A3, }, + { .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, }, + { .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, }, + { } +}; +MODULE_DEVICE_TABLE(of, si5351_dt_ids); + +static int si5351_dt_parse(struct i2c_client *client) +{ + struct device_node *child, *np = client->dev.of_node; + struct si5351_platform_data *pdata; + const struct of_device_id *match; + struct property *prop; + const __be32 *p; + int num = 0; + u32 val; + + if (np == NULL) + return 0; + + match = of_match_node(si5351_dt_ids, np); + if (match == NULL) + return -EINVAL; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->variant = (enum si5351_variant)match->data; + pdata->clk_xtal = of_clk_get(np, 0); + if (!IS_ERR(pdata->clk_xtal)) + clk_put(pdata->clk_xtal); + pdata->clk_clkin = of_clk_get(np, 1); + if (!IS_ERR(pdata->clk_clkin)) + clk_put(pdata->clk_clkin); + + /* + * property silabs,pll-source : <num src>, [<..>] + * allow to selectively set pll source + */ + of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) { + if (num >= 2) { + dev_err(&client->dev, + "invalid pll %d on pll-source prop\n", num); + return -EINVAL; + } + + p = of_prop_next_u32(prop, p, &val); + if (!p) { + dev_err(&client->dev, + "missing pll-source for pll %d\n", num); + return -EINVAL; + } + + switch (val) { + case 0: + pdata->pll_src[num] = SI5351_PLL_SRC_XTAL; + break; + case 1: + if (pdata->variant != SI5351_VARIANT_C) { + dev_err(&client->dev, + "invalid parent %d for pll %d\n", + val, num); + return -EINVAL; + } + pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN; + break; + default: + dev_err(&client->dev, + "invalid parent %d for pll %d\n", val, num); + return -EINVAL; + } + } + + /* per clkout properties */ + for_each_child_of_node(np, child) { + if (of_property_read_u32(child, "reg", &num)) { + dev_err(&client->dev, "missing reg property of %s\n", + child->name); + return -EINVAL; + } + + if (num >= 8 || + (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) { + dev_err(&client->dev, "invalid clkout %d\n", num); + return -EINVAL; + } + + if (!of_property_read_u32(child, "silabs,multisynth-source", + &val)) { + switch (val) { + case 0: + pdata->clkout[num].multisynth_src = + SI5351_MULTISYNTH_SRC_VCO0; + break; + case 1: + pdata->clkout[num].multisynth_src = + SI5351_MULTISYNTH_SRC_VCO1; + break; + default: + dev_err(&client->dev, + "invalid parent %d for multisynth %d\n", + val, num); + return -EINVAL; + } + } + + if (!of_property_read_u32(child, "silabs,clock-source", &val)) { + switch (val) { + case 0: + pdata->clkout[num].clkout_src = + SI5351_CLKOUT_SRC_MSYNTH_N; + break; + case 1: + pdata->clkout[num].clkout_src = + SI5351_CLKOUT_SRC_MSYNTH_0_4; + break; + case 2: + pdata->clkout[num].clkout_src = + SI5351_CLKOUT_SRC_XTAL; + break; + case 3: + if (pdata->variant != SI5351_VARIANT_C) { + dev_err(&client->dev, + "invalid parent %d for clkout %d\n", + val, num); + return -EINVAL; + } + pdata->clkout[num].clkout_src = + SI5351_CLKOUT_SRC_CLKIN; + break; + default: + dev_err(&client->dev, + "invalid parent %d for clkout %d\n", + val, num); + return -EINVAL; + } + } + + if (!of_property_read_u32(child, "silabs,drive-strength", + &val)) { + switch (val) { + case SI5351_DRIVE_2MA: + case SI5351_DRIVE_4MA: + case SI5351_DRIVE_6MA: + case SI5351_DRIVE_8MA: + pdata->clkout[num].drive = val; + break; + default: + dev_err(&client->dev, + "invalid drive strength %d for clkout %d\n", + val, num); + return -EINVAL; + } + } + + if (!of_property_read_u32(child, "clock-frequency", &val)) + pdata->clkout[num].rate = val; + + pdata->clkout[num].pll_master = + of_property_read_bool(child, "silabs,pll-master"); + } + client->dev.platform_data = pdata; + + return 0; +} +#else +static int si5351_dt_parse(struct i2c_client *client) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int si5351_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si5351_platform_data *pdata; + struct si5351_driver_data *drvdata; + struct clk_init_data init; + struct clk *clk; + const char *parent_names[4]; + u8 num_parents, num_clocks; + int ret, n; + + ret = si5351_dt_parse(client); + if (ret) + return ret; + + pdata = client->dev.platform_data; + if (!pdata) + return -EINVAL; + + drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL); + if (drvdata == NULL) { + dev_err(&client->dev, "unable to allocate driver data\n"); + return -ENOMEM; + } + + i2c_set_clientdata(client, drvdata); + drvdata->client = client; + drvdata->variant = pdata->variant; + drvdata->pxtal = pdata->clk_xtal; + drvdata->pclkin = pdata->clk_clkin; + + drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config); + if (IS_ERR(drvdata->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(drvdata->regmap); + } + + /* Disable interrupts */ + si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0); + /* Set disabled output drivers to drive low */ + si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00); + si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00); + /* Ensure pll select is on XTAL for Si5351A/B */ + if (drvdata->variant != SI5351_VARIANT_C) + si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, + SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0); + + /* setup clock configuration */ + for (n = 0; n < 2; n++) { + ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]); + if (ret) { + dev_err(&client->dev, + "failed to reparent pll %d to %d\n", + n, pdata->pll_src[n]); + return ret; + } + } + + for (n = 0; n < 8; n++) { + ret = _si5351_msynth_reparent(drvdata, n, + pdata->clkout[n].multisynth_src); + if (ret) { + dev_err(&client->dev, + "failed to reparent multisynth %d to %d\n", + n, pdata->clkout[n].multisynth_src); + return ret; + } + + ret = _si5351_clkout_reparent(drvdata, n, + pdata->clkout[n].clkout_src); + if (ret) { + dev_err(&client->dev, + "failed to reparent clkout %d to %d\n", + n, pdata->clkout[n].clkout_src); + return ret; + } + + ret = _si5351_clkout_set_drive_strength(drvdata, n, + pdata->clkout[n].drive); + if (ret) { + dev_err(&client->dev, + "failed set drive strength of clkout%d to %d\n", + n, pdata->clkout[n].drive); + return ret; + } + } + + /* register xtal input clock gate */ + memset(&init, 0, sizeof(init)); + init.name = si5351_input_names[0]; + init.ops = &si5351_xtal_ops; + init.flags = 0; + if (!IS_ERR(drvdata->pxtal)) { + drvdata->pxtal_name = __clk_get_name(drvdata->pxtal); + init.parent_names = &drvdata->pxtal_name; + init.num_parents = 1; + } + drvdata->xtal.init = &init; + clk = devm_clk_register(&client->dev, &drvdata->xtal); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return PTR_ERR(clk); + } + + /* register clkin input clock gate */ + if (drvdata->variant == SI5351_VARIANT_C) { + memset(&init, 0, sizeof(init)); + init.name = si5351_input_names[1]; + init.ops = &si5351_clkin_ops; + if (!IS_ERR(drvdata->pclkin)) { + drvdata->pclkin_name = __clk_get_name(drvdata->pclkin); + init.parent_names = &drvdata->pclkin_name; + init.num_parents = 1; + } + drvdata->clkin.init = &init; + clk = devm_clk_register(&client->dev, &drvdata->clkin); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + return PTR_ERR(clk); + } + } + + /* Si5351C allows to mux either xtal or clkin to PLL input */ + num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1; + parent_names[0] = si5351_input_names[0]; + parent_names[1] = si5351_input_names[1]; + + /* register PLLA */ + drvdata->pll[0].num = 0; + drvdata->pll[0].drvdata = drvdata; + drvdata->pll[0].hw.init = &init; + memset(&init, 0, sizeof(init)); + init.name = si5351_pll_names[0]; + init.ops = &si5351_pll_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return -EINVAL; + } + + /* register PLLB or VXCO (Si5351B) */ + drvdata->pll[1].num = 1; + drvdata->pll[1].drvdata = drvdata; + drvdata->pll[1].hw.init = &init; + memset(&init, 0, sizeof(init)); + if (drvdata->variant == SI5351_VARIANT_B) { + init.name = si5351_pll_names[2]; + init.ops = &si5351_vxco_ops; + init.flags = CLK_IS_ROOT; + init.parent_names = NULL; + init.num_parents = 0; + } else { + init.name = si5351_pll_names[1]; + init.ops = &si5351_pll_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + } + clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + return -EINVAL; + } + + /* register clk multisync and clk out divider */ + num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8; + parent_names[0] = si5351_pll_names[0]; + if (drvdata->variant == SI5351_VARIANT_B) + parent_names[1] = si5351_pll_names[2]; + else + parent_names[1] = si5351_pll_names[1]; + + drvdata->msynth = devm_kzalloc(&client->dev, num_clocks * + sizeof(*drvdata->msynth), GFP_KERNEL); + drvdata->clkout = devm_kzalloc(&client->dev, num_clocks * + sizeof(*drvdata->clkout), GFP_KERNEL); + + drvdata->onecell.clk_num = num_clocks; + drvdata->onecell.clks = devm_kzalloc(&client->dev, + num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL); + + if (WARN_ON(!drvdata->msynth || !drvdata->clkout || + !drvdata->onecell.clks)) + return -ENOMEM; + + for (n = 0; n < num_clocks; n++) { + drvdata->msynth[n].num = n; + drvdata->msynth[n].drvdata = drvdata; + drvdata->msynth[n].hw.init = &init; + memset(&init, 0, sizeof(init)); + init.name = si5351_msynth_names[n]; + init.ops = &si5351_msynth_ops; + init.flags = 0; + if (pdata->clkout[n].pll_master) + init.flags |= CLK_SET_RATE_PARENT; + init.parent_names = parent_names; + init.num_parents = 2; + clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + return -EINVAL; + } + } + + num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3; + parent_names[2] = si5351_input_names[0]; + parent_names[3] = si5351_input_names[1]; + for (n = 0; n < num_clocks; n++) { + parent_names[0] = si5351_msynth_names[n]; + parent_names[1] = (n < 4) ? si5351_msynth_names[0] : + si5351_msynth_names[4]; + + drvdata->clkout[n].num = n; + drvdata->clkout[n].drvdata = drvdata; + drvdata->clkout[n].hw.init = &init; + memset(&init, 0, sizeof(init)); + init.name = si5351_clkout_names[n]; + init.ops = &si5351_clkout_ops; + init.flags = 0; + if (pdata->clkout[n].clkout_src == SI5351_CLKOUT_SRC_MSYNTH_N) + init.flags |= CLK_SET_RATE_PARENT; + init.parent_names = parent_names; + init.num_parents = num_parents; + clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + return -EINVAL; + } + drvdata->onecell.clks[n] = clk; + + /* set initial clkout rate */ + if (pdata->clkout[n].rate != 0) { + int ret; + ret = clk_set_rate(clk, pdata->clkout[n].rate); + if (ret != 0) { + dev_err(&client->dev, "Cannot set rate : %d\n", + ret); + } + } + } + + ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, + &drvdata->onecell); + if (ret) { + dev_err(&client->dev, "unable to add clk provider\n"); + return ret; + } + + return 0; +} + +static const struct i2c_device_id si5351_i2c_ids[] = { + { "silabs,si5351", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids); + +static struct i2c_driver si5351_driver = { + .driver = { + .name = "si5351", + .of_match_table = of_match_ptr(si5351_dt_ids), + }, + .probe = si5351_i2c_probe, + .id_table = si5351_i2c_ids, +}; +module_i2c_driver(si5351_driver); + +MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com"); +MODULE_DESCRIPTION("Silicon Labs Si5351A/B/C clock generator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h new file mode 100644 index 00000000000..af41b5080f4 --- /dev/null +++ b/drivers/clk/clk-si5351.h @@ -0,0 +1,155 @@ +/* + * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator + * + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Rabeeh Khoury <rabeeh@solid-run.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _CLK_SI5351_H_ +#define _CLK_SI5351_H_ + +#define SI5351_BUS_BASE_ADDR 0x60 + +#define SI5351_PLL_VCO_MIN 600000000 +#define SI5351_PLL_VCO_MAX 900000000 +#define SI5351_MULTISYNTH_MIN_FREQ 1000000 +#define SI5351_MULTISYNTH_DIVBY4_FREQ 150000000 +#define SI5351_MULTISYNTH_MAX_FREQ 160000000 +#define SI5351_MULTISYNTH67_MAX_FREQ SI5351_MULTISYNTH_DIVBY4_FREQ +#define SI5351_CLKOUT_MIN_FREQ 8000 +#define SI5351_CLKOUT_MAX_FREQ SI5351_MULTISYNTH_MAX_FREQ +#define SI5351_CLKOUT67_MAX_FREQ SI5351_MULTISYNTH67_MAX_FREQ + +#define SI5351_PLL_A_MIN 15 +#define SI5351_PLL_A_MAX 90 +#define SI5351_PLL_B_MAX (SI5351_PLL_C_MAX-1) +#define SI5351_PLL_C_MAX 1048575 +#define SI5351_MULTISYNTH_A_MIN 6 +#define SI5351_MULTISYNTH_A_MAX 1800 +#define SI5351_MULTISYNTH67_A_MAX 254 +#define SI5351_MULTISYNTH_B_MAX (SI5351_MULTISYNTH_C_MAX-1) +#define SI5351_MULTISYNTH_C_MAX 1048575 +#define SI5351_MULTISYNTH_P1_MAX ((1<<18)-1) +#define SI5351_MULTISYNTH_P2_MAX ((1<<20)-1) +#define SI5351_MULTISYNTH_P3_MAX ((1<<20)-1) + +#define SI5351_DEVICE_STATUS 0 +#define SI5351_INTERRUPT_STATUS 1 +#define SI5351_INTERRUPT_MASK 2 +#define SI5351_STATUS_SYS_INIT (1<<7) +#define SI5351_STATUS_LOL_B (1<<6) +#define SI5351_STATUS_LOL_A (1<<5) +#define SI5351_STATUS_LOS (1<<4) +#define SI5351_OUTPUT_ENABLE_CTRL 3 +#define SI5351_OEB_PIN_ENABLE_CTRL 9 +#define SI5351_PLL_INPUT_SOURCE 15 +#define SI5351_CLKIN_DIV_MASK (3<<6) +#define SI5351_CLKIN_DIV_1 (0<<6) +#define SI5351_CLKIN_DIV_2 (1<<6) +#define SI5351_CLKIN_DIV_4 (2<<6) +#define SI5351_CLKIN_DIV_8 (3<<6) +#define SI5351_PLLB_SOURCE (1<<3) +#define SI5351_PLLA_SOURCE (1<<2) + +#define SI5351_CLK0_CTRL 16 +#define SI5351_CLK1_CTRL 17 +#define SI5351_CLK2_CTRL 18 +#define SI5351_CLK3_CTRL 19 +#define SI5351_CLK4_CTRL 20 +#define SI5351_CLK5_CTRL 21 +#define SI5351_CLK6_CTRL 22 +#define SI5351_CLK7_CTRL 23 +#define SI5351_CLK_POWERDOWN (1<<7) +#define SI5351_CLK_INTEGER_MODE (1<<6) +#define SI5351_CLK_PLL_SELECT (1<<5) +#define SI5351_CLK_INVERT (1<<4) +#define SI5351_CLK_INPUT_MASK (3<<2) +#define SI5351_CLK_INPUT_XTAL (0<<2) +#define SI5351_CLK_INPUT_CLKIN (1<<2) +#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2) +#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2) +#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0) +#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0) +#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0) +#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0) +#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0) + +#define SI5351_CLK3_0_DISABLE_STATE 24 +#define SI5351_CLK7_4_DISABLE_STATE 25 +#define SI5351_CLK_DISABLE_STATE_LOW 0 +#define SI5351_CLK_DISABLE_STATE_HIGH 1 +#define SI5351_CLK_DISABLE_STATE_FLOAT 2 +#define SI5351_CLK_DISABLE_STATE_NEVER 3 + +#define SI5351_PARAMETERS_LENGTH 8 +#define SI5351_PLLA_PARAMETERS 26 +#define SI5351_PLLB_PARAMETERS 34 +#define SI5351_CLK0_PARAMETERS 42 +#define SI5351_CLK1_PARAMETERS 50 +#define SI5351_CLK2_PARAMETERS 58 +#define SI5351_CLK3_PARAMETERS 66 +#define SI5351_CLK4_PARAMETERS 74 +#define SI5351_CLK5_PARAMETERS 82 +#define SI5351_CLK6_PARAMETERS 90 +#define SI5351_CLK7_PARAMETERS 91 +#define SI5351_CLK6_7_OUTPUT_DIVIDER 92 +#define SI5351_OUTPUT_CLK_DIV_MASK (7 << 4) +#define SI5351_OUTPUT_CLK6_DIV_MASK (7 << 0) +#define SI5351_OUTPUT_CLK_DIV_SHIFT 4 +#define SI5351_OUTPUT_CLK_DIV6_SHIFT 0 +#define SI5351_OUTPUT_CLK_DIV_1 0 +#define SI5351_OUTPUT_CLK_DIV_2 1 +#define SI5351_OUTPUT_CLK_DIV_4 2 +#define SI5351_OUTPUT_CLK_DIV_8 3 +#define SI5351_OUTPUT_CLK_DIV_16 4 +#define SI5351_OUTPUT_CLK_DIV_32 5 +#define SI5351_OUTPUT_CLK_DIV_64 6 +#define SI5351_OUTPUT_CLK_DIV_128 7 +#define SI5351_OUTPUT_CLK_DIVBY4 (3<<2) + +#define SI5351_SSC_PARAM0 149 +#define SI5351_SSC_PARAM1 150 +#define SI5351_SSC_PARAM2 151 +#define SI5351_SSC_PARAM3 152 +#define SI5351_SSC_PARAM4 153 +#define SI5351_SSC_PARAM5 154 +#define SI5351_SSC_PARAM6 155 +#define SI5351_SSC_PARAM7 156 +#define SI5351_SSC_PARAM8 157 +#define SI5351_SSC_PARAM9 158 +#define SI5351_SSC_PARAM10 159 +#define SI5351_SSC_PARAM11 160 +#define SI5351_SSC_PARAM12 161 + +#define SI5351_VXCO_PARAMETERS_LOW 162 +#define SI5351_VXCO_PARAMETERS_MID 163 +#define SI5351_VXCO_PARAMETERS_HIGH 164 + +#define SI5351_CLK0_PHASE_OFFSET 165 +#define SI5351_CLK1_PHASE_OFFSET 166 +#define SI5351_CLK2_PHASE_OFFSET 167 +#define SI5351_CLK3_PHASE_OFFSET 168 +#define SI5351_CLK4_PHASE_OFFSET 169 +#define SI5351_CLK5_PHASE_OFFSET 170 + +#define SI5351_PLL_RESET 177 +#define SI5351_PLL_RESET_B (1<<7) +#define SI5351_PLL_RESET_A (1<<5) + +#define SI5351_CRYSTAL_LOAD 183 +#define SI5351_CRYSTAL_LOAD_MASK (3<<6) +#define SI5351_CRYSTAL_LOAD_6PF (1<<6) +#define SI5351_CRYSTAL_LOAD_8PF (2<<6) +#define SI5351_CRYSTAL_LOAD_10PF (3<<6) + +#define SI5351_FANOUT_ENABLE 187 +#define SI5351_CLKIN_ENABLE (1<<7) +#define SI5351_XTAL_ENABLE (1<<6) +#define SI5351_MULTISYNTH_ENABLE (1<<4) + +#endif diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index b5538bba7a1..553ac35bcc9 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -157,7 +157,7 @@ static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate, divisor = parent_rate / rate; /* If prate / rate would be decimal, incr the divisor */ - if (rate * divisor < *prate) + if (rate * divisor < parent_rate) divisor++; if (divisor == cdev->div_mask + 1) @@ -183,7 +183,7 @@ static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate, writel(divisor, cdev->div_reg); vt8500_pmc_wait_busy(); - spin_lock_irqsave(cdev->lock, flags); + spin_unlock_irqrestore(cdev->lock, flags); return 0; } @@ -488,6 +488,7 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, case PLL_TYPE_WM8750: wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2); pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2); + break; default: pr_err("%s: invalid pll type\n", __func__); return 0; @@ -523,6 +524,7 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate, case PLL_TYPE_WM8750: wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2); round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2); + break; default: round_rate = 0; } diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c index b14a25f3925..32062977f45 100644 --- a/drivers/clk/clk-zynq.c +++ b/drivers/clk/clk-zynq.c @@ -20,6 +20,7 @@ #include <linux/slab.h> #include <linux/kernel.h> #include <linux/clk-provider.h> +#include <linux/clk/zynq.h> static void __iomem *slcr_base; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ed87b240580..934cfd18f72 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -19,14 +19,77 @@ #include <linux/of.h> #include <linux/device.h> #include <linux/init.h> +#include <linux/sched.h> static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); +static struct task_struct *prepare_owner; +static struct task_struct *enable_owner; + +static int prepare_refcnt; +static int enable_refcnt; + static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); +/*** locking ***/ +static void clk_prepare_lock(void) +{ + if (!mutex_trylock(&prepare_lock)) { + if (prepare_owner == current) { + prepare_refcnt++; + return; + } + mutex_lock(&prepare_lock); + } + WARN_ON_ONCE(prepare_owner != NULL); + WARN_ON_ONCE(prepare_refcnt != 0); + prepare_owner = current; + prepare_refcnt = 1; +} + +static void clk_prepare_unlock(void) +{ + WARN_ON_ONCE(prepare_owner != current); + WARN_ON_ONCE(prepare_refcnt == 0); + + if (--prepare_refcnt) + return; + prepare_owner = NULL; + mutex_unlock(&prepare_lock); +} + +static unsigned long clk_enable_lock(void) +{ + unsigned long flags; + + if (!spin_trylock_irqsave(&enable_lock, flags)) { + if (enable_owner == current) { + enable_refcnt++; + return flags; + } + spin_lock_irqsave(&enable_lock, flags); + } + WARN_ON_ONCE(enable_owner != NULL); + WARN_ON_ONCE(enable_refcnt != 0); + enable_owner = current; + enable_refcnt = 1; + return flags; +} + +static void clk_enable_unlock(unsigned long flags) +{ + WARN_ON_ONCE(enable_owner != current); + WARN_ON_ONCE(enable_refcnt == 0); + + if (--enable_refcnt) + return; + enable_owner = NULL; + spin_unlock_irqrestore(&enable_lock, flags); +} + /*** debugfs support ***/ #ifdef CONFIG_COMMON_CLK_DEBUG @@ -69,7 +132,7 @@ static int clk_summary_show(struct seq_file *s, void *data) seq_printf(s, " clock enable_cnt prepare_cnt rate\n"); seq_printf(s, "---------------------------------------------------------------------\n"); - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(c, &clk_root_list, child_node) clk_summary_show_subtree(s, c, 0); @@ -77,7 +140,7 @@ static int clk_summary_show(struct seq_file *s, void *data) hlist_for_each_entry(c, &clk_orphan_list, child_node) clk_summary_show_subtree(s, c, 0); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -130,7 +193,7 @@ static int clk_dump(struct seq_file *s, void *data) seq_printf(s, "{"); - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(c, &clk_root_list, child_node) { if (!first_node) @@ -144,7 +207,7 @@ static int clk_dump(struct seq_file *s, void *data) clk_dump_subtree(s, c, 0); } - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); seq_printf(s, "}"); return 0; @@ -280,6 +343,39 @@ out: } /** + * clk_debug_reparent - reparent clk node in the debugfs clk tree + * @clk: the clk being reparented + * @new_parent: the new clk parent, may be NULL + * + * Rename clk entry in the debugfs clk tree if debugfs has been + * initialized. Otherwise it bails out early since the debugfs clk tree + * will be created lazily by clk_debug_init as part of a late_initcall. + * + * Caller must hold prepare_lock. + */ +static void clk_debug_reparent(struct clk *clk, struct clk *new_parent) +{ + struct dentry *d; + struct dentry *new_parent_d; + + if (!inited) + return; + + if (new_parent) + new_parent_d = new_parent->dentry; + else + new_parent_d = orphandir; + + d = debugfs_rename(clk->dentry->d_parent, clk->dentry, + new_parent_d, clk->name); + if (d) + clk->dentry = d; + else + pr_debug("%s: failed to rename debugfs entry for %s\n", + __func__, clk->name); +} + +/** * clk_debug_init - lazily create the debugfs clk tree visualization * * clks are often initialized very early during boot before memory can @@ -316,7 +412,7 @@ static int __init clk_debug_init(void) if (!orphandir) return -ENOMEM; - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(clk, &clk_root_list, child_node) clk_debug_create_subtree(clk, rootdir); @@ -326,16 +422,45 @@ static int __init clk_debug_init(void) inited = 1; - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } late_initcall(clk_debug_init); #else static inline int clk_debug_register(struct clk *clk) { return 0; } +static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent) +{ +} #endif /* caller must hold prepare_lock */ +static void clk_unprepare_unused_subtree(struct clk *clk) +{ + struct clk *child; + + if (!clk) + return; + + hlist_for_each_entry(child, &clk->children, child_node) + clk_unprepare_unused_subtree(child); + + if (clk->prepare_count) + return; + + if (clk->flags & CLK_IGNORE_UNUSED) + return; + + if (__clk_is_prepared(clk)) { + if (clk->ops->unprepare_unused) + clk->ops->unprepare_unused(clk->hw); + else if (clk->ops->unprepare) + clk->ops->unprepare(clk->hw); + } +} +EXPORT_SYMBOL_GPL(__clk_get_flags); + +/* caller must hold prepare_lock */ static void clk_disable_unused_subtree(struct clk *clk) { struct clk *child; @@ -347,7 +472,7 @@ static void clk_disable_unused_subtree(struct clk *clk) hlist_for_each_entry(child, &clk->children, child_node) clk_disable_unused_subtree(child); - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) goto unlock_out; @@ -368,17 +493,30 @@ static void clk_disable_unused_subtree(struct clk *clk) } unlock_out: - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); out: return; } +static bool clk_ignore_unused; +static int __init clk_ignore_unused_setup(char *__unused) +{ + clk_ignore_unused = true; + return 1; +} +__setup("clk_ignore_unused", clk_ignore_unused_setup); + static int clk_disable_unused(void) { struct clk *clk; - mutex_lock(&prepare_lock); + if (clk_ignore_unused) { + pr_warn("clk: Not disabling unused clocks\n"); + return 0; + } + + clk_prepare_lock(); hlist_for_each_entry(clk, &clk_root_list, child_node) clk_disable_unused_subtree(clk); @@ -386,7 +524,13 @@ static int clk_disable_unused(void) hlist_for_each_entry(clk, &clk_orphan_list, child_node) clk_disable_unused_subtree(clk); - mutex_unlock(&prepare_lock); + hlist_for_each_entry(clk, &clk_root_list, child_node) + clk_unprepare_unused_subtree(clk); + + hlist_for_each_entry(clk, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(clk); + + clk_prepare_unlock(); return 0; } @@ -451,6 +595,27 @@ unsigned long __clk_get_flags(struct clk *clk) return !clk ? 0 : clk->flags; } +bool __clk_is_prepared(struct clk *clk) +{ + int ret; + + if (!clk) + return false; + + /* + * .is_prepared is optional for clocks that can prepare + * fall back to software usage counter if it is missing + */ + if (!clk->ops->is_prepared) { + ret = clk->prepare_count ? 1 : 0; + goto out; + } + + ret = clk->ops->is_prepared(clk->hw); +out: + return !!ret; +} + bool __clk_is_enabled(struct clk *clk) { int ret; @@ -548,9 +713,9 @@ void __clk_unprepare(struct clk *clk) */ void clk_unprepare(struct clk *clk) { - mutex_lock(&prepare_lock); + clk_prepare_lock(); __clk_unprepare(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); } EXPORT_SYMBOL_GPL(clk_unprepare); @@ -596,9 +761,9 @@ int clk_prepare(struct clk *clk) { int ret; - mutex_lock(&prepare_lock); + clk_prepare_lock(); ret = __clk_prepare(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -640,9 +805,9 @@ void clk_disable(struct clk *clk) { unsigned long flags; - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); __clk_disable(clk); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); } EXPORT_SYMBOL_GPL(clk_disable); @@ -693,9 +858,9 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret; - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); ret = __clk_enable(clk); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); return ret; } @@ -740,9 +905,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate) { unsigned long ret; - mutex_lock(&prepare_lock); + clk_prepare_lock(); ret = __clk_round_rate(clk, rate); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -837,13 +1002,13 @@ unsigned long clk_get_rate(struct clk *clk) { unsigned long rate; - mutex_lock(&prepare_lock); + clk_prepare_lock(); if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) __clk_recalc_rates(clk, 0); rate = __clk_get_rate(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return rate; } @@ -876,16 +1041,16 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) else new_rate = parent_rate; - /* abort the rate change if a driver returns NOTIFY_BAD */ + /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ if (clk->notifier_count) ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); - if (ret == NOTIFY_BAD) + if (ret & NOTIFY_STOP_MASK) goto out; hlist_for_each_entry(child, &clk->children, child_node) { ret = __clk_speculate_rates(child, new_rate); - if (ret == NOTIFY_BAD) + if (ret & NOTIFY_STOP_MASK) break; } @@ -974,11 +1139,11 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even int ret = NOTIFY_DONE; if (clk->rate == clk->new_rate) - return 0; + return NULL; if (clk->notifier_count) { ret = __clk_notify(clk, event, clk->rate, clk->new_rate); - if (ret == NOTIFY_BAD) + if (ret & NOTIFY_STOP_MASK) fail_clk = clk; } @@ -1048,7 +1213,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) int ret = 0; /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* bail early if nothing to do */ if (rate == clk->rate) @@ -1080,7 +1245,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) clk_change_rate(top); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1096,9 +1261,9 @@ struct clk *clk_get_parent(struct clk *clk) { struct clk *parent; - mutex_lock(&prepare_lock); + clk_prepare_lock(); parent = __clk_get_parent(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return parent; } @@ -1162,16 +1327,8 @@ out: return ret; } -void __clk_reparent(struct clk *clk, struct clk *new_parent) +static void clk_reparent(struct clk *clk, struct clk *new_parent) { -#ifdef CONFIG_COMMON_CLK_DEBUG - struct dentry *d; - struct dentry *new_parent_d; -#endif - - if (!clk || !new_parent) - return; - hlist_del(&clk->child_node); if (new_parent) @@ -1179,39 +1336,20 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent) else hlist_add_head(&clk->child_node, &clk_orphan_list); -#ifdef CONFIG_COMMON_CLK_DEBUG - if (!inited) - goto out; - - if (new_parent) - new_parent_d = new_parent->dentry; - else - new_parent_d = orphandir; - - d = debugfs_rename(clk->dentry->d_parent, clk->dentry, - new_parent_d, clk->name); - if (d) - clk->dentry = d; - else - pr_debug("%s: failed to rename debugfs entry for %s\n", - __func__, clk->name); -out: -#endif - clk->parent = new_parent; +} +void __clk_reparent(struct clk *clk, struct clk *new_parent) +{ + clk_reparent(clk, new_parent); + clk_debug_reparent(clk, new_parent); __clk_recalc_rates(clk, POST_RATE_CHANGE); } -static int __clk_set_parent(struct clk *clk, struct clk *parent) +static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) { - struct clk *old_parent; - unsigned long flags; - int ret = -EINVAL; u8 i; - old_parent = clk->parent; - if (!clk->parents) clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), GFP_KERNEL); @@ -1231,36 +1369,79 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent) } } - if (i == clk->num_parents) { - pr_debug("%s: clock %s is not a possible parent of clock %s\n", - __func__, parent->name, clk->name); - goto out; - } + return i; +} - /* migrate prepare and enable */ +static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) +{ + unsigned long flags; + int ret = 0; + struct clk *old_parent = clk->parent; + bool migrated_enable = false; + + /* migrate prepare */ if (clk->prepare_count) __clk_prepare(parent); - /* FIXME replace with clk_is_enabled(clk) someday */ - spin_lock_irqsave(&enable_lock, flags); - if (clk->enable_count) + flags = clk_enable_lock(); + + /* migrate enable */ + if (clk->enable_count) { __clk_enable(parent); - spin_unlock_irqrestore(&enable_lock, flags); + migrated_enable = true; + } + + /* update the clk tree topology */ + clk_reparent(clk, parent); + + clk_enable_unlock(flags); /* change clock input source */ - ret = clk->ops->set_parent(clk->hw, i); + if (parent && clk->ops->set_parent) + ret = clk->ops->set_parent(clk->hw, p_index); - /* clean up old prepare and enable */ - spin_lock_irqsave(&enable_lock, flags); - if (clk->enable_count) + if (ret) { + /* + * The error handling is tricky due to that we need to release + * the spinlock while issuing the .set_parent callback. This + * means the new parent might have been enabled/disabled in + * between, which must be considered when doing rollback. + */ + flags = clk_enable_lock(); + + clk_reparent(clk, old_parent); + + if (migrated_enable && clk->enable_count) { + __clk_disable(parent); + } else if (migrated_enable && (clk->enable_count == 0)) { + __clk_disable(old_parent); + } else if (!migrated_enable && clk->enable_count) { + __clk_disable(parent); + __clk_enable(old_parent); + } + + clk_enable_unlock(flags); + + if (clk->prepare_count) + __clk_unprepare(parent); + + return ret; + } + + /* clean up enable for old parent if migration was done */ + if (migrated_enable) { + flags = clk_enable_lock(); __clk_disable(old_parent); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); + } + /* clean up prepare for old parent if migration was done */ if (clk->prepare_count) __clk_unprepare(old_parent); -out: - return ret; + /* update debugfs with new clk tree topology */ + clk_debug_reparent(clk, parent); + return 0; } /** @@ -1278,44 +1459,59 @@ out: int clk_set_parent(struct clk *clk, struct clk *parent) { int ret = 0; + u8 p_index = 0; + unsigned long p_rate = 0; if (!clk || !clk->ops) return -EINVAL; - if (!clk->ops->set_parent) + /* verify ops for for multi-parent clks */ + if ((clk->num_parents > 1) && (!clk->ops->set_parent)) return -ENOSYS; /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); + clk_prepare_lock(); if (clk->parent == parent) goto out; + /* check that we are allowed to re-parent if the clock is in use */ + if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + ret = -EBUSY; + goto out; + } + + /* try finding the new parent index */ + if (parent) { + p_index = clk_fetch_parent_index(clk, parent); + p_rate = parent->rate; + if (p_index == clk->num_parents) { + pr_debug("%s: clk %s can not be parent of clk %s\n", + __func__, parent->name, clk->name); + ret = -EINVAL; + goto out; + } + } + /* propagate PRE_RATE_CHANGE notifications */ if (clk->notifier_count) - ret = __clk_speculate_rates(clk, parent->rate); + ret = __clk_speculate_rates(clk, p_rate); /* abort if a driver objects */ - if (ret == NOTIFY_STOP) + if (ret & NOTIFY_STOP_MASK) goto out; - /* only re-parent if the clock is not in use */ - if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) - ret = -EBUSY; - else - ret = __clk_set_parent(clk, parent); + /* do the re-parent */ + ret = __clk_set_parent(clk, parent, p_index); - /* propagate ABORT_RATE_CHANGE if .set_parent failed */ - if (ret) { + /* propagate rate recalculation accordingly */ + if (ret) __clk_recalc_rates(clk, ABORT_RATE_CHANGE); - goto out; - } - - /* propagate rate recalculation downstream */ - __clk_reparent(clk, parent); + else + __clk_recalc_rates(clk, POST_RATE_CHANGE); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1338,7 +1534,7 @@ int __clk_init(struct device *dev, struct clk *clk) if (!clk) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* check to see if a clock with this name is already registered */ if (__clk_lookup(clk->name)) { @@ -1462,7 +1658,7 @@ int __clk_init(struct device *dev, struct clk *clk) clk_debug_register(clk); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1696,7 +1892,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb) if (!clk || !nb) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* search the list of notifiers for this clk */ list_for_each_entry(cn, &clk_notifier_list, node) @@ -1720,7 +1916,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb) clk->notifier_count++; out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1745,7 +1941,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) if (!clk || !nb) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); list_for_each_entry(cn, &clk_notifier_list, node) if (cn->clk == clk) @@ -1766,7 +1962,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) ret = -ENOENT; } - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index ade435820c7..d1f1a19d435 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -221,7 +221,7 @@ void __init mmp2_clk_init(void) clk = mmp_clk_register_apbc("gpio", "vctcxo", apbc_base + APBC_GPIO, 10, 0, &clk_lock); - clk_register_clkdev(clk, NULL, "pxa-gpio"); + clk_register_clkdev(clk, NULL, "mmp2-gpio"); clk = mmp_clk_register_apbc("kpc", "clk32", apbc_base + APBC_KPC, 10, 0, &clk_lock); diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c index e8d036c12cb..28b3b51c794 100644 --- a/drivers/clk/mmp/clk-pxa168.c +++ b/drivers/clk/mmp/clk-pxa168.c @@ -172,7 +172,7 @@ void __init pxa168_clk_init(void) clk = mmp_clk_register_apbc("gpio", "vctcxo", apbc_base + APBC_GPIO, 10, 0, &clk_lock); - clk_register_clkdev(clk, NULL, "pxa-gpio"); + clk_register_clkdev(clk, NULL, "mmp-gpio"); clk = mmp_clk_register_apbc("kpc", "clk32", apbc_base + APBC_KPC, 10, 0, &clk_lock); diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c index 7048c31d6e7..6ec05698ed3 100644 --- a/drivers/clk/mmp/clk-pxa910.c +++ b/drivers/clk/mmp/clk-pxa910.c @@ -177,7 +177,7 @@ void __init pxa910_clk_init(void) clk = mmp_clk_register_apbc("gpio", "vctcxo", apbc_base + APBC_GPIO, 10, 0, &clk_lock); - clk_register_clkdev(clk, NULL, "pxa-gpio"); + clk_register_clkdev(clk, NULL, "mmp-gpio"); clk = mmp_clk_register_apbc("kpc", "clk32", apbc_base + APBC_KPC, 10, 0, &clk_lock); diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c index 69056a7479e..0a53edbae8b 100644 --- a/drivers/clk/mvebu/clk-core.c +++ b/drivers/clk/mvebu/clk-core.c @@ -156,8 +156,8 @@ static u32 __init armada_370_get_cpu_freq(void __iomem *sar) 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); + if (cpu_freq_select >= ARRAY_SIZE(armada_370_cpu_frequencies)) { + pr_err("CPU freq select unsupported %d\n", cpu_freq_select); cpu_freq = 0; } else cpu_freq = armada_370_cpu_frequencies[cpu_freq_select]; @@ -278,8 +278,8 @@ static u32 __init armada_xp_get_cpu_freq(void __iomem *sar) 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); + if (cpu_freq_select >= ARRAY_SIZE(armada_xp_cpu_frequencies)) { + pr_err("CPU freq select unsupported: %d\n", cpu_freq_select); cpu_freq = 0; } else cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select]; diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index 9dd2551a0a4..b0fbc071549 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -16,7 +16,6 @@ #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 @@ -173,17 +172,5 @@ clks_out: 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); -} +CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock", + of_cpu_clk_setup); diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h deleted file mode 100644 index 08e2affba4e..00000000000 --- a/drivers/clk/mvebu/clk-cpu.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Marvell MVEBU CPU clock handling. - * - * Copyright (C) 2012 Marvell - * - * Gregory CLEMENT <gregory.clement@free-electrons.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#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.c b/drivers/clk/mvebu/clk.c index 855681b8a9d..29f10fb3006 100644 --- a/drivers/clk/mvebu/clk.c +++ b/drivers/clk/mvebu/clk.c @@ -10,18 +10,14 @@ * 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(); + of_clk_init(NULL); } diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index b5c06f9766f..f6a74872f14 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -15,12 +15,15 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/of.h> -#include <mach/common.h> -#include <mach/mx23.h> +#include <linux/of_address.h> #include "clk.h" -#define DIGCTRL MX23_IO_ADDRESS(MX23_DIGCTL_BASE_ADDR) -#define CLKCTRL MX23_IO_ADDRESS(MX23_CLKCTRL_BASE_ADDR) +static void __iomem *clkctrl; +static void __iomem *digctrl; + +#define CLKCTRL clkctrl +#define DIGCTRL digctrl + #define PLLCTRL0 (CLKCTRL + 0x0000) #define CPU (CLKCTRL + 0x0020) #define HBUS (CLKCTRL + 0x0030) @@ -48,10 +51,10 @@ static void __init clk_misc_init(void) u32 val; /* Gate off cpu clock in WFI for power saving */ - __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU); + writel_relaxed(1 << BP_CPU_INTERRUPT_WAIT, CPU + SET); /* Clear BYPASS for SAIF */ - __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ); + writel_relaxed(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ + CLR); /* SAIF has to use frac div for functional operation */ val = readl_relaxed(SAIF); @@ -62,14 +65,14 @@ static void __init clk_misc_init(void) * Source ssp clock from ref_io than ref_xtal, * as ref_xtal only provides 24 MHz as maximum. */ - __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ); + writel_relaxed(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ + CLR); /* * 480 MHz seems too high to be ssp clock source directly, * so set frac to get a 288 MHz ref_io. */ - __mxs_clrl(0x3f << BP_FRAC_IOFRAC, FRAC); - __mxs_setl(30 << BP_FRAC_IOFRAC, FRAC); + writel_relaxed(0x3f << BP_FRAC_IOFRAC, FRAC + CLR); + writel_relaxed(30 << BP_FRAC_IOFRAC, FRAC + SET); } static const char *sel_pll[] __initconst = { "pll", "ref_xtal", }; @@ -101,6 +104,14 @@ int __init mx23_clocks_init(void) struct device_node *np; u32 i; + np = of_find_compatible_node(NULL, NULL, "fsl,imx23-digctl"); + digctrl = of_iomap(np, 0); + WARN_ON(!digctrl); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl"); + clkctrl = of_iomap(np, 0); + WARN_ON(!clkctrl); + clk_misc_init(); clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000); @@ -153,19 +164,12 @@ int __init mx23_clocks_init(void) return PTR_ERR(clks[i]); } - np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl"); - if (np) { - clk_data.clks = clks; - clk_data.clk_num = ARRAY_SIZE(clks); - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - } - - clk_register_clkdev(clks[clk32k], NULL, "timrot"); + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clks[clks_init_on[i]]); - mxs_timer_init(); - return 0; } diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 76ce6c6d111..4faf0afc44c 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -10,16 +10,18 @@ */ #include <linux/clk.h> +#include <linux/clk/mxs.h> #include <linux/clkdev.h> #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> #include <linux/of.h> -#include <mach/common.h> -#include <mach/mx28.h> +#include <linux/of_address.h> #include "clk.h" -#define CLKCTRL MX28_IO_ADDRESS(MX28_CLKCTRL_BASE_ADDR) +static void __iomem *clkctrl; +#define CLKCTRL clkctrl + #define PLL0CTRL0 (CLKCTRL + 0x0000) #define PLL1CTRL0 (CLKCTRL + 0x0020) #define PLL2CTRL0 (CLKCTRL + 0x0040) @@ -53,7 +55,8 @@ #define BP_FRAC0_IO1FRAC 16 #define BP_FRAC0_IO0FRAC 24 -#define DIGCTRL MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR) +static void __iomem *digctrl; +#define DIGCTRL digctrl #define BP_SAIF_CLKMUX 10 /* @@ -72,8 +75,8 @@ int mxs_saif_clkmux_select(unsigned int clkmux) if (clkmux > 0x3) return -EINVAL; - __mxs_clrl(0x3 << BP_SAIF_CLKMUX, DIGCTRL); - __mxs_setl(clkmux << BP_SAIF_CLKMUX, DIGCTRL); + writel_relaxed(0x3 << BP_SAIF_CLKMUX, DIGCTRL + CLR); + writel_relaxed(clkmux << BP_SAIF_CLKMUX, DIGCTRL + SET); return 0; } @@ -83,13 +86,13 @@ static void __init clk_misc_init(void) u32 val; /* Gate off cpu clock in WFI for power saving */ - __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU); + writel_relaxed(1 << BP_CPU_INTERRUPT_WAIT, CPU + SET); /* 0 is a bad default value for a divider */ - __mxs_setl(1 << BP_ENET_DIV_TIME, ENET); + writel_relaxed(1 << BP_ENET_DIV_TIME, ENET + SET); /* Clear BYPASS for SAIF */ - __mxs_clrl(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ); + writel_relaxed(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ + CLR); /* SAIF has to use frac div for functional operation */ val = readl_relaxed(SAIF0); @@ -109,7 +112,7 @@ static void __init clk_misc_init(void) * Source ssp clock from ref_io than ref_xtal, * as ref_xtal only provides 24 MHz as maximum. */ - __mxs_clrl(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ); + writel_relaxed(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ + CLR); /* * 480 MHz seems too high to be ssp clock source directly, @@ -156,6 +159,14 @@ int __init mx28_clocks_init(void) struct device_node *np; u32 i; + np = of_find_compatible_node(NULL, NULL, "fsl,imx28-digctl"); + digctrl = of_iomap(np, 0); + WARN_ON(!digctrl); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl"); + clkctrl = of_iomap(np, 0); + WARN_ON(!clkctrl); + clk_misc_init(); clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000); @@ -231,20 +242,14 @@ int __init mx28_clocks_init(void) return PTR_ERR(clks[i]); } - np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl"); - if (np) { - clk_data.clks = clks; - clk_data.clk_num = ARRAY_SIZE(clks); - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - } + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - clk_register_clkdev(clks[xbus], NULL, "timrot"); clk_register_clkdev(clks[enet_out], NULL, "enet_out"); for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clks[clks_init_on[i]]); - mxs_timer_init(); - return 0; } diff --git a/drivers/clk/mxs/clk.c b/drivers/clk/mxs/clk.c index b24d56067c8..5301bce8957 100644 --- a/drivers/clk/mxs/clk.c +++ b/drivers/clk/mxs/clk.c @@ -13,6 +13,7 @@ #include <linux/io.h> #include <linux/jiffies.h> #include <linux/spinlock.h> +#include "clk.h" DEFINE_SPINLOCK(mxs_lock); diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile new file mode 100644 index 00000000000..b7c232e6742 --- /dev/null +++ b/drivers/clk/samsung/Makefile @@ -0,0 +1,8 @@ +# +# Samsung Clock specific Makefile +# + +obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o +obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o +obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o +obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c new file mode 100644 index 00000000000..3c1f88868f2 --- /dev/null +++ b/drivers/clk/samsung/clk-exynos4.c @@ -0,0 +1,1084 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham <thomas.ab@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for all Exynos4 SoCs. +*/ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk.h" +#include "clk-pll.h" + +/* Exynos4 clock controller register offsets */ +#define SRC_LEFTBUS 0x4200 +#define DIV_LEFTBUS 0x4500 +#define GATE_IP_LEFTBUS 0x4800 +#define E4X12_GATE_IP_IMAGE 0x4930 +#define SRC_RIGHTBUS 0x8200 +#define DIV_RIGHTBUS 0x8500 +#define GATE_IP_RIGHTBUS 0x8800 +#define E4X12_GATE_IP_PERIR 0x8960 +#define EPLL_LOCK 0xc010 +#define VPLL_LOCK 0xc020 +#define EPLL_CON0 0xc110 +#define EPLL_CON1 0xc114 +#define EPLL_CON2 0xc118 +#define VPLL_CON0 0xc120 +#define VPLL_CON1 0xc124 +#define VPLL_CON2 0xc128 +#define SRC_TOP0 0xc210 +#define SRC_TOP1 0xc214 +#define SRC_CAM 0xc220 +#define SRC_TV 0xc224 +#define SRC_MFC 0xcc28 +#define SRC_G3D 0xc22c +#define E4210_SRC_IMAGE 0xc230 +#define SRC_LCD0 0xc234 +#define E4210_SRC_LCD1 0xc238 +#define E4X12_SRC_ISP 0xc238 +#define SRC_MAUDIO 0xc23c +#define SRC_FSYS 0xc240 +#define SRC_PERIL0 0xc250 +#define SRC_PERIL1 0xc254 +#define E4X12_SRC_CAM1 0xc258 +#define SRC_MASK_TOP 0xc310 +#define SRC_MASK_CAM 0xc320 +#define SRC_MASK_TV 0xc324 +#define SRC_MASK_LCD0 0xc334 +#define E4210_SRC_MASK_LCD1 0xc338 +#define E4X12_SRC_MASK_ISP 0xc338 +#define SRC_MASK_MAUDIO 0xc33c +#define SRC_MASK_FSYS 0xc340 +#define SRC_MASK_PERIL0 0xc350 +#define SRC_MASK_PERIL1 0xc354 +#define DIV_TOP 0xc510 +#define DIV_CAM 0xc520 +#define DIV_TV 0xc524 +#define DIV_MFC 0xc528 +#define DIV_G3D 0xc52c +#define DIV_IMAGE 0xc530 +#define DIV_LCD0 0xc534 +#define E4210_DIV_LCD1 0xc538 +#define E4X12_DIV_ISP 0xc538 +#define DIV_MAUDIO 0xc53c +#define DIV_FSYS0 0xc540 +#define DIV_FSYS1 0xc544 +#define DIV_FSYS2 0xc548 +#define DIV_FSYS3 0xc54c +#define DIV_PERIL0 0xc550 +#define DIV_PERIL1 0xc554 +#define DIV_PERIL2 0xc558 +#define DIV_PERIL3 0xc55c +#define DIV_PERIL4 0xc560 +#define DIV_PERIL5 0xc564 +#define E4X12_DIV_CAM1 0xc568 +#define GATE_SCLK_CAM 0xc820 +#define GATE_IP_CAM 0xc920 +#define GATE_IP_TV 0xc924 +#define GATE_IP_MFC 0xc928 +#define GATE_IP_G3D 0xc92c +#define E4210_GATE_IP_IMAGE 0xc930 +#define GATE_IP_LCD0 0xc934 +#define E4210_GATE_IP_LCD1 0xc938 +#define E4X12_GATE_IP_ISP 0xc938 +#define E4X12_GATE_IP_MAUDIO 0xc93c +#define GATE_IP_FSYS 0xc940 +#define GATE_IP_GPS 0xc94c +#define GATE_IP_PERIL 0xc950 +#define E4210_GATE_IP_PERIR 0xc960 +#define GATE_BLOCK 0xc970 +#define E4X12_MPLL_CON0 0x10108 +#define SRC_DMC 0x10200 +#define SRC_MASK_DMC 0x10300 +#define DIV_DMC0 0x10500 +#define DIV_DMC1 0x10504 +#define GATE_IP_DMC 0x10900 +#define APLL_CON0 0x14100 +#define E4210_MPLL_CON0 0x14108 +#define SRC_CPU 0x14200 +#define DIV_CPU0 0x14500 +#define DIV_CPU1 0x14504 +#define GATE_SCLK_CPU 0x14800 +#define GATE_IP_CPU 0x14900 +#define E4X12_DIV_ISP0 0x18300 +#define E4X12_DIV_ISP1 0x18304 +#define E4X12_GATE_ISP0 0x18800 +#define E4X12_GATE_ISP1 0x18804 + +/* the exynos4 soc type */ +enum exynos4_soc { + EXYNOS4210, + EXYNOS4X12, +}; + +/* + * Let each supported clock get a unique id. This id is used to lookup the clock + * for device tree based platforms. The clocks are categorized into three + * sections: core, sclk gate and bus interface gate clocks. + * + * When adding a new clock to this list, it is advised to choose a clock + * category and add it to the end of that category. That is because the the + * device tree source file is referring to these ids and any change in the + * sequence number of existing clocks will require corresponding change in the + * device tree files. This limitation would go away when pre-processor support + * for dtc would be available. + */ +enum exynos4_clks { + none, + + /* core clocks */ + xxti, xusbxti, fin_pll, fout_apll, fout_mpll, fout_epll, fout_vpll, + sclk_apll, sclk_mpll, sclk_epll, sclk_vpll, arm_clk, aclk200, aclk100, + aclk160, aclk133, mout_mpll_user_t, mout_mpll_user_c, mout_core, + mout_apll, /* 20 */ + + /* gate for special clocks (sclk) */ + sclk_fimc0 = 128, sclk_fimc1, sclk_fimc2, sclk_fimc3, sclk_cam0, + sclk_cam1, sclk_csis0, sclk_csis1, sclk_hdmi, sclk_mixer, sclk_dac, + sclk_pixel, sclk_fimd0, sclk_mdnie0, sclk_mdnie_pwm0, sclk_mipi0, + sclk_audio0, sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_mmc4, + sclk_sata, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_uart4, + sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, + sclk_slimbus, sclk_fimd1, sclk_mipi1, sclk_pcm1, sclk_pcm2, sclk_i2s1, + sclk_i2s2, sclk_mipihsi, sclk_mfc, sclk_pcm0, sclk_g3d, sclk_pwm_isp, + sclk_spi0_isp, sclk_spi1_isp, sclk_uart_isp, + + /* gate clocks */ + fimc0 = 256, fimc1, fimc2, fimc3, csis0, csis1, jpeg, smmu_fimc0, + smmu_fimc1, smmu_fimc2, smmu_fimc3, smmu_jpeg, vp, mixer, tvenc, hdmi, + smmu_tv, mfc, smmu_mfcl, smmu_mfcr, g3d, g2d, rotator, mdma, smmu_g2d, + smmu_rotator, smmu_mdma, fimd0, mie0, mdnie0, dsim0, smmu_fimd0, fimd1, + mie1, dsim1, smmu_fimd1, pdma0, pdma1, pcie_phy, sata_phy, tsi, sdmmc0, + sdmmc1, sdmmc2, sdmmc3, sdmmc4, sata, sromc, usb_host, usb_device, pcie, + onenand, nfcon, smmu_pcie, gps, smmu_gps, uart0, uart1, uart2, uart3, + uart4, i2c0, i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, tsadc, + spi0, spi1, spi2, i2s1, i2s2, pcm0, i2s0, pcm1, pcm2, pwm, slimbus, + spdif, ac97, modemif, chipid, sysreg, hdmi_cec, mct, wdt, rtc, keyif, + audss, mipi_hsi, mdma2, pixelasyncm0, pixelasyncm1, fimc_lite0, + fimc_lite1, ppmuispx, ppmuispmx, fimc_isp, fimc_drc, fimc_fd, mcuisp, + gicisp, smmu_isp, smmu_drc, smmu_fd, smmu_lite0, smmu_lite1, mcuctl_isp, + mpwm_isp, i2c0_isp, i2c1_isp, mtcadc_isp, pwm_isp, wdt_isp, uart_isp, + asyncaxim, smmu_ispcx, spi0_isp, spi1_isp, pwm_isp_sclk, spi0_isp_sclk, + spi1_isp_sclk, uart_isp_sclk, + + /* mux clocks */ + mout_fimc0 = 384, mout_fimc1, mout_fimc2, mout_fimc3, mout_cam0, + mout_cam1, mout_csis0, mout_csis1, mout_g3d0, mout_g3d1, mout_g3d, + aclk400_mcuisp, + + /* div clocks */ + div_isp0 = 450, div_isp1, div_mcuisp0, div_mcuisp1, div_aclk200, + div_aclk400_mcuisp, + + nr_clks, +}; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static __initdata unsigned long exynos4210_clk_save[] = { + E4210_SRC_IMAGE, + E4210_SRC_LCD1, + E4210_SRC_MASK_LCD1, + E4210_DIV_LCD1, + E4210_GATE_IP_IMAGE, + E4210_GATE_IP_LCD1, + E4210_GATE_IP_PERIR, + E4210_MPLL_CON0, +}; + +static __initdata unsigned long exynos4x12_clk_save[] = { + E4X12_GATE_IP_IMAGE, + E4X12_GATE_IP_PERIR, + E4X12_SRC_CAM1, + E4X12_DIV_ISP, + E4X12_DIV_CAM1, + E4X12_MPLL_CON0, +}; + +static __initdata unsigned long exynos4_clk_regs[] = { + SRC_LEFTBUS, + DIV_LEFTBUS, + GATE_IP_LEFTBUS, + SRC_RIGHTBUS, + DIV_RIGHTBUS, + GATE_IP_RIGHTBUS, + EPLL_CON0, + EPLL_CON1, + EPLL_CON2, + VPLL_CON0, + VPLL_CON1, + VPLL_CON2, + SRC_TOP0, + SRC_TOP1, + SRC_CAM, + SRC_TV, + SRC_MFC, + SRC_G3D, + SRC_LCD0, + SRC_MAUDIO, + SRC_FSYS, + SRC_PERIL0, + SRC_PERIL1, + SRC_MASK_TOP, + SRC_MASK_CAM, + SRC_MASK_TV, + SRC_MASK_LCD0, + SRC_MASK_MAUDIO, + SRC_MASK_FSYS, + SRC_MASK_PERIL0, + SRC_MASK_PERIL1, + DIV_TOP, + DIV_CAM, + DIV_TV, + DIV_MFC, + DIV_G3D, + DIV_IMAGE, + DIV_LCD0, + DIV_MAUDIO, + DIV_FSYS0, + DIV_FSYS1, + DIV_FSYS2, + DIV_FSYS3, + DIV_PERIL0, + DIV_PERIL1, + DIV_PERIL2, + DIV_PERIL3, + DIV_PERIL4, + DIV_PERIL5, + GATE_SCLK_CAM, + GATE_IP_CAM, + GATE_IP_TV, + GATE_IP_MFC, + GATE_IP_G3D, + GATE_IP_LCD0, + GATE_IP_FSYS, + GATE_IP_GPS, + GATE_IP_PERIL, + GATE_BLOCK, + SRC_MASK_DMC, + SRC_DMC, + DIV_DMC0, + DIV_DMC1, + GATE_IP_DMC, + APLL_CON0, + SRC_CPU, + DIV_CPU0, + DIV_CPU1, + GATE_SCLK_CPU, + GATE_IP_CPU, +}; + +/* list of all parent clock list */ +PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; +PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", }; +PNAME(mout_epll_p) = { "fin_pll", "fout_epll", }; +PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi24m", }; +PNAME(mout_vpll_p) = { "fin_pll", "fout_vpll", }; +PNAME(sclk_evpll_p) = { "sclk_epll", "sclk_vpll", }; +PNAME(mout_mfc_p) = { "mout_mfc0", "mout_mfc1", }; +PNAME(mout_g3d_p) = { "mout_g3d0", "mout_g3d1", }; +PNAME(mout_g2d_p) = { "mout_g2d0", "mout_g2d1", }; +PNAME(mout_hdmi_p) = { "sclk_pixel", "sclk_hdmiphy", }; +PNAME(mout_jpeg_p) = { "mout_jpeg0", "mout_jpeg1", }; +PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2", + "spdif_extclk", }; +PNAME(mout_onenand_p) = {"aclk133", "aclk160", }; +PNAME(mout_onenand1_p) = {"mout_onenand", "sclk_vpll", }; + +/* Exynos 4210-specific parent groups */ +PNAME(sclk_vpll_p4210) = { "mout_vpllsrc", "fout_vpll", }; +PNAME(mout_core_p4210) = { "mout_apll", "sclk_mpll", }; +PNAME(sclk_ampll_p4210) = { "sclk_mpll", "sclk_apll", }; +PNAME(group1_p4210) = { "xxti", "xusbxti", "sclk_hdmi24m", + "sclk_usbphy0", "none", "sclk_hdmiphy", + "sclk_mpll", "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio0_p4210) = { "cdclk0", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll", + "sclk_epll", "sclk_vpll" }; +PNAME(mout_audio1_p4210) = { "cdclk1", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll", + "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio2_p4210) = { "cdclk2", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", "sclk_mpll", + "sclk_epll", "sclk_vpll", }; +PNAME(mout_mixer_p4210) = { "sclk_dac", "sclk_hdmi", }; +PNAME(mout_dac_p4210) = { "sclk_vpll", "sclk_hdmiphy", }; + +/* Exynos 4x12-specific parent groups */ +PNAME(mout_mpll_user_p4x12) = { "fin_pll", "sclk_mpll", }; +PNAME(mout_core_p4x12) = { "mout_apll", "mout_mpll_user_c", }; +PNAME(sclk_ampll_p4x12) = { "mout_mpll_user_t", "sclk_apll", }; +PNAME(group1_p4x12) = { "xxti", "xusbxti", "sclk_hdmi24m", "sclk_usbphy0", + "none", "sclk_hdmiphy", "mout_mpll_user_t", + "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio0_p4x12) = { "cdclk0", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", + "mout_mpll_user_t", "sclk_epll", "sclk_vpll" }; +PNAME(mout_audio1_p4x12) = { "cdclk1", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", + "mout_mpll_user_t", "sclk_epll", "sclk_vpll", }; +PNAME(mout_audio2_p4x12) = { "cdclk2", "none", "sclk_hdmi24m", + "sclk_usbphy0", "xxti", "xusbxti", + "mout_mpll_user_t", "sclk_epll", "sclk_vpll", }; +PNAME(aclk_p4412) = { "mout_mpll_user_t", "sclk_apll", }; +PNAME(mout_user_aclk400_mcuisp_p4x12) = {"fin_pll", "div_aclk400_mcuisp", }; +PNAME(mout_user_aclk200_p4x12) = {"fin_pll", "div_aclk200", }; +PNAME(mout_user_aclk266_gps_p4x12) = {"fin_pll", "div_aclk266_gps", }; + +/* fixed rate clocks generated outside the soc */ +struct samsung_fixed_rate_clock exynos4_fixed_rate_ext_clks[] __initdata = { + FRATE(xxti, "xxti", NULL, CLK_IS_ROOT, 0), + FRATE(xusbxti, "xusbxti", NULL, CLK_IS_ROOT, 0), +}; + +/* fixed rate clocks generated inside the soc */ +struct samsung_fixed_rate_clock exynos4_fixed_rate_clks[] __initdata = { + FRATE(none, "sclk_hdmi24m", NULL, CLK_IS_ROOT, 24000000), + FRATE(none, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), + FRATE(none, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000), +}; + +struct samsung_fixed_rate_clock exynos4210_fixed_rate_clks[] __initdata = { + FRATE(none, "sclk_usbphy1", NULL, CLK_IS_ROOT, 48000000), +}; + +/* list of mux clocks supported in all exynos4 soc's */ +struct samsung_mux_clock exynos4_mux_clks[] __initdata = { + MUX_F(mout_apll, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, + CLK_SET_RATE_PARENT, 0), + MUX(none, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), + MUX(none, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), + MUX(none, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), + MUX_F(mout_g3d1, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1, + CLK_SET_RATE_PARENT, 0), + MUX_F(mout_g3d, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1, + CLK_SET_RATE_PARENT, 0), + MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIL1, 8, 2), + MUX(none, "mout_onenand1", mout_onenand1_p, SRC_TOP0, 0, 1), + MUX_A(sclk_epll, "sclk_epll", mout_epll_p, SRC_TOP0, 4, 1, "sclk_epll"), + MUX(none, "mout_onenand", mout_onenand_p, SRC_TOP0, 28, 1), +}; + +/* list of mux clocks supported in exynos4210 soc */ +struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { + MUX(none, "mout_aclk200", sclk_ampll_p4210, SRC_TOP0, 12, 1), + MUX(none, "mout_aclk100", sclk_ampll_p4210, SRC_TOP0, 16, 1), + MUX(none, "mout_aclk160", sclk_ampll_p4210, SRC_TOP0, 20, 1), + MUX(none, "mout_aclk133", sclk_ampll_p4210, SRC_TOP0, 24, 1), + MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP1, 0, 1), + MUX(none, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1), + MUX(none, "mout_dac", mout_dac_p4210, SRC_TV, 8, 1), + MUX(none, "mout_g2d0", sclk_ampll_p4210, E4210_SRC_IMAGE, 0, 1), + MUX(none, "mout_g2d1", sclk_evpll_p, E4210_SRC_IMAGE, 4, 1), + MUX(none, "mout_g2d", mout_g2d_p, E4210_SRC_IMAGE, 8, 1), + MUX(none, "mout_fimd1", group1_p4210, E4210_SRC_LCD1, 0, 4), + MUX(none, "mout_mipi1", group1_p4210, E4210_SRC_LCD1, 12, 4), + MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1, "sclk_mpll"), + MUX_A(mout_core, "mout_core", mout_core_p4210, + SRC_CPU, 16, 1, "mout_core"), + MUX_A(sclk_vpll, "sclk_vpll", sclk_vpll_p4210, + SRC_TOP0, 8, 1, "sclk_vpll"), + MUX(mout_fimc0, "mout_fimc0", group1_p4210, SRC_CAM, 0, 4), + MUX(mout_fimc1, "mout_fimc1", group1_p4210, SRC_CAM, 4, 4), + MUX(mout_fimc2, "mout_fimc2", group1_p4210, SRC_CAM, 8, 4), + MUX(mout_fimc3, "mout_fimc3", group1_p4210, SRC_CAM, 12, 4), + MUX(mout_cam0, "mout_cam0", group1_p4210, SRC_CAM, 16, 4), + MUX(mout_cam1, "mout_cam1", group1_p4210, SRC_CAM, 20, 4), + MUX(mout_csis0, "mout_csis0", group1_p4210, SRC_CAM, 24, 4), + MUX(mout_csis1, "mout_csis1", group1_p4210, SRC_CAM, 28, 4), + MUX(none, "mout_mfc0", sclk_ampll_p4210, SRC_MFC, 0, 1), + MUX_F(mout_g3d0, "mout_g3d0", sclk_ampll_p4210, SRC_G3D, 0, 1, + CLK_SET_RATE_PARENT, 0), + MUX(none, "mout_fimd0", group1_p4210, SRC_LCD0, 0, 4), + MUX(none, "mout_mipi0", group1_p4210, SRC_LCD0, 12, 4), + MUX(none, "mout_audio0", mout_audio0_p4210, SRC_MAUDIO, 0, 4), + MUX(none, "mout_mmc0", group1_p4210, SRC_FSYS, 0, 4), + MUX(none, "mout_mmc1", group1_p4210, SRC_FSYS, 4, 4), + MUX(none, "mout_mmc2", group1_p4210, SRC_FSYS, 8, 4), + MUX(none, "mout_mmc3", group1_p4210, SRC_FSYS, 12, 4), + MUX(none, "mout_mmc4", group1_p4210, SRC_FSYS, 16, 4), + MUX(none, "mout_sata", sclk_ampll_p4210, SRC_FSYS, 24, 1), + MUX(none, "mout_uart0", group1_p4210, SRC_PERIL0, 0, 4), + MUX(none, "mout_uart1", group1_p4210, SRC_PERIL0, 4, 4), + MUX(none, "mout_uart2", group1_p4210, SRC_PERIL0, 8, 4), + MUX(none, "mout_uart3", group1_p4210, SRC_PERIL0, 12, 4), + MUX(none, "mout_uart4", group1_p4210, SRC_PERIL0, 16, 4), + MUX(none, "mout_audio1", mout_audio1_p4210, SRC_PERIL1, 0, 4), + MUX(none, "mout_audio2", mout_audio2_p4210, SRC_PERIL1, 4, 4), + MUX(none, "mout_spi0", group1_p4210, SRC_PERIL1, 16, 4), + MUX(none, "mout_spi1", group1_p4210, SRC_PERIL1, 20, 4), + MUX(none, "mout_spi2", group1_p4210, SRC_PERIL1, 24, 4), +}; + +/* list of mux clocks supported in exynos4x12 soc */ +struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { + MUX(mout_mpll_user_c, "mout_mpll_user_c", mout_mpll_user_p4x12, + SRC_CPU, 24, 1), + MUX(none, "mout_aclk266_gps", aclk_p4412, SRC_TOP1, 4, 1), + MUX(none, "mout_aclk400_mcuisp", aclk_p4412, SRC_TOP1, 8, 1), + MUX(mout_mpll_user_t, "mout_mpll_user_t", mout_mpll_user_p4x12, + SRC_TOP1, 12, 1), + MUX(none, "mout_user_aclk266_gps", mout_user_aclk266_gps_p4x12, + SRC_TOP1, 16, 1), + MUX(aclk200, "aclk200", mout_user_aclk200_p4x12, SRC_TOP1, 20, 1), + MUX(aclk400_mcuisp, "aclk400_mcuisp", mout_user_aclk400_mcuisp_p4x12, + SRC_TOP1, 24, 1), + MUX(none, "mout_aclk200", aclk_p4412, SRC_TOP0, 12, 1), + MUX(none, "mout_aclk100", aclk_p4412, SRC_TOP0, 16, 1), + MUX(none, "mout_aclk160", aclk_p4412, SRC_TOP0, 20, 1), + MUX(none, "mout_aclk133", aclk_p4412, SRC_TOP0, 24, 1), + MUX(none, "mout_mdnie0", group1_p4x12, SRC_LCD0, 4, 4), + MUX(none, "mout_mdnie_pwm0", group1_p4x12, SRC_LCD0, 8, 4), + MUX(none, "mout_sata", sclk_ampll_p4x12, SRC_FSYS, 24, 1), + MUX(none, "mout_jpeg0", sclk_ampll_p4x12, E4X12_SRC_CAM1, 0, 1), + MUX(none, "mout_jpeg1", sclk_evpll_p, E4X12_SRC_CAM1, 4, 1), + MUX(none, "mout_jpeg", mout_jpeg_p, E4X12_SRC_CAM1, 8, 1), + MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, + SRC_DMC, 12, 1, "sclk_mpll"), + MUX_A(sclk_vpll, "sclk_vpll", mout_vpll_p, + SRC_TOP0, 8, 1, "sclk_vpll"), + MUX(mout_core, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1), + MUX(mout_fimc0, "mout_fimc0", group1_p4x12, SRC_CAM, 0, 4), + MUX(mout_fimc1, "mout_fimc1", group1_p4x12, SRC_CAM, 4, 4), + MUX(mout_fimc2, "mout_fimc2", group1_p4x12, SRC_CAM, 8, 4), + MUX(mout_fimc3, "mout_fimc3", group1_p4x12, SRC_CAM, 12, 4), + MUX(mout_cam0, "mout_cam0", group1_p4x12, SRC_CAM, 16, 4), + MUX(mout_cam1, "mout_cam1", group1_p4x12, SRC_CAM, 20, 4), + MUX(mout_csis0, "mout_csis0", group1_p4x12, SRC_CAM, 24, 4), + MUX(mout_csis1, "mout_csis1", group1_p4x12, SRC_CAM, 28, 4), + MUX(none, "mout_mfc0", sclk_ampll_p4x12, SRC_MFC, 0, 1), + MUX_F(mout_g3d0, "mout_g3d0", sclk_ampll_p4x12, SRC_G3D, 0, 1, + CLK_SET_RATE_PARENT, 0), + MUX(none, "mout_fimd0", group1_p4x12, SRC_LCD0, 0, 4), + MUX(none, "mout_mipi0", group1_p4x12, SRC_LCD0, 12, 4), + MUX(none, "mout_audio0", mout_audio0_p4x12, SRC_MAUDIO, 0, 4), + MUX(none, "mout_mmc0", group1_p4x12, SRC_FSYS, 0, 4), + MUX(none, "mout_mmc1", group1_p4x12, SRC_FSYS, 4, 4), + MUX(none, "mout_mmc2", group1_p4x12, SRC_FSYS, 8, 4), + MUX(none, "mout_mmc3", group1_p4x12, SRC_FSYS, 12, 4), + MUX(none, "mout_mmc4", group1_p4x12, SRC_FSYS, 16, 4), + MUX(none, "mout_mipihsi", aclk_p4412, SRC_FSYS, 24, 1), + MUX(none, "mout_uart0", group1_p4x12, SRC_PERIL0, 0, 4), + MUX(none, "mout_uart1", group1_p4x12, SRC_PERIL0, 4, 4), + MUX(none, "mout_uart2", group1_p4x12, SRC_PERIL0, 8, 4), + MUX(none, "mout_uart3", group1_p4x12, SRC_PERIL0, 12, 4), + MUX(none, "mout_uart4", group1_p4x12, SRC_PERIL0, 16, 4), + MUX(none, "mout_audio1", mout_audio1_p4x12, SRC_PERIL1, 0, 4), + MUX(none, "mout_audio2", mout_audio2_p4x12, SRC_PERIL1, 4, 4), + MUX(none, "mout_spi0", group1_p4x12, SRC_PERIL1, 16, 4), + MUX(none, "mout_spi1", group1_p4x12, SRC_PERIL1, 20, 4), + MUX(none, "mout_spi2", group1_p4x12, SRC_PERIL1, 24, 4), + MUX(none, "mout_pwm_isp", group1_p4x12, E4X12_SRC_ISP, 0, 4), + MUX(none, "mout_spi0_isp", group1_p4x12, E4X12_SRC_ISP, 4, 4), + MUX(none, "mout_spi1_isp", group1_p4x12, E4X12_SRC_ISP, 8, 4), + MUX(none, "mout_uart_isp", group1_p4x12, E4X12_SRC_ISP, 12, 4), +}; + +/* list of divider clocks supported in all exynos4 soc's */ +struct samsung_div_clock exynos4_div_clks[] __initdata = { + DIV(none, "div_core", "mout_core", DIV_CPU0, 0, 3), + DIV(none, "div_core2", "div_core", DIV_CPU0, 28, 3), + DIV(none, "div_fimc0", "mout_fimc0", DIV_CAM, 0, 4), + DIV(none, "div_fimc1", "mout_fimc1", DIV_CAM, 4, 4), + DIV(none, "div_fimc2", "mout_fimc2", DIV_CAM, 8, 4), + DIV(none, "div_fimc3", "mout_fimc3", DIV_CAM, 12, 4), + DIV(none, "div_cam0", "mout_cam0", DIV_CAM, 16, 4), + DIV(none, "div_cam1", "mout_cam1", DIV_CAM, 20, 4), + DIV(none, "div_csis0", "mout_csis0", DIV_CAM, 24, 4), + DIV(none, "div_csis1", "mout_csis1", DIV_CAM, 28, 4), + DIV(sclk_mfc, "sclk_mfc", "mout_mfc", DIV_MFC, 0, 4), + DIV_F(none, "div_g3d", "mout_g3d", DIV_G3D, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV(none, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4), + DIV(none, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4), + DIV(none, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4), + DIV(sclk_pcm0, "sclk_pcm0", "sclk_audio0", DIV_MAUDIO, 4, 8), + DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), + DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), + DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4), + DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4), + DIV(sclk_pixel, "sclk_pixel", "sclk_vpll", DIV_TV, 0, 4), + DIV(aclk100, "aclk100", "mout_aclk100", DIV_TOP, 4, 4), + DIV(aclk160, "aclk160", "mout_aclk160", DIV_TOP, 8, 3), + DIV(aclk133, "aclk133", "mout_aclk133", DIV_TOP, 12, 3), + DIV(none, "div_onenand", "mout_onenand1", DIV_TOP, 16, 3), + DIV(sclk_slimbus, "sclk_slimbus", "sclk_epll", DIV_PERIL3, 4, 4), + DIV(sclk_pcm1, "sclk_pcm1", "sclk_audio1", DIV_PERIL4, 4, 8), + DIV(sclk_pcm2, "sclk_pcm2", "sclk_audio2", DIV_PERIL4, 20, 8), + DIV(sclk_i2s1, "sclk_i2s1", "sclk_audio1", DIV_PERIL5, 0, 6), + DIV(sclk_i2s2, "sclk_i2s2", "sclk_audio2", DIV_PERIL5, 8, 6), + DIV(none, "div_mmc4", "mout_mmc4", DIV_FSYS3, 0, 4), + DIV(none, "div_mmc_pre4", "div_mmc4", DIV_FSYS3, 8, 8), + DIV(none, "div_uart0", "mout_uart0", DIV_PERIL0, 0, 4), + DIV(none, "div_uart1", "mout_uart1", DIV_PERIL0, 4, 4), + DIV(none, "div_uart2", "mout_uart2", DIV_PERIL0, 8, 4), + DIV(none, "div_uart3", "mout_uart3", DIV_PERIL0, 12, 4), + DIV(none, "div_uart4", "mout_uart4", DIV_PERIL0, 16, 4), + DIV(none, "div_spi0", "mout_spi0", DIV_PERIL1, 0, 4), + DIV(none, "div_spi_pre0", "div_spi0", DIV_PERIL1, 8, 8), + DIV(none, "div_spi1", "mout_spi1", DIV_PERIL1, 16, 4), + DIV(none, "div_spi_pre1", "div_spi1", DIV_PERIL1, 24, 8), + DIV(none, "div_spi2", "mout_spi2", DIV_PERIL2, 0, 4), + DIV(none, "div_spi_pre2", "div_spi2", DIV_PERIL2, 8, 8), + DIV(none, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4), + DIV(none, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4), + DIV_A(arm_clk, "arm_clk", "div_core2", DIV_CPU0, 28, 3, "arm_clk"), + DIV_A(sclk_apll, "sclk_apll", "mout_apll", + DIV_CPU0, 24, 3, "sclk_apll"), + DIV_F(none, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre0", "div_mmc0", DIV_FSYS1, 8, 8, + CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre1", "div_mmc1", DIV_FSYS1, 24, 8, + CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre2", "div_mmc2", DIV_FSYS2, 8, 8, + CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre3", "div_mmc3", DIV_FSYS2, 24, 8, + CLK_SET_RATE_PARENT, 0), +}; + +/* list of divider clocks supported in exynos4210 soc */ +struct samsung_div_clock exynos4210_div_clks[] __initdata = { + DIV(aclk200, "aclk200", "mout_aclk200", DIV_TOP, 0, 3), + DIV(none, "div_g2d", "mout_g2d", DIV_IMAGE, 0, 4), + DIV(none, "div_fimd1", "mout_fimd1", E4210_DIV_LCD1, 0, 4), + DIV(none, "div_mipi1", "mout_mipi1", E4210_DIV_LCD1, 16, 4), + DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4), + DIV_F(none, "div_mipi_pre1", "div_mipi1", E4210_DIV_LCD1, 20, 4, + CLK_SET_RATE_PARENT, 0), +}; + +/* list of divider clocks supported in exynos4x12 soc */ +struct samsung_div_clock exynos4x12_div_clks[] __initdata = { + DIV(none, "div_mdnie0", "mout_mdnie0", DIV_LCD0, 4, 4), + DIV(none, "div_mdnie_pwm0", "mout_mdnie_pwm0", DIV_LCD0, 8, 4), + DIV(none, "div_mdnie_pwm_pre0", "div_mdnie_pwm0", DIV_LCD0, 12, 4), + DIV(none, "div_mipihsi", "mout_mipihsi", DIV_FSYS0, 20, 4), + DIV(none, "div_jpeg", "mout_jpeg", E4X12_DIV_CAM1, 0, 4), + DIV(div_aclk200, "div_aclk200", "mout_aclk200", DIV_TOP, 0, 3), + DIV(none, "div_aclk266_gps", "mout_aclk266_gps", DIV_TOP, 20, 3), + DIV(div_aclk400_mcuisp, "div_aclk400_mcuisp", "mout_aclk400_mcuisp", + DIV_TOP, 24, 3), + DIV(none, "div_pwm_isp", "mout_pwm_isp", E4X12_DIV_ISP, 0, 4), + DIV(none, "div_spi0_isp", "mout_spi0_isp", E4X12_DIV_ISP, 4, 4), + DIV(none, "div_spi0_isp_pre", "div_spi0_isp", E4X12_DIV_ISP, 8, 8), + DIV(none, "div_spi1_isp", "mout_spi1_isp", E4X12_DIV_ISP, 16, 4), + DIV(none, "div_spi1_isp_pre", "div_spi1_isp", E4X12_DIV_ISP, 20, 8), + DIV(none, "div_uart_isp", "mout_uart_isp", E4X12_DIV_ISP, 28, 4), + DIV(div_isp0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3), + DIV(div_isp1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3), + DIV(none, "div_mpwm", "div_isp1", E4X12_DIV_ISP1, 0, 3), + DIV(div_mcuisp0, "div_mcuisp0", "aclk400_mcuisp", E4X12_DIV_ISP1, 4, 3), + DIV(div_mcuisp1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, 8, 3), +}; + +/* list of gate clocks supported in all exynos4 soc's */ +struct samsung_gate_clock exynos4_gate_clks[] __initdata = { + /* + * After all Exynos4 based platforms are migrated to use device tree, + * the device name and clock alias names specified below for some + * of the clocks can be removed. + */ + GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi", SRC_MASK_TV, 0, 0, 0), + GATE(sclk_spdif, "sclk_spdif", "mout_spdif", SRC_MASK_PERIL1, 8, 0, 0), + GATE(jpeg, "jpeg", "aclk160", GATE_IP_CAM, 6, 0, 0), + GATE(mie0, "mie0", "aclk160", GATE_IP_LCD0, 1, 0, 0), + GATE(dsim0, "dsim0", "aclk160", GATE_IP_LCD0, 3, 0, 0), + GATE(fimd1, "fimd1", "aclk160", E4210_GATE_IP_LCD1, 0, 0, 0), + GATE(mie1, "mie1", "aclk160", E4210_GATE_IP_LCD1, 1, 0, 0), + GATE(dsim1, "dsim1", "aclk160", E4210_GATE_IP_LCD1, 3, 0, 0), + GATE(smmu_fimd1, "smmu_fimd1", "aclk160", E4210_GATE_IP_LCD1, 4, 0, 0), + GATE(tsi, "tsi", "aclk133", GATE_IP_FSYS, 4, 0, 0), + GATE(sromc, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0), + GATE(sclk_g3d, "sclk_g3d", "div_g3d", GATE_IP_G3D, 0, + CLK_SET_RATE_PARENT, 0), + GATE(usb_device, "usb_device", "aclk133", GATE_IP_FSYS, 13, 0, 0), + GATE(onenand, "onenand", "aclk133", GATE_IP_FSYS, 15, 0, 0), + GATE(nfcon, "nfcon", "aclk133", GATE_IP_FSYS, 16, 0, 0), + GATE(gps, "gps", "aclk133", GATE_IP_GPS, 0, 0, 0), + GATE(smmu_gps, "smmu_gps", "aclk133", GATE_IP_GPS, 1, 0, 0), + GATE(slimbus, "slimbus", "aclk100", GATE_IP_PERIL, 25, 0, 0), + GATE(sclk_cam0, "sclk_cam0", "div_cam0", GATE_SCLK_CAM, 4, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_cam1, "sclk_cam1", "div_cam1", GATE_SCLK_CAM, 5, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_mipi0, "sclk_mipi0", "div_mipi_pre0", + SRC_MASK_LCD0, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_audio0, "sclk_audio0", "div_audio0", SRC_MASK_MAUDIO, 0, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_audio1, "sclk_audio1", "div_audio1", SRC_MASK_PERIL1, 0, + CLK_SET_RATE_PARENT, 0), + GATE_D(vp, "s5p-mixer", "vp", "aclk160", GATE_IP_TV, 0, 0, 0), + GATE_D(mixer, "s5p-mixer", "mixer", "aclk160", GATE_IP_TV, 1, 0, 0), + GATE_D(hdmi, "exynos4-hdmi", "hdmi", "aclk160", GATE_IP_TV, 3, 0, 0), + GATE_A(pwm, "pwm", "aclk100", GATE_IP_PERIL, 24, 0, 0, "timers"), + GATE_A(sdmmc4, "sdmmc4", "aclk133", GATE_IP_FSYS, 9, 0, 0, "biu"), + GATE_A(usb_host, "usb_host", "aclk133", + GATE_IP_FSYS, 12, 0, 0, "usbhost"), + GATE_DA(sclk_fimc0, "exynos4-fimc.0", "sclk_fimc0", "div_fimc0", + SRC_MASK_CAM, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimc"), + GATE_DA(sclk_fimc1, "exynos4-fimc.1", "sclk_fimc1", "div_fimc1", + SRC_MASK_CAM, 4, CLK_SET_RATE_PARENT, 0, "sclk_fimc"), + GATE_DA(sclk_fimc2, "exynos4-fimc.2", "sclk_fimc2", "div_fimc2", + SRC_MASK_CAM, 8, CLK_SET_RATE_PARENT, 0, "sclk_fimc"), + GATE_DA(sclk_fimc3, "exynos4-fimc.3", "sclk_fimc3", "div_fimc3", + SRC_MASK_CAM, 12, CLK_SET_RATE_PARENT, 0, "sclk_fimc"), + GATE_DA(sclk_csis0, "s5p-mipi-csis.0", "sclk_csis0", "div_csis0", + SRC_MASK_CAM, 24, CLK_SET_RATE_PARENT, 0, "sclk_csis"), + GATE_DA(sclk_csis1, "s5p-mipi-csis.1", "sclk_csis1", "div_csis1", + SRC_MASK_CAM, 28, CLK_SET_RATE_PARENT, 0, "sclk_csis"), + GATE_DA(sclk_fimd0, "exynos4-fb.0", "sclk_fimd0", "div_fimd0", + SRC_MASK_LCD0, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"), + GATE_DA(sclk_mmc0, "exynos4-sdhci.0", "sclk_mmc0", "div_mmc_pre0", + SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0, + "mmc_busclk.2"), + GATE_DA(sclk_mmc1, "exynos4-sdhci.1", "sclk_mmc1", "div_mmc_pre1", + SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0, + "mmc_busclk.2"), + GATE_DA(sclk_mmc2, "exynos4-sdhci.2", "sclk_mmc2", "div_mmc_pre2", + SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0, + "mmc_busclk.2"), + GATE_DA(sclk_mmc3, "exynos4-sdhci.3", "sclk_mmc3", "div_mmc_pre3", + SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0, + "mmc_busclk.2"), + GATE_DA(sclk_mmc4, NULL, "sclk_mmc4", "div_mmc_pre4", + SRC_MASK_FSYS, 16, CLK_SET_RATE_PARENT, 0, "ciu"), + GATE_DA(sclk_uart0, "exynos4210-uart.0", "uclk0", "div_uart0", + SRC_MASK_PERIL0, 0, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), + GATE_DA(sclk_uart1, "exynos4210-uart.1", "uclk1", "div_uart1", + SRC_MASK_PERIL0, 4, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), + GATE_DA(sclk_uart2, "exynos4210-uart.2", "uclk2", "div_uart2", + SRC_MASK_PERIL0, 8, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), + GATE_DA(sclk_uart3, "exynos4210-uart.3", "uclk3", "div_uart3", + SRC_MASK_PERIL0, 12, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), + GATE_DA(sclk_uart4, "exynos4210-uart.4", "uclk4", "div_uart4", + SRC_MASK_PERIL0, 16, CLK_SET_RATE_PARENT, + 0, "clk_uart_baud0"), + GATE(sclk_audio2, "sclk_audio2", "div_audio2", SRC_MASK_PERIL1, 4, + CLK_SET_RATE_PARENT, 0), + GATE_DA(sclk_spi0, "exynos4210-spi.0", "sclk_spi0", "div_spi_pre0", + SRC_MASK_PERIL1, 16, CLK_SET_RATE_PARENT, + 0, "spi_busclk0"), + GATE_DA(sclk_spi1, "exynos4210-spi.1", "sclk_spi1", "div_spi_pre1", + SRC_MASK_PERIL1, 20, CLK_SET_RATE_PARENT, + 0, "spi_busclk0"), + GATE_DA(sclk_spi2, "exynos4210-spi.2", "sclk_spi2", "div_spi_pre2", + SRC_MASK_PERIL1, 24, CLK_SET_RATE_PARENT, + 0, "spi_busclk0"), + GATE_DA(fimc0, "exynos4-fimc.0", "fimc0", "aclk160", + GATE_IP_CAM, 0, 0, 0, "fimc"), + GATE_DA(fimc1, "exynos4-fimc.1", "fimc1", "aclk160", + GATE_IP_CAM, 1, 0, 0, "fimc"), + GATE_DA(fimc2, "exynos4-fimc.2", "fimc2", "aclk160", + GATE_IP_CAM, 2, 0, 0, "fimc"), + GATE_DA(fimc3, "exynos4-fimc.3", "fimc3", "aclk160", + GATE_IP_CAM, 3, 0, 0, "fimc"), + GATE_DA(csis0, "s5p-mipi-csis.0", "csis0", "aclk160", + GATE_IP_CAM, 4, 0, 0, "fimc"), + GATE_DA(csis1, "s5p-mipi-csis.1", "csis1", "aclk160", + GATE_IP_CAM, 5, 0, 0, "fimc"), + GATE_DA(smmu_fimc0, "exynos-sysmmu.5", "smmu_fimc0", "aclk160", + GATE_IP_CAM, 7, 0, 0, "sysmmu"), + GATE_DA(smmu_fimc1, "exynos-sysmmu.6", "smmu_fimc1", "aclk160", + GATE_IP_CAM, 8, 0, 0, "sysmmu"), + GATE_DA(smmu_fimc2, "exynos-sysmmu.7", "smmu_fimc2", "aclk160", + GATE_IP_CAM, 9, 0, 0, "sysmmu"), + GATE_DA(smmu_fimc3, "exynos-sysmmu.8", "smmu_fimc3", "aclk160", + GATE_IP_CAM, 10, 0, 0, "sysmmu"), + GATE_DA(smmu_jpeg, "exynos-sysmmu.3", "smmu_jpeg", "aclk160", + GATE_IP_CAM, 11, 0, 0, "sysmmu"), + GATE(pixelasyncm0, "pxl_async0", "aclk160", GATE_IP_CAM, 17, 0, 0), + GATE(pixelasyncm1, "pxl_async1", "aclk160", GATE_IP_CAM, 18, 0, 0), + GATE_DA(smmu_tv, "exynos-sysmmu.2", "smmu_tv", "aclk160", + GATE_IP_TV, 4, 0, 0, "sysmmu"), + GATE_DA(mfc, "s5p-mfc", "mfc", "aclk100", GATE_IP_MFC, 0, 0, 0, "mfc"), + GATE_DA(smmu_mfcl, "exynos-sysmmu.0", "smmu_mfcl", "aclk100", + GATE_IP_MFC, 1, 0, 0, "sysmmu"), + GATE_DA(smmu_mfcr, "exynos-sysmmu.1", "smmu_mfcr", "aclk100", + GATE_IP_MFC, 2, 0, 0, "sysmmu"), + GATE_DA(fimd0, "exynos4-fb.0", "fimd0", "aclk160", + GATE_IP_LCD0, 0, 0, 0, "fimd"), + GATE_DA(smmu_fimd0, "exynos-sysmmu.10", "smmu_fimd0", "aclk160", + GATE_IP_LCD0, 4, 0, 0, "sysmmu"), + GATE_DA(pdma0, "dma-pl330.0", "pdma0", "aclk133", + GATE_IP_FSYS, 0, 0, 0, "dma"), + GATE_DA(pdma1, "dma-pl330.1", "pdma1", "aclk133", + GATE_IP_FSYS, 1, 0, 0, "dma"), + GATE_DA(sdmmc0, "exynos4-sdhci.0", "sdmmc0", "aclk133", + GATE_IP_FSYS, 5, 0, 0, "hsmmc"), + GATE_DA(sdmmc1, "exynos4-sdhci.1", "sdmmc1", "aclk133", + GATE_IP_FSYS, 6, 0, 0, "hsmmc"), + GATE_DA(sdmmc2, "exynos4-sdhci.2", "sdmmc2", "aclk133", + GATE_IP_FSYS, 7, 0, 0, "hsmmc"), + GATE_DA(sdmmc3, "exynos4-sdhci.3", "sdmmc3", "aclk133", + GATE_IP_FSYS, 8, 0, 0, "hsmmc"), + GATE_DA(uart0, "exynos4210-uart.0", "uart0", "aclk100", + GATE_IP_PERIL, 0, 0, 0, "uart"), + GATE_DA(uart1, "exynos4210-uart.1", "uart1", "aclk100", + GATE_IP_PERIL, 1, 0, 0, "uart"), + GATE_DA(uart2, "exynos4210-uart.2", "uart2", "aclk100", + GATE_IP_PERIL, 2, 0, 0, "uart"), + GATE_DA(uart3, "exynos4210-uart.3", "uart3", "aclk100", + GATE_IP_PERIL, 3, 0, 0, "uart"), + GATE_DA(uart4, "exynos4210-uart.4", "uart4", "aclk100", + GATE_IP_PERIL, 4, 0, 0, "uart"), + GATE_DA(i2c0, "s3c2440-i2c.0", "i2c0", "aclk100", + GATE_IP_PERIL, 6, 0, 0, "i2c"), + GATE_DA(i2c1, "s3c2440-i2c.1", "i2c1", "aclk100", + GATE_IP_PERIL, 7, 0, 0, "i2c"), + GATE_DA(i2c2, "s3c2440-i2c.2", "i2c2", "aclk100", + GATE_IP_PERIL, 8, 0, 0, "i2c"), + GATE_DA(i2c3, "s3c2440-i2c.3", "i2c3", "aclk100", + GATE_IP_PERIL, 9, 0, 0, "i2c"), + GATE_DA(i2c4, "s3c2440-i2c.4", "i2c4", "aclk100", + GATE_IP_PERIL, 10, 0, 0, "i2c"), + GATE_DA(i2c5, "s3c2440-i2c.5", "i2c5", "aclk100", + GATE_IP_PERIL, 11, 0, 0, "i2c"), + GATE_DA(i2c6, "s3c2440-i2c.6", "i2c6", "aclk100", + GATE_IP_PERIL, 12, 0, 0, "i2c"), + GATE_DA(i2c7, "s3c2440-i2c.7", "i2c7", "aclk100", + GATE_IP_PERIL, 13, 0, 0, "i2c"), + GATE_DA(i2c_hdmi, "s3c2440-hdmiphy-i2c", "i2c-hdmi", "aclk100", + GATE_IP_PERIL, 14, 0, 0, "i2c"), + GATE_DA(spi0, "exynos4210-spi.0", "spi0", "aclk100", + GATE_IP_PERIL, 16, 0, 0, "spi"), + GATE_DA(spi1, "exynos4210-spi.1", "spi1", "aclk100", + GATE_IP_PERIL, 17, 0, 0, "spi"), + GATE_DA(spi2, "exynos4210-spi.2", "spi2", "aclk100", + GATE_IP_PERIL, 18, 0, 0, "spi"), + GATE_DA(i2s1, "samsung-i2s.1", "i2s1", "aclk100", + GATE_IP_PERIL, 20, 0, 0, "iis"), + GATE_DA(i2s2, "samsung-i2s.2", "i2s2", "aclk100", + GATE_IP_PERIL, 21, 0, 0, "iis"), + GATE_DA(pcm1, "samsung-pcm.1", "pcm1", "aclk100", + GATE_IP_PERIL, 22, 0, 0, "pcm"), + GATE_DA(pcm2, "samsung-pcm.2", "pcm2", "aclk100", + GATE_IP_PERIL, 23, 0, 0, "pcm"), + GATE_DA(spdif, "samsung-spdif", "spdif", "aclk100", + GATE_IP_PERIL, 26, 0, 0, "spdif"), + GATE_DA(ac97, "samsung-ac97", "ac97", "aclk100", + GATE_IP_PERIL, 27, 0, 0, "ac97"), +}; + +/* list of gate clocks supported in exynos4210 soc */ +struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { + GATE(tvenc, "tvenc", "aclk160", GATE_IP_TV, 2, 0, 0), + GATE(g2d, "g2d", "aclk200", E4210_GATE_IP_IMAGE, 0, 0, 0), + GATE(rotator, "rotator", "aclk200", E4210_GATE_IP_IMAGE, 1, 0, 0), + GATE(mdma, "mdma", "aclk200", E4210_GATE_IP_IMAGE, 2, 0, 0), + GATE(smmu_g2d, "smmu_g2d", "aclk200", E4210_GATE_IP_IMAGE, 3, 0, 0), + GATE(smmu_mdma, "smmu_mdma", "aclk200", E4210_GATE_IP_IMAGE, 5, 0, 0), + GATE(pcie_phy, "pcie_phy", "aclk133", GATE_IP_FSYS, 2, 0, 0), + GATE(sata_phy, "sata_phy", "aclk133", GATE_IP_FSYS, 3, 0, 0), + GATE(sata, "sata", "aclk133", GATE_IP_FSYS, 10, 0, 0), + GATE(pcie, "pcie", "aclk133", GATE_IP_FSYS, 14, 0, 0), + GATE(smmu_pcie, "smmu_pcie", "aclk133", GATE_IP_FSYS, 18, 0, 0), + GATE(modemif, "modemif", "aclk100", GATE_IP_PERIL, 28, 0, 0), + GATE(chipid, "chipid", "aclk100", E4210_GATE_IP_PERIR, 0, 0, 0), + GATE(sysreg, "sysreg", "aclk100", E4210_GATE_IP_PERIR, 0, + CLK_IGNORE_UNUSED, 0), + GATE(hdmi_cec, "hdmi_cec", "aclk100", E4210_GATE_IP_PERIR, 11, 0, 0), + GATE(smmu_rotator, "smmu_rotator", "aclk200", + E4210_GATE_IP_IMAGE, 4, 0, 0), + GATE(sclk_mipi1, "sclk_mipi1", "div_mipi_pre1", + E4210_SRC_MASK_LCD1, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_sata, "sclk_sata", "div_sata", + SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mixer, "sclk_mixer", "mout_mixer", SRC_MASK_TV, 4, 0, 0), + GATE(sclk_dac, "sclk_dac", "mout_dac", SRC_MASK_TV, 8, 0, 0), + GATE_A(tsadc, "tsadc", "aclk100", GATE_IP_PERIL, 15, 0, 0, "adc"), + GATE_A(mct, "mct", "aclk100", E4210_GATE_IP_PERIR, 13, 0, 0, "mct"), + GATE_A(wdt, "watchdog", "aclk100", E4210_GATE_IP_PERIR, 14, 0, 0, "watchdog"), + GATE_A(rtc, "rtc", "aclk100", E4210_GATE_IP_PERIR, 15, 0, 0, "rtc"), + GATE_A(keyif, "keyif", "aclk100", E4210_GATE_IP_PERIR, 16, 0, 0, "keypad"), + GATE_DA(sclk_fimd1, "exynos4-fb.1", "sclk_fimd1", "div_fimd1", + E4210_SRC_MASK_LCD1, 0, CLK_SET_RATE_PARENT, 0, "sclk_fimd"), +}; + +/* list of gate clocks supported in exynos4x12 soc */ +struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { + GATE(audss, "audss", "sclk_epll", E4X12_GATE_IP_MAUDIO, 0, 0, 0), + GATE(mdnie0, "mdnie0", "aclk160", GATE_IP_LCD0, 2, 0, 0), + GATE(rotator, "rotator", "aclk200", E4X12_GATE_IP_IMAGE, 1, 0, 0), + GATE(mdma2, "mdma2", "aclk200", E4X12_GATE_IP_IMAGE, 2, 0, 0), + GATE(smmu_mdma, "smmu_mdma", "aclk200", E4X12_GATE_IP_IMAGE, 5, 0, 0), + GATE(mipi_hsi, "mipi_hsi", "aclk133", GATE_IP_FSYS, 10, 0, 0), + GATE(chipid, "chipid", "aclk100", E4X12_GATE_IP_PERIR, 0, 0, 0), + GATE(sysreg, "sysreg", "aclk100", E4X12_GATE_IP_PERIR, 1, + CLK_IGNORE_UNUSED, 0), + GATE(hdmi_cec, "hdmi_cec", "aclk100", E4X12_GATE_IP_PERIR, 11, 0, 0), + GATE(sclk_mdnie0, "sclk_mdnie0", "div_mdnie0", + SRC_MASK_LCD0, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mdnie_pwm0, "sclk_mdnie_pwm0", "div_mdnie_pwm_pre0", + SRC_MASK_LCD0, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mipihsi, "sclk_mipihsi", "div_mipihsi", + SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), + GATE(smmu_rotator, "smmu_rotator", "aclk200", + E4X12_GATE_IP_IMAGE, 4, 0, 0), + GATE_A(mct, "mct", "aclk100", E4X12_GATE_IP_PERIR, 13, 0, 0, "mct"), + GATE_A(rtc, "rtc", "aclk100", E4X12_GATE_IP_PERIR, 15, 0, 0, "rtc"), + GATE_A(keyif, "keyif", "aclk100", + E4X12_GATE_IP_PERIR, 16, 0, 0, "keypad"), + GATE(sclk_pwm_isp, "sclk_pwm_isp", "div_pwm_isp", + E4X12_SRC_MASK_ISP, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi0_isp, "sclk_spi0_isp", "div_spi0_isp_pre", + E4X12_SRC_MASK_ISP, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi1_isp, "sclk_spi1_isp", "div_spi1_isp_pre", + E4X12_SRC_MASK_ISP, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart_isp, "sclk_uart_isp", "div_uart_isp", + E4X12_SRC_MASK_ISP, 12, CLK_SET_RATE_PARENT, 0), + GATE(pwm_isp_sclk, "pwm_isp_sclk", "sclk_pwm_isp", + E4X12_GATE_IP_ISP, 0, 0, 0), + GATE(spi0_isp_sclk, "spi0_isp_sclk", "sclk_spi0_isp", + E4X12_GATE_IP_ISP, 1, 0, 0), + GATE(spi1_isp_sclk, "spi1_isp_sclk", "sclk_spi1_isp", + E4X12_GATE_IP_ISP, 2, 0, 0), + GATE(uart_isp_sclk, "uart_isp_sclk", "sclk_uart_isp", + E4X12_GATE_IP_ISP, 3, 0, 0), + GATE_A(wdt, "watchdog", "aclk100", + E4X12_GATE_IP_PERIR, 14, 0, 0, "watchdog"), + GATE_DA(pcm0, "samsung-pcm.0", "pcm0", "aclk100", + E4X12_GATE_IP_MAUDIO, 2, 0, 0, "pcm"), + GATE_DA(i2s0, "samsung-i2s.0", "i2s0", "aclk100", + E4X12_GATE_IP_MAUDIO, 3, 0, 0, "iis"), + GATE(fimc_isp, "isp", "aclk200", E4X12_GATE_ISP0, 0, + CLK_IGNORE_UNUSED, 0), + GATE(fimc_drc, "drc", "aclk200", E4X12_GATE_ISP0, 1, + CLK_IGNORE_UNUSED, 0), + GATE(fimc_fd, "fd", "aclk200", E4X12_GATE_ISP0, 2, + CLK_IGNORE_UNUSED, 0), + GATE(fimc_lite0, "lite0", "aclk200", E4X12_GATE_ISP0, 3, + CLK_IGNORE_UNUSED, 0), + GATE(fimc_lite1, "lite1", "aclk200", E4X12_GATE_ISP0, 4, + CLK_IGNORE_UNUSED, 0), + GATE(mcuisp, "mcuisp", "aclk200", E4X12_GATE_ISP0, 5, + CLK_IGNORE_UNUSED, 0), + GATE(gicisp, "gicisp", "aclk200", E4X12_GATE_ISP0, 7, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_isp, "smmu_isp", "aclk200", E4X12_GATE_ISP0, 8, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_drc, "smmu_drc", "aclk200", E4X12_GATE_ISP0, 9, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_fd, "smmu_fd", "aclk200", E4X12_GATE_ISP0, 10, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_lite0, "smmu_lite0", "aclk200", E4X12_GATE_ISP0, 11, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_lite1, "smmu_lite1", "aclk200", E4X12_GATE_ISP0, 12, + CLK_IGNORE_UNUSED, 0), + GATE(ppmuispmx, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20, + CLK_IGNORE_UNUSED, 0), + GATE(ppmuispx, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21, + CLK_IGNORE_UNUSED, 0), + GATE(mcuctl_isp, "mcuctl_isp", "aclk200", E4X12_GATE_ISP0, 23, + CLK_IGNORE_UNUSED, 0), + GATE(mpwm_isp, "mpwm_isp", "aclk200", E4X12_GATE_ISP0, 24, + CLK_IGNORE_UNUSED, 0), + GATE(i2c0_isp, "i2c0_isp", "aclk200", E4X12_GATE_ISP0, 25, + CLK_IGNORE_UNUSED, 0), + GATE(i2c1_isp, "i2c1_isp", "aclk200", E4X12_GATE_ISP0, 26, + CLK_IGNORE_UNUSED, 0), + GATE(mtcadc_isp, "mtcadc_isp", "aclk200", E4X12_GATE_ISP0, 27, + CLK_IGNORE_UNUSED, 0), + GATE(pwm_isp, "pwm_isp", "aclk200", E4X12_GATE_ISP0, 28, + CLK_IGNORE_UNUSED, 0), + GATE(wdt_isp, "wdt_isp", "aclk200", E4X12_GATE_ISP0, 30, + CLK_IGNORE_UNUSED, 0), + GATE(uart_isp, "uart_isp", "aclk200", E4X12_GATE_ISP0, 31, + CLK_IGNORE_UNUSED, 0), + GATE(asyncaxim, "asyncaxim", "aclk200", E4X12_GATE_ISP1, 0, + CLK_IGNORE_UNUSED, 0), + GATE(smmu_ispcx, "smmu_ispcx", "aclk200", E4X12_GATE_ISP1, 4, + CLK_IGNORE_UNUSED, 0), + GATE(spi0_isp, "spi0_isp", "aclk200", E4X12_GATE_ISP1, 12, + CLK_IGNORE_UNUSED, 0), + GATE(spi1_isp, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, + CLK_IGNORE_UNUSED, 0), +}; + +/* + * The parent of the fin_pll clock is selected by the XOM[0] bit. This bit + * resides in chipid register space, outside of the clock controller memory + * mapped space. So to determine the parent of fin_pll clock, the chipid + * controller is first remapped and the value of XOM[0] bit is read to + * determine the parent clock. + */ +static unsigned long exynos4_get_xom(void) +{ + unsigned long xom = 0; + void __iomem *chipid_base; + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid"); + if (np) { + chipid_base = of_iomap(np, 0); + + if (chipid_base) + xom = readl(chipid_base + 8); + + iounmap(chipid_base); + } + + return xom; +} + +static void __init exynos4_clk_register_finpll(unsigned long xom) +{ + struct samsung_fixed_rate_clock fclk; + struct clk *clk; + unsigned long finpll_f = 24000000; + char *parent_name; + + parent_name = xom & 1 ? "xusbxti" : "xxti"; + clk = clk_get(NULL, parent_name); + if (IS_ERR(clk)) { + pr_err("%s: failed to lookup parent clock %s, assuming " + "fin_pll clock frequency is 24MHz\n", __func__, + parent_name); + } else { + finpll_f = clk_get_rate(clk); + } + + fclk.id = fin_pll; + fclk.name = "fin_pll"; + fclk.parent_name = NULL; + fclk.flags = CLK_IS_ROOT; + fclk.fixed_rate = finpll_f; + samsung_clk_register_fixed_rate(&fclk, 1); + +} + +/* + * This function allows non-dt platforms to specify the clock speed of the + * xxti and xusbxti clocks. These clocks are then registered with the specified + * clock speed. + */ +void __init exynos4_clk_register_fixed_ext(unsigned long xxti_f, + unsigned long xusbxti_f) +{ + exynos4_fixed_rate_ext_clks[0].fixed_rate = xxti_f; + exynos4_fixed_rate_ext_clks[1].fixed_rate = xusbxti_f; + samsung_clk_register_fixed_rate(exynos4_fixed_rate_ext_clks, + ARRAY_SIZE(exynos4_fixed_rate_ext_clks)); +} + +static __initdata struct of_device_id ext_clk_match[] = { + { .compatible = "samsung,clock-xxti", .data = (void *)0, }, + { .compatible = "samsung,clock-xusbxti", .data = (void *)1, }, + {}, +}; + +/* register exynos4 clocks */ +void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_soc, void __iomem *reg_base, unsigned long xom) +{ + struct clk *apll, *mpll, *epll, *vpll; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + if (exynos4_soc == EXYNOS4210) + samsung_clk_init(np, reg_base, nr_clks, + exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs), + exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save)); + else + samsung_clk_init(np, reg_base, nr_clks, + exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs), + exynos4x12_clk_save, ARRAY_SIZE(exynos4x12_clk_save)); + + if (np) + samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks, + ARRAY_SIZE(exynos4_fixed_rate_ext_clks), + ext_clk_match); + + exynos4_clk_register_finpll(xom); + + if (exynos4_soc == EXYNOS4210) { + apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll", + reg_base + APLL_CON0, pll_4508); + mpll = samsung_clk_register_pll45xx("fout_mpll", "fin_pll", + reg_base + E4210_MPLL_CON0, pll_4508); + epll = samsung_clk_register_pll46xx("fout_epll", "fin_pll", + reg_base + EPLL_CON0, pll_4600); + vpll = samsung_clk_register_pll46xx("fout_vpll", "mout_vpllsrc", + reg_base + VPLL_CON0, pll_4650c); + } else { + apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll", + reg_base + APLL_CON0); + mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll", + reg_base + E4X12_MPLL_CON0); + epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll", + reg_base + EPLL_CON0); + vpll = samsung_clk_register_pll36xx("fout_vpll", "fin_pll", + reg_base + VPLL_CON0); + } + + samsung_clk_add_lookup(apll, fout_apll); + samsung_clk_add_lookup(mpll, fout_mpll); + samsung_clk_add_lookup(epll, fout_epll); + samsung_clk_add_lookup(vpll, fout_vpll); + + samsung_clk_register_fixed_rate(exynos4_fixed_rate_clks, + ARRAY_SIZE(exynos4_fixed_rate_clks)); + samsung_clk_register_mux(exynos4_mux_clks, + ARRAY_SIZE(exynos4_mux_clks)); + samsung_clk_register_div(exynos4_div_clks, + ARRAY_SIZE(exynos4_div_clks)); + samsung_clk_register_gate(exynos4_gate_clks, + ARRAY_SIZE(exynos4_gate_clks)); + + if (exynos4_soc == EXYNOS4210) { + samsung_clk_register_fixed_rate(exynos4210_fixed_rate_clks, + ARRAY_SIZE(exynos4210_fixed_rate_clks)); + samsung_clk_register_mux(exynos4210_mux_clks, + ARRAY_SIZE(exynos4210_mux_clks)); + samsung_clk_register_div(exynos4210_div_clks, + ARRAY_SIZE(exynos4210_div_clks)); + samsung_clk_register_gate(exynos4210_gate_clks, + ARRAY_SIZE(exynos4210_gate_clks)); + } else { + samsung_clk_register_mux(exynos4x12_mux_clks, + ARRAY_SIZE(exynos4x12_mux_clks)); + samsung_clk_register_div(exynos4x12_div_clks, + ARRAY_SIZE(exynos4x12_div_clks)); + samsung_clk_register_gate(exynos4x12_gate_clks, + ARRAY_SIZE(exynos4x12_gate_clks)); + } + + pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n" + "\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n", + exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12", + _get_rate("sclk_apll"), _get_rate("sclk_mpll"), + _get_rate("sclk_epll"), _get_rate("sclk_vpll"), + _get_rate("arm_clk")); +} + + +static void __init exynos4210_clk_init(struct device_node *np) +{ + exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom()); +} +CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init); + +static void __init exynos4412_clk_init(struct device_node *np) +{ + exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom()); +} +CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init); diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c new file mode 100644 index 00000000000..5c97e75924a --- /dev/null +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham <thomas.ab@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for Exynos5250 SoC. +*/ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk.h" +#include "clk-pll.h" + +#define SRC_CPU 0x200 +#define DIV_CPU0 0x500 +#define SRC_CORE1 0x4204 +#define SRC_TOP0 0x10210 +#define SRC_TOP2 0x10218 +#define SRC_GSCL 0x10220 +#define SRC_DISP1_0 0x1022c +#define SRC_MAU 0x10240 +#define SRC_FSYS 0x10244 +#define SRC_GEN 0x10248 +#define SRC_PERIC0 0x10250 +#define SRC_PERIC1 0x10254 +#define SRC_MASK_GSCL 0x10320 +#define SRC_MASK_DISP1_0 0x1032c +#define SRC_MASK_MAU 0x10334 +#define SRC_MASK_FSYS 0x10340 +#define SRC_MASK_GEN 0x10344 +#define SRC_MASK_PERIC0 0x10350 +#define SRC_MASK_PERIC1 0x10354 +#define DIV_TOP0 0x10510 +#define DIV_TOP1 0x10514 +#define DIV_GSCL 0x10520 +#define DIV_DISP1_0 0x1052c +#define DIV_GEN 0x1053c +#define DIV_MAU 0x10544 +#define DIV_FSYS0 0x10548 +#define DIV_FSYS1 0x1054c +#define DIV_FSYS2 0x10550 +#define DIV_PERIC0 0x10558 +#define DIV_PERIC1 0x1055c +#define DIV_PERIC2 0x10560 +#define DIV_PERIC3 0x10564 +#define DIV_PERIC4 0x10568 +#define DIV_PERIC5 0x1056c +#define GATE_IP_GSCL 0x10920 +#define GATE_IP_MFC 0x1092c +#define GATE_IP_GEN 0x10934 +#define GATE_IP_FSYS 0x10944 +#define GATE_IP_PERIC 0x10950 +#define GATE_IP_PERIS 0x10960 +#define SRC_CDREX 0x20200 +#define PLL_DIV2_SEL 0x20a24 +#define GATE_IP_DISP1 0x10928 + +/* + * Let each supported clock get a unique id. This id is used to lookup the clock + * for device tree based platforms. The clocks are categorized into three + * sections: core, sclk gate and bus interface gate clocks. + * + * When adding a new clock to this list, it is advised to choose a clock + * category and add it to the end of that category. That is because the the + * device tree source file is referring to these ids and any change in the + * sequence number of existing clocks will require corresponding change in the + * device tree files. This limitation would go away when pre-processor support + * for dtc would be available. + */ +enum exynos5250_clks { + none, + + /* core clocks */ + fin_pll, + + /* gate for special clocks (sclk) */ + sclk_cam_bayer = 128, sclk_cam0, sclk_cam1, sclk_gscl_wa, sclk_gscl_wb, + sclk_fimd1, sclk_mipi1, sclk_dp, sclk_hdmi, sclk_pixel, sclk_audio0, + sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_sata, sclk_usb3, + sclk_jpeg, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_pwm, + sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, + + /* gate clocks */ + gscl0 = 256, gscl1, gscl2, gscl3, gscl_wa, gscl_wb, smmu_gscl0, + smmu_gscl1, smmu_gscl2, smmu_gscl3, mfc, smmu_mfcl, smmu_mfcr, rotator, + jpeg, mdma1, smmu_rotator, smmu_jpeg, smmu_mdma1, pdma0, pdma1, sata, + usbotg, mipi_hsi, sdmmc0, sdmmc1, sdmmc2, sdmmc3, sromc, usb2, usb3, + sata_phyctrl, sata_phyi2c, uart0, uart1, uart2, uart3, uart4, i2c0, + i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, adc, spi0, spi1, + spi2, i2s1, i2s2, pcm1, pcm2, pwm, spdif, ac97, hsi2c0, hsi2c1, hsi2c2, + hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1, + tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct, + wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, + + nr_clks, +}; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static __initdata unsigned long exynos5250_clk_regs[] = { + SRC_CPU, + DIV_CPU0, + SRC_CORE1, + SRC_TOP0, + SRC_TOP2, + SRC_GSCL, + SRC_DISP1_0, + SRC_MAU, + SRC_FSYS, + SRC_GEN, + SRC_PERIC0, + SRC_PERIC1, + SRC_MASK_GSCL, + SRC_MASK_DISP1_0, + SRC_MASK_MAU, + SRC_MASK_FSYS, + SRC_MASK_GEN, + SRC_MASK_PERIC0, + SRC_MASK_PERIC1, + DIV_TOP0, + DIV_TOP1, + DIV_GSCL, + DIV_DISP1_0, + DIV_GEN, + DIV_MAU, + DIV_FSYS0, + DIV_FSYS1, + DIV_FSYS2, + DIV_PERIC0, + DIV_PERIC1, + DIV_PERIC2, + DIV_PERIC3, + DIV_PERIC4, + DIV_PERIC5, + GATE_IP_GSCL, + GATE_IP_MFC, + GATE_IP_GEN, + GATE_IP_FSYS, + GATE_IP_PERIC, + GATE_IP_PERIS, + SRC_CDREX, + PLL_DIV2_SEL, + GATE_IP_DISP1, +}; + +/* list of all parent clock list */ +PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; +PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", }; +PNAME(mout_mpll_fout_p) = { "fout_mplldiv2", "fout_mpll" }; +PNAME(mout_mpll_p) = { "fin_pll", "mout_mpll_fout" }; +PNAME(mout_bpll_fout_p) = { "fout_bplldiv2", "fout_bpll" }; +PNAME(mout_bpll_p) = { "fin_pll", "mout_bpll_fout" }; +PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi27m" }; +PNAME(mout_vpll_p) = { "mout_vpllsrc", "fout_vpll" }; +PNAME(mout_cpll_p) = { "fin_pll", "fout_cpll" }; +PNAME(mout_epll_p) = { "fin_pll", "fout_epll" }; +PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll" }; +PNAME(mout_bpll_user_p) = { "fin_pll", "sclk_bpll" }; +PNAME(mout_aclk166_p) = { "sclk_cpll", "sclk_mpll_user" }; +PNAME(mout_aclk200_p) = { "sclk_mpll_user", "sclk_bpll_user" }; +PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" }; +PNAME(mout_usb3_p) = { "sclk_mpll_user", "sclk_cpll" }; +PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m", + "sclk_dptxphy", "sclk_uhostphy", "sclk_hdmiphy", + "sclk_mpll_user", "sclk_epll", "sclk_vpll", + "sclk_cpll" }; +PNAME(mout_audio0_p) = { "cdclk0", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy", + "sclk_uhostphy", "sclk_hdmiphy", + "sclk_mpll_user", "sclk_epll", "sclk_vpll", + "sclk_cpll" }; +PNAME(mout_audio1_p) = { "cdclk1", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy", + "sclk_uhostphy", "sclk_hdmiphy", + "sclk_mpll_user", "sclk_epll", "sclk_vpll", + "sclk_cpll" }; +PNAME(mout_audio2_p) = { "cdclk2", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy", + "sclk_uhostphy", "sclk_hdmiphy", + "sclk_mpll_user", "sclk_epll", "sclk_vpll", + "sclk_cpll" }; +PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2", + "spdif_extclk" }; + +/* fixed rate clocks generated outside the soc */ +struct samsung_fixed_rate_clock exynos5250_fixed_rate_ext_clks[] __initdata = { + FRATE(fin_pll, "fin_pll", NULL, CLK_IS_ROOT, 0), +}; + +/* fixed rate clocks generated inside the soc */ +struct samsung_fixed_rate_clock exynos5250_fixed_rate_clks[] __initdata = { + FRATE(none, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000), + FRATE(none, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000), + FRATE(none, "sclk_dptxphy", NULL, CLK_IS_ROOT, 24000000), + FRATE(none, "sclk_uhostphy", NULL, CLK_IS_ROOT, 48000000), +}; + +struct samsung_fixed_factor_clock exynos5250_fixed_factor_clks[] __initdata = { + FFACTOR(none, "fout_mplldiv2", "fout_mpll", 1, 2, 0), + FFACTOR(none, "fout_bplldiv2", "fout_bpll", 1, 2, 0), +}; + +struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { + MUX(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), + MUX(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1), + MUX(none, "mout_mpll_fout", mout_mpll_fout_p, PLL_DIV2_SEL, 4, 1), + MUX(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1), + MUX(none, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1), + MUX(none, "sclk_bpll", mout_bpll_p, SRC_CDREX, 0, 1), + MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1), + MUX(none, "sclk_vpll", mout_vpll_p, SRC_TOP2, 16, 1), + MUX(none, "sclk_epll", mout_epll_p, SRC_TOP2, 12, 1), + MUX(none, "sclk_cpll", mout_cpll_p, SRC_TOP2, 8, 1), + MUX(none, "sclk_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1), + MUX(none, "sclk_bpll_user", mout_bpll_user_p, SRC_TOP2, 24, 1), + MUX(none, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1), + MUX(none, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1), + MUX(none, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1), + MUX(none, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4), + MUX(none, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4), + MUX(none, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4), + MUX(none, "mout_gscl_wa", mout_group1_p, SRC_GSCL, 24, 4), + MUX(none, "mout_gscl_wb", mout_group1_p, SRC_GSCL, 28, 4), + MUX(none, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4), + MUX(none, "mout_mipi1", mout_group1_p, SRC_DISP1_0, 12, 4), + MUX(none, "mout_dp", mout_group1_p, SRC_DISP1_0, 16, 4), + MUX(none, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1), + MUX(none, "mout_audio0", mout_audio0_p, SRC_MAU, 0, 4), + MUX(none, "mout_mmc0", mout_group1_p, SRC_FSYS, 0, 4), + MUX(none, "mout_mmc1", mout_group1_p, SRC_FSYS, 4, 4), + MUX(none, "mout_mmc2", mout_group1_p, SRC_FSYS, 8, 4), + MUX(none, "mout_mmc3", mout_group1_p, SRC_FSYS, 12, 4), + MUX(none, "mout_sata", mout_aclk200_p, SRC_FSYS, 24, 1), + MUX(none, "mout_usb3", mout_usb3_p, SRC_FSYS, 28, 1), + MUX(none, "mout_jpeg", mout_group1_p, SRC_GEN, 0, 4), + MUX(none, "mout_uart0", mout_group1_p, SRC_PERIC0, 0, 4), + MUX(none, "mout_uart1", mout_group1_p, SRC_PERIC0, 4, 4), + MUX(none, "mout_uart2", mout_group1_p, SRC_PERIC0, 8, 4), + MUX(none, "mout_uart3", mout_group1_p, SRC_PERIC0, 12, 4), + MUX(none, "mout_pwm", mout_group1_p, SRC_PERIC0, 24, 4), + MUX(none, "mout_audio1", mout_audio1_p, SRC_PERIC1, 0, 4), + MUX(none, "mout_audio2", mout_audio2_p, SRC_PERIC1, 4, 4), + MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIC1, 8, 2), + MUX(none, "mout_spi0", mout_group1_p, SRC_PERIC1, 16, 4), + MUX(none, "mout_spi1", mout_group1_p, SRC_PERIC1, 20, 4), + MUX(none, "mout_spi2", mout_group1_p, SRC_PERIC1, 24, 4), +}; + +struct samsung_div_clock exynos5250_div_clks[] __initdata = { + DIV(none, "div_arm", "mout_cpu", DIV_CPU0, 0, 3), + DIV(none, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3), + DIV(none, "aclk66_pre", "sclk_mpll_user", DIV_TOP1, 24, 3), + DIV(none, "aclk66", "aclk66_pre", DIV_TOP0, 0, 3), + DIV(none, "aclk266", "sclk_mpll_user", DIV_TOP0, 16, 3), + DIV(none, "aclk166", "mout_aclk166", DIV_TOP0, 8, 3), + DIV(none, "aclk333", "mout_aclk333", DIV_TOP0, 20, 3), + DIV(none, "aclk200", "mout_aclk200", DIV_TOP0, 12, 3), + DIV(none, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4), + DIV(none, "div_cam0", "mout_cam0", DIV_GSCL, 16, 4), + DIV(none, "div_cam1", "mout_cam1", DIV_GSCL, 20, 4), + DIV(none, "div_gscl_wa", "mout_gscl_wa", DIV_GSCL, 24, 4), + DIV(none, "div_gscl_wb", "mout_gscl_wb", DIV_GSCL, 28, 4), + DIV(none, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4), + DIV(none, "div_mipi1", "mout_mipi1", DIV_DISP1_0, 16, 4), + DIV(none, "div_dp", "mout_dp", DIV_DISP1_0, 24, 4), + DIV(none, "div_jpeg", "mout_jpeg", DIV_GEN, 4, 4), + DIV(none, "div_audio0", "mout_audio0", DIV_MAU, 0, 4), + DIV(none, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8), + DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4), + DIV(none, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4), + DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), + DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), + DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4), + DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4), + DIV(none, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4), + DIV(none, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4), + DIV(none, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4), + DIV(none, "div_uart3", "mout_uart3", DIV_PERIC0, 12, 4), + DIV(none, "div_spi0", "mout_spi0", DIV_PERIC1, 0, 4), + DIV(none, "div_spi1", "mout_spi1", DIV_PERIC1, 16, 4), + DIV(none, "div_spi2", "mout_spi2", DIV_PERIC2, 0, 4), + DIV(none, "div_pwm", "mout_pwm", DIV_PERIC3, 0, 4), + DIV(none, "div_audio1", "mout_audio1", DIV_PERIC4, 0, 4), + DIV(none, "div_pcm1", "sclk_audio1", DIV_PERIC4, 4, 8), + DIV(none, "div_audio2", "mout_audio2", DIV_PERIC4, 16, 4), + DIV(none, "div_pcm2", "sclk_audio2", DIV_PERIC4, 20, 8), + DIV(none, "div_i2s1", "sclk_audio1", DIV_PERIC5, 0, 6), + DIV(none, "div_i2s2", "sclk_audio2", DIV_PERIC5, 8, 6), + DIV(sclk_pixel, "div_hdmi_pixel", "sclk_vpll", DIV_DISP1_0, 28, 4), + DIV_A(none, "armclk", "div_arm", DIV_CPU0, 28, 3, "armclk"), + DIV_F(none, "div_mipi1_pre", "div_mipi1", + DIV_DISP1_0, 20, 4, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre0", "div_mmc0", + DIV_FSYS1, 8, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre1", "div_mmc1", + DIV_FSYS1, 24, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre2", "div_mmc2", + DIV_FSYS2, 8, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_mmc_pre3", "div_mmc3", + DIV_FSYS2, 24, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_spi_pre0", "div_spi0", + DIV_PERIC1, 8, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_spi_pre1", "div_spi1", + DIV_PERIC1, 24, 8, CLK_SET_RATE_PARENT, 0), + DIV_F(none, "div_spi_pre2", "div_spi2", + DIV_PERIC2, 8, 8, CLK_SET_RATE_PARENT, 0), +}; + +struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { + GATE(gscl0, "gscl0", "none", GATE_IP_GSCL, 0, 0, 0), + GATE(gscl1, "gscl1", "none", GATE_IP_GSCL, 1, 0, 0), + GATE(gscl2, "gscl2", "aclk266", GATE_IP_GSCL, 2, 0, 0), + GATE(gscl3, "gscl3", "aclk266", GATE_IP_GSCL, 3, 0, 0), + GATE(gscl_wa, "gscl_wa", "div_gscl_wa", GATE_IP_GSCL, 5, 0, 0), + GATE(gscl_wb, "gscl_wb", "div_gscl_wb", GATE_IP_GSCL, 6, 0, 0), + GATE(smmu_gscl0, "smmu_gscl0", "aclk266", GATE_IP_GSCL, 7, 0, 0), + GATE(smmu_gscl1, "smmu_gscl1", "aclk266", GATE_IP_GSCL, 8, 0, 0), + GATE(smmu_gscl2, "smmu_gscl2", "aclk266", GATE_IP_GSCL, 9, 0, 0), + GATE(smmu_gscl3, "smmu_gscl3", "aclk266", GATE_IP_GSCL, 10, 0, 0), + GATE(mfc, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0), + GATE(smmu_mfcl, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0), + GATE(smmu_mfcr, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0), + GATE(rotator, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0), + GATE(jpeg, "jpeg", "aclk166", GATE_IP_GEN, 2, 0, 0), + GATE(mdma1, "mdma1", "aclk266", GATE_IP_GEN, 4, 0, 0), + GATE(smmu_rotator, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0), + GATE(smmu_jpeg, "smmu_jpeg", "aclk166", GATE_IP_GEN, 7, 0, 0), + GATE(smmu_mdma1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0), + GATE(pdma0, "pdma0", "aclk200", GATE_IP_FSYS, 1, 0, 0), + GATE(pdma1, "pdma1", "aclk200", GATE_IP_FSYS, 2, 0, 0), + GATE(sata, "sata", "aclk200", GATE_IP_FSYS, 6, 0, 0), + GATE(usbotg, "usbotg", "aclk200", GATE_IP_FSYS, 7, 0, 0), + GATE(mipi_hsi, "mipi_hsi", "aclk200", GATE_IP_FSYS, 8, 0, 0), + GATE(sdmmc0, "sdmmc0", "aclk200", GATE_IP_FSYS, 12, 0, 0), + GATE(sdmmc1, "sdmmc1", "aclk200", GATE_IP_FSYS, 13, 0, 0), + GATE(sdmmc2, "sdmmc2", "aclk200", GATE_IP_FSYS, 14, 0, 0), + GATE(sdmmc3, "sdmmc3", "aclk200", GATE_IP_FSYS, 15, 0, 0), + GATE(sromc, "sromc", "aclk200", GATE_IP_FSYS, 17, 0, 0), + GATE(usb2, "usb2", "aclk200", GATE_IP_FSYS, 18, 0, 0), + GATE(usb3, "usb3", "aclk200", GATE_IP_FSYS, 19, 0, 0), + GATE(sata_phyctrl, "sata_phyctrl", "aclk200", GATE_IP_FSYS, 24, 0, 0), + GATE(sata_phyi2c, "sata_phyi2c", "aclk200", GATE_IP_FSYS, 25, 0, 0), + GATE(uart0, "uart0", "aclk66", GATE_IP_PERIC, 0, 0, 0), + GATE(uart1, "uart1", "aclk66", GATE_IP_PERIC, 1, 0, 0), + GATE(uart2, "uart2", "aclk66", GATE_IP_PERIC, 2, 0, 0), + GATE(uart3, "uart3", "aclk66", GATE_IP_PERIC, 3, 0, 0), + GATE(uart4, "uart4", "aclk66", GATE_IP_PERIC, 4, 0, 0), + GATE(i2c0, "i2c0", "aclk66", GATE_IP_PERIC, 6, 0, 0), + GATE(i2c1, "i2c1", "aclk66", GATE_IP_PERIC, 7, 0, 0), + GATE(i2c2, "i2c2", "aclk66", GATE_IP_PERIC, 8, 0, 0), + GATE(i2c3, "i2c3", "aclk66", GATE_IP_PERIC, 9, 0, 0), + GATE(i2c4, "i2c4", "aclk66", GATE_IP_PERIC, 10, 0, 0), + GATE(i2c5, "i2c5", "aclk66", GATE_IP_PERIC, 11, 0, 0), + GATE(i2c6, "i2c6", "aclk66", GATE_IP_PERIC, 12, 0, 0), + GATE(i2c7, "i2c7", "aclk66", GATE_IP_PERIC, 13, 0, 0), + GATE(i2c_hdmi, "i2c_hdmi", "aclk66", GATE_IP_PERIC, 14, 0, 0), + GATE(adc, "adc", "aclk66", GATE_IP_PERIC, 15, 0, 0), + GATE(spi0, "spi0", "aclk66", GATE_IP_PERIC, 16, 0, 0), + GATE(spi1, "spi1", "aclk66", GATE_IP_PERIC, 17, 0, 0), + GATE(spi2, "spi2", "aclk66", GATE_IP_PERIC, 18, 0, 0), + GATE(i2s1, "i2s1", "aclk66", GATE_IP_PERIC, 20, 0, 0), + GATE(i2s2, "i2s2", "aclk66", GATE_IP_PERIC, 21, 0, 0), + GATE(pcm1, "pcm1", "aclk66", GATE_IP_PERIC, 22, 0, 0), + GATE(pcm2, "pcm2", "aclk66", GATE_IP_PERIC, 23, 0, 0), + GATE(pwm, "pwm", "aclk66", GATE_IP_PERIC, 24, 0, 0), + GATE(spdif, "spdif", "aclk66", GATE_IP_PERIC, 26, 0, 0), + GATE(ac97, "ac97", "aclk66", GATE_IP_PERIC, 27, 0, 0), + GATE(hsi2c0, "hsi2c0", "aclk66", GATE_IP_PERIC, 28, 0, 0), + GATE(hsi2c1, "hsi2c1", "aclk66", GATE_IP_PERIC, 29, 0, 0), + GATE(hsi2c2, "hsi2c2", "aclk66", GATE_IP_PERIC, 30, 0, 0), + GATE(hsi2c3, "hsi2c3", "aclk66", GATE_IP_PERIC, 31, 0, 0), + GATE(chipid, "chipid", "aclk66", GATE_IP_PERIS, 0, 0, 0), + GATE(sysreg, "sysreg", "aclk66", GATE_IP_PERIS, 1, 0, 0), + GATE(pmu, "pmu", "aclk66", GATE_IP_PERIS, 2, 0, 0), + GATE(tzpc0, "tzpc0", "aclk66", GATE_IP_PERIS, 6, 0, 0), + GATE(tzpc1, "tzpc1", "aclk66", GATE_IP_PERIS, 7, 0, 0), + GATE(tzpc2, "tzpc2", "aclk66", GATE_IP_PERIS, 8, 0, 0), + GATE(tzpc3, "tzpc3", "aclk66", GATE_IP_PERIS, 9, 0, 0), + GATE(tzpc4, "tzpc4", "aclk66", GATE_IP_PERIS, 10, 0, 0), + GATE(tzpc5, "tzpc5", "aclk66", GATE_IP_PERIS, 11, 0, 0), + GATE(tzpc6, "tzpc6", "aclk66", GATE_IP_PERIS, 12, 0, 0), + GATE(tzpc7, "tzpc7", "aclk66", GATE_IP_PERIS, 13, 0, 0), + GATE(tzpc8, "tzpc8", "aclk66", GATE_IP_PERIS, 14, 0, 0), + GATE(tzpc9, "tzpc9", "aclk66", GATE_IP_PERIS, 15, 0, 0), + GATE(hdmi_cec, "hdmi_cec", "aclk66", GATE_IP_PERIS, 16, 0, 0), + GATE(mct, "mct", "aclk66", GATE_IP_PERIS, 18, 0, 0), + GATE(wdt, "wdt", "aclk66", GATE_IP_PERIS, 19, 0, 0), + GATE(rtc, "rtc", "aclk66", GATE_IP_PERIS, 20, 0, 0), + GATE(tmu, "tmu", "aclk66", GATE_IP_PERIS, 21, 0, 0), + GATE(cmu_top, "cmu_top", "aclk66", + GATE_IP_PERIS, 3, CLK_IGNORE_UNUSED, 0), + GATE(cmu_core, "cmu_core", "aclk66", + GATE_IP_PERIS, 4, CLK_IGNORE_UNUSED, 0), + GATE(cmu_mem, "cmu_mem", "aclk66", + GATE_IP_PERIS, 5, CLK_IGNORE_UNUSED, 0), + GATE(sclk_cam_bayer, "sclk_cam_bayer", "div_cam_bayer", + SRC_MASK_GSCL, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_cam0, "sclk_cam0", "div_cam0", + SRC_MASK_GSCL, 16, CLK_SET_RATE_PARENT, 0), + GATE(sclk_cam1, "sclk_cam1", "div_cam1", + SRC_MASK_GSCL, 20, CLK_SET_RATE_PARENT, 0), + GATE(sclk_gscl_wa, "sclk_gscl_wa", "div_gscl_wa", + SRC_MASK_GSCL, 24, CLK_SET_RATE_PARENT, 0), + GATE(sclk_gscl_wb, "sclk_gscl_wb", "div_gscl_wb", + SRC_MASK_GSCL, 28, CLK_SET_RATE_PARENT, 0), + GATE(sclk_fimd1, "sclk_fimd1", "div_fimd1", + SRC_MASK_DISP1_0, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mipi1, "sclk_mipi1", "div_mipi1", + SRC_MASK_DISP1_0, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_dp, "sclk_dp", "div_dp", + SRC_MASK_DISP1_0, 16, CLK_SET_RATE_PARENT, 0), + GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi", + SRC_MASK_DISP1_0, 20, 0, 0), + GATE(sclk_audio0, "sclk_audio0", "div_audio0", + SRC_MASK_MAU, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc0, "sclk_mmc0", "div_mmc_pre0", + SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc1, "sclk_mmc1", "div_mmc_pre1", + SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc2, "sclk_mmc2", "div_mmc_pre2", + SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc3, "sclk_mmc3", "div_mmc_pre3", + SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_sata, "sclk_sata", "div_sata", + SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), + GATE(sclk_usb3, "sclk_usb3", "div_usb3", + SRC_MASK_FSYS, 28, CLK_SET_RATE_PARENT, 0), + GATE(sclk_jpeg, "sclk_jpeg", "div_jpeg", + SRC_MASK_GEN, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart0, "sclk_uart0", "div_uart0", + SRC_MASK_PERIC0, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart1, "sclk_uart1", "div_uart1", + SRC_MASK_PERIC0, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart2, "sclk_uart2", "div_uart2", + SRC_MASK_PERIC0, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart3, "sclk_uart3", "div_uart3", + SRC_MASK_PERIC0, 12, CLK_SET_RATE_PARENT, 0), + GATE(sclk_pwm, "sclk_pwm", "div_pwm", + SRC_MASK_PERIC0, 24, CLK_SET_RATE_PARENT, 0), + GATE(sclk_audio1, "sclk_audio1", "div_audio1", + SRC_MASK_PERIC1, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_audio2, "sclk_audio2", "div_audio2", + SRC_MASK_PERIC1, 4, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spdif, "sclk_spdif", "mout_spdif", + SRC_MASK_PERIC1, 4, 0, 0), + GATE(sclk_spi0, "sclk_spi0", "div_spi_pre0", + SRC_MASK_PERIC1, 16, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi1, "sclk_spi1", "div_spi_pre1", + SRC_MASK_PERIC1, 20, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi2, "sclk_spi2", "div_spi_pre2", + SRC_MASK_PERIC1, 24, CLK_SET_RATE_PARENT, 0), + GATE(fimd1, "fimd1", "aclk200", GATE_IP_DISP1, 0, 0, 0), + GATE(mie1, "mie1", "aclk200", GATE_IP_DISP1, 1, 0, 0), + GATE(dsim0, "dsim0", "aclk200", GATE_IP_DISP1, 3, 0, 0), + GATE(dp, "dp", "aclk200", GATE_IP_DISP1, 4, 0, 0), + GATE(mixer, "mixer", "aclk200", GATE_IP_DISP1, 5, 0, 0), + GATE(hdmi, "hdmi", "aclk200", GATE_IP_DISP1, 6, 0, 0), +}; + +static __initdata struct of_device_id ext_clk_match[] = { + { .compatible = "samsung,clock-xxti", .data = (void *)0, }, + { }, +}; + +/* register exynox5250 clocks */ +void __init exynos5250_clk_init(struct device_node *np) +{ + void __iomem *reg_base; + struct clk *apll, *mpll, *epll, *vpll, *bpll, *gpll, *cpll; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } else { + panic("%s: unable to determine soc\n", __func__); + } + + samsung_clk_init(np, reg_base, nr_clks, + exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs), + NULL, 0); + samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks, + ARRAY_SIZE(exynos5250_fixed_rate_ext_clks), + ext_clk_match); + + apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll", + reg_base + 0x100); + mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll", + reg_base + 0x4100); + bpll = samsung_clk_register_pll35xx("fout_bpll", "fin_pll", + reg_base + 0x20110); + gpll = samsung_clk_register_pll35xx("fout_gpll", "fin_pll", + reg_base + 0x10150); + cpll = samsung_clk_register_pll35xx("fout_cpll", "fin_pll", + reg_base + 0x10120); + epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll", + reg_base + 0x10130); + vpll = samsung_clk_register_pll36xx("fout_vpll", "mout_vpllsrc", + reg_base + 0x10140); + + samsung_clk_register_fixed_rate(exynos5250_fixed_rate_clks, + ARRAY_SIZE(exynos5250_fixed_rate_clks)); + samsung_clk_register_fixed_factor(exynos5250_fixed_factor_clks, + ARRAY_SIZE(exynos5250_fixed_factor_clks)); + samsung_clk_register_mux(exynos5250_mux_clks, + ARRAY_SIZE(exynos5250_mux_clks)); + samsung_clk_register_div(exynos5250_div_clks, + ARRAY_SIZE(exynos5250_div_clks)); + samsung_clk_register_gate(exynos5250_gate_clks, + ARRAY_SIZE(exynos5250_gate_clks)); + + pr_info("Exynos5250: clock setup completed, armclk=%ld\n", + _get_rate("armclk")); +} +CLK_OF_DECLARE(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init); diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c new file mode 100644 index 00000000000..7d5434167a9 --- /dev/null +++ b/drivers/clk/samsung/clk-exynos5440.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Thomas Abraham <thomas.ab@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for Exynos5440 SoC. +*/ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk.h" +#include "clk-pll.h" + +#define CLKEN_OV_VAL 0xf8 +#define CPU_CLK_STATUS 0xfc +#define MISC_DOUT1 0x558 + +/* + * Let each supported clock get a unique id. This id is used to lookup the clock + * for device tree based platforms. + */ +enum exynos5440_clks { + none, xtal, arm_clk, + + spi_baud = 16, pb0_250, pr0_250, pr1_250, b_250, b_125, b_200, sata, + usb, gmac0, cs250, pb0_250_o, pr0_250_o, pr1_250_o, b_250_o, b_125_o, + b_200_o, sata_o, usb_o, gmac0_o, cs250_o, + + nr_clks, +}; + +/* parent clock name list */ +PNAME(mout_armclk_p) = { "cplla", "cpllb" }; +PNAME(mout_spi_p) = { "div125", "div200" }; + +/* fixed rate clocks generated outside the soc */ +struct samsung_fixed_rate_clock exynos5440_fixed_rate_ext_clks[] __initdata = { + FRATE(none, "xtal", NULL, CLK_IS_ROOT, 0), +}; + +/* fixed rate clocks */ +struct samsung_fixed_rate_clock exynos5440_fixed_rate_clks[] __initdata = { + FRATE(none, "ppll", NULL, CLK_IS_ROOT, 1000000000), + FRATE(none, "usb_phy0", NULL, CLK_IS_ROOT, 60000000), + FRATE(none, "usb_phy1", NULL, CLK_IS_ROOT, 60000000), + FRATE(none, "usb_ohci12", NULL, CLK_IS_ROOT, 12000000), + FRATE(none, "usb_ohci48", NULL, CLK_IS_ROOT, 48000000), +}; + +/* fixed factor clocks */ +struct samsung_fixed_factor_clock exynos5440_fixed_factor_clks[] __initdata = { + FFACTOR(none, "div250", "ppll", 1, 4, 0), + FFACTOR(none, "div200", "ppll", 1, 5, 0), + FFACTOR(none, "div125", "div250", 1, 2, 0), +}; + +/* mux clocks */ +struct samsung_mux_clock exynos5440_mux_clks[] __initdata = { + MUX(none, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1), + MUX_A(arm_clk, "arm_clk", mout_armclk_p, + CPU_CLK_STATUS, 0, 1, "armclk"), +}; + +/* divider clocks */ +struct samsung_div_clock exynos5440_div_clks[] __initdata = { + DIV(spi_baud, "div_spi", "mout_spi", MISC_DOUT1, 3, 2), +}; + +/* gate clocks */ +struct samsung_gate_clock exynos5440_gate_clks[] __initdata = { + GATE(pb0_250, "pb0_250", "div250", CLKEN_OV_VAL, 3, 0, 0), + GATE(pr0_250, "pr0_250", "div250", CLKEN_OV_VAL, 4, 0, 0), + GATE(pr1_250, "pr1_250", "div250", CLKEN_OV_VAL, 5, 0, 0), + GATE(b_250, "b_250", "div250", CLKEN_OV_VAL, 9, 0, 0), + GATE(b_125, "b_125", "div125", CLKEN_OV_VAL, 10, 0, 0), + GATE(b_200, "b_200", "div200", CLKEN_OV_VAL, 11, 0, 0), + GATE(sata, "sata", "div200", CLKEN_OV_VAL, 12, 0, 0), + GATE(usb, "usb", "div200", CLKEN_OV_VAL, 13, 0, 0), + GATE(gmac0, "gmac0", "div200", CLKEN_OV_VAL, 14, 0, 0), + GATE(cs250, "cs250", "div250", CLKEN_OV_VAL, 19, 0, 0), + GATE(pb0_250_o, "pb0_250_o", "pb0_250", CLKEN_OV_VAL, 3, 0, 0), + GATE(pr0_250_o, "pr0_250_o", "pr0_250", CLKEN_OV_VAL, 4, 0, 0), + GATE(pr1_250_o, "pr1_250_o", "pr1_250", CLKEN_OV_VAL, 5, 0, 0), + GATE(b_250_o, "b_250_o", "b_250", CLKEN_OV_VAL, 9, 0, 0), + GATE(b_125_o, "b_125_o", "b_125", CLKEN_OV_VAL, 10, 0, 0), + GATE(b_200_o, "b_200_o", "b_200", CLKEN_OV_VAL, 11, 0, 0), + GATE(sata_o, "sata_o", "sata", CLKEN_OV_VAL, 12, 0, 0), + GATE(usb_o, "usb_o", "usb", CLKEN_OV_VAL, 13, 0, 0), + GATE(gmac0_o, "gmac0_o", "gmac", CLKEN_OV_VAL, 14, 0, 0), + GATE(cs250_o, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0), +}; + +static __initdata struct of_device_id ext_clk_match[] = { + { .compatible = "samsung,clock-xtal", .data = (void *)0, }, + {}, +}; + +/* register exynos5440 clocks */ +void __init exynos5440_clk_init(struct device_node *np) +{ + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: failed to map clock controller registers," + " aborting clock initialization\n", __func__); + return; + } + + samsung_clk_init(np, reg_base, nr_clks, NULL, 0, NULL, 0); + samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks, + ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match); + + samsung_clk_register_pll2550x("cplla", "xtal", reg_base + 0x1c, 0x10); + samsung_clk_register_pll2550x("cpllb", "xtal", reg_base + 0x20, 0x10); + + samsung_clk_register_fixed_rate(exynos5440_fixed_rate_clks, + ARRAY_SIZE(exynos5440_fixed_rate_clks)); + samsung_clk_register_fixed_factor(exynos5440_fixed_factor_clks, + ARRAY_SIZE(exynos5440_fixed_factor_clks)); + samsung_clk_register_mux(exynos5440_mux_clks, + ARRAY_SIZE(exynos5440_mux_clks)); + samsung_clk_register_div(exynos5440_div_clks, + ARRAY_SIZE(exynos5440_div_clks)); + samsung_clk_register_gate(exynos5440_gate_clks, + ARRAY_SIZE(exynos5440_gate_clks)); + + pr_info("Exynos5440: arm_clk = %ldHz\n", _get_rate("armclk")); + pr_info("exynos5440 clock initialization complete\n"); +} +CLK_OF_DECLARE(exynos5440_clk, "samsung,exynos5440-clock", exynos5440_clk_init); diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c new file mode 100644 index 00000000000..89135f6be11 --- /dev/null +++ b/drivers/clk/samsung/clk-pll.c @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * + * 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. + * + * This file contains the utility functions to register the pll clocks. +*/ + +#include <linux/errno.h> +#include "clk.h" +#include "clk-pll.h" + +/* + * PLL35xx Clock Type + */ + +#define PLL35XX_MDIV_MASK (0x3FF) +#define PLL35XX_PDIV_MASK (0x3F) +#define PLL35XX_SDIV_MASK (0x7) +#define PLL35XX_MDIV_SHIFT (16) +#define PLL35XX_PDIV_SHIFT (8) +#define PLL35XX_SDIV_SHIFT (0) + +struct samsung_clk_pll35xx { + struct clk_hw hw; + const void __iomem *con_reg; +}; + +#define to_clk_pll35xx(_hw) container_of(_hw, struct samsung_clk_pll35xx, hw) + +static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll35xx *pll = to_clk_pll35xx(hw); + u32 mdiv, pdiv, sdiv, pll_con; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; + sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK; + + fvco *= mdiv; + do_div(fvco, (pdiv << sdiv)); + + return (unsigned long)fvco; +} + +static const struct clk_ops samsung_pll35xx_clk_ops = { + .recalc_rate = samsung_pll35xx_recalc_rate, +}; + +struct clk * __init samsung_clk_register_pll35xx(const char *name, + const char *pname, const void __iomem *con_reg) +{ + struct samsung_clk_pll35xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll35xx_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->con_reg = con_reg; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} + +/* + * PLL36xx Clock Type + */ + +#define PLL36XX_KDIV_MASK (0xFFFF) +#define PLL36XX_MDIV_MASK (0x1FF) +#define PLL36XX_PDIV_MASK (0x3F) +#define PLL36XX_SDIV_MASK (0x7) +#define PLL36XX_MDIV_SHIFT (16) +#define PLL36XX_PDIV_SHIFT (8) +#define PLL36XX_SDIV_SHIFT (0) + +struct samsung_clk_pll36xx { + struct clk_hw hw; + const void __iomem *con_reg; +}; + +#define to_clk_pll36xx(_hw) container_of(_hw, struct samsung_clk_pll36xx, hw) + +static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll36xx *pll = to_clk_pll36xx(hw); + u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1; + u64 fvco = parent_rate; + + pll_con0 = __raw_readl(pll->con_reg); + pll_con1 = __raw_readl(pll->con_reg + 4); + mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK; + pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK; + sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK; + kdiv = pll_con1 & PLL36XX_KDIV_MASK; + + fvco *= (mdiv << 16) + kdiv; + do_div(fvco, (pdiv << sdiv)); + fvco >>= 16; + + return (unsigned long)fvco; +} + +static const struct clk_ops samsung_pll36xx_clk_ops = { + .recalc_rate = samsung_pll36xx_recalc_rate, +}; + +struct clk * __init samsung_clk_register_pll36xx(const char *name, + const char *pname, const void __iomem *con_reg) +{ + struct samsung_clk_pll36xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll36xx_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->con_reg = con_reg; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} + +/* + * PLL45xx Clock Type + */ + +#define PLL45XX_MDIV_MASK (0x3FF) +#define PLL45XX_PDIV_MASK (0x3F) +#define PLL45XX_SDIV_MASK (0x7) +#define PLL45XX_MDIV_SHIFT (16) +#define PLL45XX_PDIV_SHIFT (8) +#define PLL45XX_SDIV_SHIFT (0) + +struct samsung_clk_pll45xx { + struct clk_hw hw; + enum pll45xx_type type; + const void __iomem *con_reg; +}; + +#define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw) + +static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw); + u32 mdiv, pdiv, sdiv, pll_con; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK; + pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK; + sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK; + + if (pll->type == pll_4508) + sdiv = sdiv - 1; + + fvco *= mdiv; + do_div(fvco, (pdiv << sdiv)); + + return (unsigned long)fvco; +} + +static const struct clk_ops samsung_pll45xx_clk_ops = { + .recalc_rate = samsung_pll45xx_recalc_rate, +}; + +struct clk * __init samsung_clk_register_pll45xx(const char *name, + const char *pname, const void __iomem *con_reg, + enum pll45xx_type type) +{ + struct samsung_clk_pll45xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll45xx_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->con_reg = con_reg; + pll->type = type; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} + +/* + * PLL46xx Clock Type + */ + +#define PLL46XX_MDIV_MASK (0x1FF) +#define PLL46XX_PDIV_MASK (0x3F) +#define PLL46XX_SDIV_MASK (0x7) +#define PLL46XX_MDIV_SHIFT (16) +#define PLL46XX_PDIV_SHIFT (8) +#define PLL46XX_SDIV_SHIFT (0) + +#define PLL46XX_KDIV_MASK (0xFFFF) +#define PLL4650C_KDIV_MASK (0xFFF) +#define PLL46XX_KDIV_SHIFT (0) + +struct samsung_clk_pll46xx { + struct clk_hw hw; + enum pll46xx_type type; + const void __iomem *con_reg; +}; + +#define to_clk_pll46xx(_hw) container_of(_hw, struct samsung_clk_pll46xx, hw) + +static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw); + u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift; + u64 fvco = parent_rate; + + pll_con0 = __raw_readl(pll->con_reg); + pll_con1 = __raw_readl(pll->con_reg + 4); + mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK; + pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK; + sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK; + kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK : + pll_con1 & PLL46XX_KDIV_MASK; + + shift = pll->type == pll_4600 ? 16 : 10; + fvco *= (mdiv << shift) + kdiv; + do_div(fvco, (pdiv << sdiv)); + fvco >>= shift; + + return (unsigned long)fvco; +} + +static const struct clk_ops samsung_pll46xx_clk_ops = { + .recalc_rate = samsung_pll46xx_recalc_rate, +}; + +struct clk * __init samsung_clk_register_pll46xx(const char *name, + const char *pname, const void __iomem *con_reg, + enum pll46xx_type type) +{ + struct samsung_clk_pll46xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll46xx_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->con_reg = con_reg; + pll->type = type; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} + +/* + * PLL2550x Clock Type + */ + +#define PLL2550X_R_MASK (0x1) +#define PLL2550X_P_MASK (0x3F) +#define PLL2550X_M_MASK (0x3FF) +#define PLL2550X_S_MASK (0x7) +#define PLL2550X_R_SHIFT (20) +#define PLL2550X_P_SHIFT (14) +#define PLL2550X_M_SHIFT (4) +#define PLL2550X_S_SHIFT (0) + +struct samsung_clk_pll2550x { + struct clk_hw hw; + const void __iomem *reg_base; + unsigned long offset; +}; + +#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw) + +static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw); + u32 r, p, m, s, pll_stat; + u64 fvco = parent_rate; + + pll_stat = __raw_readl(pll->reg_base + pll->offset * 3); + r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK; + if (!r) + return 0; + p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK; + m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK; + s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK; + + fvco *= m; + do_div(fvco, (p << s)); + + return (unsigned long)fvco; +} + +static const struct clk_ops samsung_pll2550x_clk_ops = { + .recalc_rate = samsung_pll2550x_recalc_rate, +}; + +struct clk * __init samsung_clk_register_pll2550x(const char *name, + const char *pname, const void __iomem *reg_base, + const unsigned long offset) +{ + struct samsung_clk_pll2550x *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", __func__, name); + return NULL; + } + + init.name = name; + init.ops = &samsung_pll2550x_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + init.parent_names = &pname; + init.num_parents = 1; + + pll->hw.init = &init; + pll->reg_base = reg_base; + pll->offset = offset; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s\n", __func__, + name); + kfree(pll); + } + + if (clk_register_clkdev(clk, name, NULL)) + pr_err("%s: failed to register lookup for %s", __func__, name); + + return clk; +} diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h new file mode 100644 index 00000000000..f33786e9a78 --- /dev/null +++ b/drivers/clk/samsung/clk-pll.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * + * 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. + * + * Common Clock Framework support for all PLL's in Samsung platforms +*/ + +#ifndef __SAMSUNG_CLK_PLL_H +#define __SAMSUNG_CLK_PLL_H + +enum pll45xx_type { + pll_4500, + pll_4502, + pll_4508 +}; + +enum pll46xx_type { + pll_4600, + pll_4650, + pll_4650c, +}; + +extern struct clk * __init samsung_clk_register_pll35xx(const char *name, + const char *pname, const void __iomem *con_reg); +extern struct clk * __init samsung_clk_register_pll36xx(const char *name, + const char *pname, const void __iomem *con_reg); +extern struct clk * __init samsung_clk_register_pll45xx(const char *name, + const char *pname, const void __iomem *con_reg, + enum pll45xx_type type); +extern struct clk * __init samsung_clk_register_pll46xx(const char *name, + const char *pname, const void __iomem *con_reg, + enum pll46xx_type type); +extern struct clk * __init samsung_clk_register_pll2550x(const char *name, + const char *pname, const void __iomem *reg_base, + const unsigned long offset); + +#endif /* __SAMSUNG_CLK_PLL_H */ diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c new file mode 100644 index 00000000000..cd3c40ab50f --- /dev/null +++ b/drivers/clk/samsung/clk.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham <thomas.ab@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file includes utility functions to register clocks to common + * clock framework for Samsung platforms. +*/ + +#include <linux/syscore_ops.h> +#include "clk.h" + +static DEFINE_SPINLOCK(lock); +static struct clk **clk_table; +static void __iomem *reg_base; +#ifdef CONFIG_OF +static struct clk_onecell_data clk_data; +#endif + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *reg_dump; +static unsigned long nr_reg_dump; + +static int samsung_clk_suspend(void) +{ + struct samsung_clk_reg_dump *rd = reg_dump; + unsigned long i; + + for (i = 0; i < nr_reg_dump; i++, rd++) + rd->value = __raw_readl(reg_base + rd->offset); + + return 0; +} + +static void samsung_clk_resume(void) +{ + struct samsung_clk_reg_dump *rd = reg_dump; + unsigned long i; + + for (i = 0; i < nr_reg_dump; i++, rd++) + __raw_writel(rd->value, reg_base + rd->offset); +} + +static struct syscore_ops samsung_clk_syscore_ops = { + .suspend = samsung_clk_suspend, + .resume = samsung_clk_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +/* setup the essentials required to support clock lookup using ccf */ +void __init samsung_clk_init(struct device_node *np, void __iomem *base, + unsigned long nr_clks, unsigned long *rdump, + unsigned long nr_rdump, unsigned long *soc_rdump, + unsigned long nr_soc_rdump) +{ + reg_base = base; + +#ifdef CONFIG_PM_SLEEP + if (rdump && nr_rdump) { + unsigned int idx; + reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump) + * (nr_rdump + nr_soc_rdump), GFP_KERNEL); + if (!reg_dump) { + pr_err("%s: memory alloc for register dump failed\n", + __func__); + return; + } + + for (idx = 0; idx < nr_rdump; idx++) + reg_dump[idx].offset = rdump[idx]; + for (idx = 0; idx < nr_soc_rdump; idx++) + reg_dump[nr_rdump + idx].offset = soc_rdump[idx]; + nr_reg_dump = nr_rdump + nr_soc_rdump; + register_syscore_ops(&samsung_clk_syscore_ops); + } +#endif + + clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); + if (!clk_table) + panic("could not allocate clock lookup table\n"); + + if (!np) + return; + +#ifdef CONFIG_OF + clk_data.clks = clk_table; + clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +#endif +} + +/* add a clock instance to the clock lookup table used for dt based lookup */ +void samsung_clk_add_lookup(struct clk *clk, unsigned int id) +{ + if (clk_table && id) + clk_table[id] = clk; +} + +/* register a list of aliases */ +void __init samsung_clk_register_alias(struct samsung_clock_alias *list, + unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + if (!clk_table) { + pr_err("%s: clock table missing\n", __func__); + return; + } + + for (idx = 0; idx < nr_clk; idx++, list++) { + if (!list->id) { + pr_err("%s: clock id missing for index %d\n", __func__, + idx); + continue; + } + + clk = clk_table[list->id]; + if (!clk) { + pr_err("%s: failed to find clock %d\n", __func__, + list->id); + continue; + } + + ret = clk_register_clkdev(clk, list->alias, list->dev_name); + if (ret) + pr_err("%s: failed to register lookup %s\n", + __func__, list->alias); + } +} + +/* register a list of fixed clocks */ +void __init samsung_clk_register_fixed_rate( + struct samsung_fixed_rate_clock *list, unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_fixed_rate(NULL, list->name, + list->parent_name, list->flags, list->fixed_rate); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + samsung_clk_add_lookup(clk, list->id); + + /* + * Unconditionally add a clock lookup for the fixed rate clocks. + * There are not many of these on any of Samsung platforms. + */ + ret = clk_register_clkdev(clk, list->name, NULL); + if (ret) + pr_err("%s: failed to register clock lookup for %s", + __func__, list->name); + } +} + +/* register a list of fixed factor clocks */ +void __init samsung_clk_register_fixed_factor( + struct samsung_fixed_factor_clock *list, unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_fixed_factor(NULL, list->name, + list->parent_name, list->flags, list->mult, list->div); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + samsung_clk_add_lookup(clk, list->id); + } +} + +/* register a list of mux clocks */ +void __init samsung_clk_register_mux(struct samsung_mux_clock *list, + unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_mux(NULL, list->name, list->parent_names, + list->num_parents, list->flags, reg_base + list->offset, + list->shift, list->width, list->mux_flags, &lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + samsung_clk_add_lookup(clk, list->id); + + /* register a clock lookup only if a clock alias is specified */ + if (list->alias) { + ret = clk_register_clkdev(clk, list->alias, + list->dev_name); + if (ret) + pr_err("%s: failed to register lookup %s\n", + __func__, list->alias); + } + } +} + +/* register a list of div clocks */ +void __init samsung_clk_register_div(struct samsung_div_clock *list, + unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + for (idx = 0; idx < nr_clk; idx++, list++) { + if (list->table) + clk = clk_register_divider_table(NULL, list->name, + list->parent_name, list->flags, + reg_base + list->offset, list->shift, + list->width, list->div_flags, + list->table, &lock); + else + clk = clk_register_divider(NULL, list->name, + list->parent_name, list->flags, + reg_base + list->offset, list->shift, + list->width, list->div_flags, &lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + samsung_clk_add_lookup(clk, list->id); + + /* register a clock lookup only if a clock alias is specified */ + if (list->alias) { + ret = clk_register_clkdev(clk, list->alias, + list->dev_name); + if (ret) + pr_err("%s: failed to register lookup %s\n", + __func__, list->alias); + } + } +} + +/* register a list of gate clocks */ +void __init samsung_clk_register_gate(struct samsung_gate_clock *list, + unsigned int nr_clk) +{ + struct clk *clk; + unsigned int idx, ret; + + for (idx = 0; idx < nr_clk; idx++, list++) { + clk = clk_register_gate(NULL, list->name, list->parent_name, + list->flags, reg_base + list->offset, + list->bit_idx, list->gate_flags, &lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + list->name); + continue; + } + + /* register a clock lookup only if a clock alias is specified */ + if (list->alias) { + ret = clk_register_clkdev(clk, list->alias, + list->dev_name); + if (ret) + pr_err("%s: failed to register lookup %s\n", + __func__, list->alias); + } + + samsung_clk_add_lookup(clk, list->id); + } +} + +/* + * obtain the clock speed of all external fixed clock sources from device + * tree and register it + */ +#ifdef CONFIG_OF +void __init samsung_clk_of_register_fixed_ext( + struct samsung_fixed_rate_clock *fixed_rate_clk, + unsigned int nr_fixed_rate_clk, + struct of_device_id *clk_matches) +{ + const struct of_device_id *match; + struct device_node *np; + u32 freq; + + for_each_matching_node_and_match(np, clk_matches, &match) { + if (of_property_read_u32(np, "clock-frequency", &freq)) + continue; + fixed_rate_clk[(u32)match->data].fixed_rate = freq; + } + samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk); +} +#endif + +/* utility function to get the rate of a specified clock */ +unsigned long _get_rate(const char *clk_name) +{ + struct clk *clk; + unsigned long rate; + + clk = clk_get(NULL, clk_name); + if (IS_ERR(clk)) { + pr_err("%s: could not find clock %s\n", __func__, clk_name); + return 0; + } + rate = clk_get_rate(clk); + clk_put(clk); + return rate; +} diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h new file mode 100644 index 00000000000..e4ad6ea9aa7 --- /dev/null +++ b/drivers/clk/samsung/clk.h @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham <thomas.ab@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for all Samsung platforms +*/ + +#ifndef __SAMSUNG_CLK_H +#define __SAMSUNG_CLK_H + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> + +/** + * struct samsung_clock_alias: information about mux clock + * @id: platform specific id of the clock. + * @dev_name: name of the device to which this clock belongs. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_clock_alias { + unsigned int id; + const char *dev_name; + const char *alias; +}; + +#define ALIAS(_id, dname, a) \ + { \ + .id = _id, \ + .dev_name = dname, \ + .alias = a, \ + } + +/** + * struct samsung_fixed_rate_clock: information about fixed-rate clock + * @id: platform specific id of the clock. + * @name: name of this fixed-rate clock. + * @parent_name: optional parent clock name. + * @flags: optional fixed-rate clock flags. + * @fixed-rate: fixed clock rate of this clock. + */ +struct samsung_fixed_rate_clock { + unsigned int id; + char *name; + const char *parent_name; + unsigned long flags; + unsigned long fixed_rate; +}; + +#define FRATE(_id, cname, pname, f, frate) \ + { \ + .id = _id, \ + .name = cname, \ + .parent_name = pname, \ + .flags = f, \ + .fixed_rate = frate, \ + } + +/* + * struct samsung_fixed_factor_clock: information about fixed-factor clock + * @id: platform specific id of the clock. + * @name: name of this fixed-factor clock. + * @parent_name: parent clock name. + * @mult: fixed multiplication factor. + * @div: fixed division factor. + * @flags: optional fixed-factor clock flags. + */ +struct samsung_fixed_factor_clock { + unsigned int id; + char *name; + const char *parent_name; + unsigned long mult; + unsigned long div; + unsigned long flags; +}; + +#define FFACTOR(_id, cname, pname, m, d, f) \ + { \ + .id = _id, \ + .name = cname, \ + .parent_name = pname, \ + .mult = m, \ + .div = d, \ + .flags = f, \ + } + +/** + * struct samsung_mux_clock: information about mux clock + * @id: platform specific id of the clock. + * @dev_name: name of the device to which this clock belongs. + * @name: name of this mux clock. + * @parent_names: array of pointer to parent clock names. + * @num_parents: number of parents listed in @parent_names. + * @flags: optional flags for basic clock. + * @offset: offset of the register for configuring the mux. + * @shift: starting bit location of the mux control bit-field in @reg. + * @width: width of the mux control bit-field in @reg. + * @mux_flags: flags for mux-type clock. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_mux_clock { + unsigned int id; + const char *dev_name; + const char *name; + const char **parent_names; + u8 num_parents; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 mux_flags; + const char *alias; +}; + +#define __MUX(_id, dname, cname, pnames, o, s, w, f, mf, a) \ + { \ + .id = _id, \ + .dev_name = dname, \ + .name = cname, \ + .parent_names = pnames, \ + .num_parents = ARRAY_SIZE(pnames), \ + .flags = f, \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .mux_flags = mf, \ + .alias = a, \ + } + +#define MUX(_id, cname, pnames, o, s, w) \ + __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, NULL) + +#define MUX_A(_id, cname, pnames, o, s, w, a) \ + __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, a) + +#define MUX_F(_id, cname, pnames, o, s, w, f, mf) \ + __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, NULL) + +/** + * @id: platform specific id of the clock. + * struct samsung_div_clock: information about div clock + * @dev_name: name of the device to which this clock belongs. + * @name: name of this div clock. + * @parent_name: name of the parent clock. + * @flags: optional flags for basic clock. + * @offset: offset of the register for configuring the div. + * @shift: starting bit location of the div control bit-field in @reg. + * @div_flags: flags for div-type clock. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_div_clock { + unsigned int id; + const char *dev_name; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 div_flags; + const char *alias; + struct clk_div_table *table; +}; + +#define __DIV(_id, dname, cname, pname, o, s, w, f, df, a, t) \ + { \ + .id = _id, \ + .dev_name = dname, \ + .name = cname, \ + .parent_name = pname, \ + .flags = f, \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .div_flags = df, \ + .alias = a, \ + .table = t, \ + } + +#define DIV(_id, cname, pname, o, s, w) \ + __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, NULL) + +#define DIV_A(_id, cname, pname, o, s, w, a) \ + __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, a, NULL) + +#define DIV_F(_id, cname, pname, o, s, w, f, df) \ + __DIV(_id, NULL, cname, pname, o, s, w, f, df, NULL, NULL) + +#define DIV_T(_id, cname, pname, o, s, w, t) \ + __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, t) + +/** + * struct samsung_gate_clock: information about gate clock + * @id: platform specific id of the clock. + * @dev_name: name of the device to which this clock belongs. + * @name: name of this gate clock. + * @parent_name: name of the parent clock. + * @flags: optional flags for basic clock. + * @offset: offset of the register for configuring the gate. + * @bit_idx: bit index of the gate control bit-field in @reg. + * @gate_flags: flags for gate-type clock. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_gate_clock { + unsigned int id; + const char *dev_name; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 bit_idx; + u8 gate_flags; + const char *alias; +}; + +#define __GATE(_id, dname, cname, pname, o, b, f, gf, a) \ + { \ + .id = _id, \ + .dev_name = dname, \ + .name = cname, \ + .parent_name = pname, \ + .flags = f, \ + .offset = o, \ + .bit_idx = b, \ + .gate_flags = gf, \ + .alias = a, \ + } + +#define GATE(_id, cname, pname, o, b, f, gf) \ + __GATE(_id, NULL, cname, pname, o, b, f, gf, NULL) + +#define GATE_A(_id, cname, pname, o, b, f, gf, a) \ + __GATE(_id, NULL, cname, pname, o, b, f, gf, a) + +#define GATE_D(_id, dname, cname, pname, o, b, f, gf) \ + __GATE(_id, dname, cname, pname, o, b, f, gf, NULL) + +#define GATE_DA(_id, dname, cname, pname, o, b, f, gf, a) \ + __GATE(_id, dname, cname, pname, o, b, f, gf, a) + +#define PNAME(x) static const char *x[] __initdata + +/** + * struct samsung_clk_reg_dump: register dump of clock controller registers. + * @offset: clock register offset from the controller base address. + * @value: the value to be register at offset. + */ +struct samsung_clk_reg_dump { + u32 offset; + u32 value; +}; + +extern void __init samsung_clk_init(struct device_node *np, void __iomem *base, + unsigned long nr_clks, unsigned long *rdump, + unsigned long nr_rdump, unsigned long *soc_rdump, + unsigned long nr_soc_rdump); +extern void __init samsung_clk_of_register_fixed_ext( + struct samsung_fixed_rate_clock *fixed_rate_clk, + unsigned int nr_fixed_rate_clk, + struct of_device_id *clk_matches); + +extern void samsung_clk_add_lookup(struct clk *clk, unsigned int id); + +extern void samsung_clk_register_alias(struct samsung_clock_alias *list, + unsigned int nr_clk); +extern void __init samsung_clk_register_fixed_rate( + struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk); +extern void __init samsung_clk_register_fixed_factor( + struct samsung_fixed_factor_clock *list, unsigned int nr_clk); +extern void __init samsung_clk_register_mux(struct samsung_mux_clock *clk_list, + unsigned int nr_clk); +extern void __init samsung_clk_register_div(struct samsung_div_clock *clk_list, + unsigned int nr_clk); +extern void __init samsung_clk_register_gate( + struct samsung_gate_clock *clk_list, unsigned int nr_clk); + +extern unsigned long _get_rate(const char *clk_name); + +#endif /* __SAMSUNG_CLK_H */ diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 2c855a6394f..bd11315cf5a 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2012 Altera Corporation <www.altera.com> + * Copyright 2011-2012 Calxeda, Inc. + * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,41 +12,161 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * Based from clk-highbank.c + * * 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/clkdev.h> #include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> -#define SOCFPGA_OSC1_CLK 10000000 -#define SOCFPGA_MPU_CLK 800000000 -#define SOCFPGA_MAIN_QSPI_CLK 432000000 -#define SOCFPGA_MAIN_NAND_SDMMC_CLK 250000000 -#define SOCFPGA_S2F_USR_CLK 125000000 +/* Clock Manager offsets */ +#define CLKMGR_CTRL 0x0 +#define CLKMGR_BYPASS 0x4 -void __init socfpga_init_clocks(void) +/* Clock bypass bits */ +#define MAINPLL_BYPASS (1<<0) +#define SDRAMPLL_BYPASS (1<<1) +#define SDRAMPLL_SRC_BYPASS (1<<2) +#define PERPLL_BYPASS (1<<3) +#define PERPLL_SRC_BYPASS (1<<4) + +#define SOCFPGA_PLL_BG_PWRDWN 0 +#define SOCFPGA_PLL_EXT_ENA 1 +#define SOCFPGA_PLL_PWR_DOWN 2 +#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8 +#define SOCFPGA_PLL_DIVF_SHIFT 3 +#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 +#define SOCFPGA_PLL_DIVQ_SHIFT 16 + +extern void __iomem *clk_mgr_base_addr; + +struct socfpga_clk { + struct clk_gate hw; + char *parent_name; + char *clk_name; + u32 fixed_div; +}; +#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw) + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) { + struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); + unsigned long divf, divq, vco_freq, reg; + unsigned long bypass; + + reg = readl(socfpgaclk->hw.reg); + bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS); + if (bypass & MAINPLL_BYPASS) + return parent_rate; + + divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; + divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; + vco_freq = parent_rate * (divf + 1); + return vco_freq / (1 + divq); +} + + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, +}; + +static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); + u32 div; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else + div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1); + + return parent_rate / div; +} + +static const struct clk_ops periclk_ops = { + .recalc_rate = clk_periclk_recalc_rate, +}; + +static __init struct clk *socfpga_clk_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; struct clk *clk; + struct socfpga_clk *socfpga_clk; + const char *clk_name = node->name; + const char *parent_name; + struct clk_init_data init; + int rc; + u32 fixed_div; + + rc = of_property_read_u32(node, "reg", ®); + if (WARN_ON(rc)) + return NULL; + + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); + if (WARN_ON(!socfpga_clk)) + return NULL; + + socfpga_clk->hw.reg = clk_mgr_base_addr + reg; + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + socfpga_clk->fixed_div = 0; + else + socfpga_clk->fixed_div = fixed_div; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + init.num_parents = 1; - clk = clk_register_fixed_rate(NULL, "osc1_clk", NULL, CLK_IS_ROOT, SOCFPGA_OSC1_CLK); - clk_register_clkdev(clk, "osc1_clk", NULL); + socfpga_clk->hw.hw.init = &init; - clk = clk_register_fixed_rate(NULL, "mpu_clk", NULL, CLK_IS_ROOT, SOCFPGA_MPU_CLK); - clk_register_clkdev(clk, "mpu_clk", NULL); + if (strcmp(clk_name, "main_pll") || strcmp(clk_name, "periph_pll") || + strcmp(clk_name, "sdram_pll")) { + socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; + clk_pll_ops.enable = clk_gate_ops.enable; + clk_pll_ops.disable = clk_gate_ops.disable; + } - clk = clk_register_fixed_rate(NULL, "main_clk", NULL, CLK_IS_ROOT, SOCFPGA_MPU_CLK/2); - clk_register_clkdev(clk, "main_clk", NULL); + clk = clk_register(NULL, &socfpga_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(socfpga_clk); + return NULL; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + return clk; +} - clk = clk_register_fixed_rate(NULL, "dbg_base_clk", NULL, CLK_IS_ROOT, SOCFPGA_MPU_CLK/2); - clk_register_clkdev(clk, "dbg_base_clk", NULL); +static void __init socfpga_pll_init(struct device_node *node) +{ + socfpga_clk_init(node, &clk_pll_ops); +} +CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init); - clk = clk_register_fixed_rate(NULL, "main_qspi_clk", NULL, CLK_IS_ROOT, SOCFPGA_MAIN_QSPI_CLK); - clk_register_clkdev(clk, "main_qspi_clk", NULL); +static void __init socfpga_periph_init(struct device_node *node) +{ + socfpga_clk_init(node, &periclk_ops); +} +CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init); - clk = clk_register_fixed_rate(NULL, "main_nand_sdmmc_clk", NULL, CLK_IS_ROOT, SOCFPGA_MAIN_NAND_SDMMC_CLK); - clk_register_clkdev(clk, "main_nand_sdmmc_clk", NULL); +void __init socfpga_init_clocks(void) +{ + struct clk *clk; + int ret; - clk = clk_register_fixed_rate(NULL, "s2f_usr_clk", NULL, CLK_IS_ROOT, SOCFPGA_S2F_USR_CLK); - clk_register_clkdev(clk, "s2f_usr_clk", NULL); + clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4); + ret = clk_register_clkdev(clk, NULL, "smp_twd"); + if (ret) + pr_err("smp_twd alias not registered\n"); } diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c index ed9af427861..aedbbe12f32 100644 --- a/drivers/clk/spear/spear1310_clock.c +++ b/drivers/clk/spear/spear1310_clock.c @@ -17,12 +17,10 @@ #include <linux/io.h> #include <linux/of_platform.h> #include <linux/spinlock_types.h> -#include <mach/spear.h> #include "clk.h" -#define VA_SPEAR1310_RAS_BASE IOMEM(UL(0xFA400000)) /* PLL related registers and bit values */ -#define SPEAR1310_PLL_CFG (VA_MISC_BASE + 0x210) +#define SPEAR1310_PLL_CFG (misc_base + 0x210) /* PLL_CFG bit values */ #define SPEAR1310_CLCD_SYNT_CLK_MASK 1 #define SPEAR1310_CLCD_SYNT_CLK_SHIFT 31 @@ -35,15 +33,15 @@ #define SPEAR1310_PLL2_CLK_SHIFT 22 #define SPEAR1310_PLL1_CLK_SHIFT 20 -#define SPEAR1310_PLL1_CTR (VA_MISC_BASE + 0x214) -#define SPEAR1310_PLL1_FRQ (VA_MISC_BASE + 0x218) -#define SPEAR1310_PLL2_CTR (VA_MISC_BASE + 0x220) -#define SPEAR1310_PLL2_FRQ (VA_MISC_BASE + 0x224) -#define SPEAR1310_PLL3_CTR (VA_MISC_BASE + 0x22C) -#define SPEAR1310_PLL3_FRQ (VA_MISC_BASE + 0x230) -#define SPEAR1310_PLL4_CTR (VA_MISC_BASE + 0x238) -#define SPEAR1310_PLL4_FRQ (VA_MISC_BASE + 0x23C) -#define SPEAR1310_PERIP_CLK_CFG (VA_MISC_BASE + 0x244) +#define SPEAR1310_PLL1_CTR (misc_base + 0x214) +#define SPEAR1310_PLL1_FRQ (misc_base + 0x218) +#define SPEAR1310_PLL2_CTR (misc_base + 0x220) +#define SPEAR1310_PLL2_FRQ (misc_base + 0x224) +#define SPEAR1310_PLL3_CTR (misc_base + 0x22C) +#define SPEAR1310_PLL3_FRQ (misc_base + 0x230) +#define SPEAR1310_PLL4_CTR (misc_base + 0x238) +#define SPEAR1310_PLL4_FRQ (misc_base + 0x23C) +#define SPEAR1310_PERIP_CLK_CFG (misc_base + 0x244) /* PERIP_CLK_CFG bit values */ #define SPEAR1310_GPT_OSC24_VAL 0 #define SPEAR1310_GPT_APB_VAL 1 @@ -65,7 +63,7 @@ #define SPEAR1310_C3_CLK_MASK 1 #define SPEAR1310_C3_CLK_SHIFT 1 -#define SPEAR1310_GMAC_CLK_CFG (VA_MISC_BASE + 0x248) +#define SPEAR1310_GMAC_CLK_CFG (misc_base + 0x248) #define SPEAR1310_GMAC_PHY_IF_SEL_MASK 3 #define SPEAR1310_GMAC_PHY_IF_SEL_SHIFT 4 #define SPEAR1310_GMAC_PHY_CLK_MASK 1 @@ -73,7 +71,7 @@ #define SPEAR1310_GMAC_PHY_INPUT_CLK_MASK 2 #define SPEAR1310_GMAC_PHY_INPUT_CLK_SHIFT 1 -#define SPEAR1310_I2S_CLK_CFG (VA_MISC_BASE + 0x24C) +#define SPEAR1310_I2S_CLK_CFG (misc_base + 0x24C) /* I2S_CLK_CFG register mask */ #define SPEAR1310_I2S_SCLK_X_MASK 0x1F #define SPEAR1310_I2S_SCLK_X_SHIFT 27 @@ -91,21 +89,21 @@ #define SPEAR1310_I2S_SRC_CLK_MASK 2 #define SPEAR1310_I2S_SRC_CLK_SHIFT 0 -#define SPEAR1310_C3_CLK_SYNT (VA_MISC_BASE + 0x250) -#define SPEAR1310_UART_CLK_SYNT (VA_MISC_BASE + 0x254) -#define SPEAR1310_GMAC_CLK_SYNT (VA_MISC_BASE + 0x258) -#define SPEAR1310_SDHCI_CLK_SYNT (VA_MISC_BASE + 0x25C) -#define SPEAR1310_CFXD_CLK_SYNT (VA_MISC_BASE + 0x260) -#define SPEAR1310_ADC_CLK_SYNT (VA_MISC_BASE + 0x264) -#define SPEAR1310_AMBA_CLK_SYNT (VA_MISC_BASE + 0x268) -#define SPEAR1310_CLCD_CLK_SYNT (VA_MISC_BASE + 0x270) -#define SPEAR1310_RAS_CLK_SYNT0 (VA_MISC_BASE + 0x280) -#define SPEAR1310_RAS_CLK_SYNT1 (VA_MISC_BASE + 0x288) -#define SPEAR1310_RAS_CLK_SYNT2 (VA_MISC_BASE + 0x290) -#define SPEAR1310_RAS_CLK_SYNT3 (VA_MISC_BASE + 0x298) +#define SPEAR1310_C3_CLK_SYNT (misc_base + 0x250) +#define SPEAR1310_UART_CLK_SYNT (misc_base + 0x254) +#define SPEAR1310_GMAC_CLK_SYNT (misc_base + 0x258) +#define SPEAR1310_SDHCI_CLK_SYNT (misc_base + 0x25C) +#define SPEAR1310_CFXD_CLK_SYNT (misc_base + 0x260) +#define SPEAR1310_ADC_CLK_SYNT (misc_base + 0x264) +#define SPEAR1310_AMBA_CLK_SYNT (misc_base + 0x268) +#define SPEAR1310_CLCD_CLK_SYNT (misc_base + 0x270) +#define SPEAR1310_RAS_CLK_SYNT0 (misc_base + 0x280) +#define SPEAR1310_RAS_CLK_SYNT1 (misc_base + 0x288) +#define SPEAR1310_RAS_CLK_SYNT2 (misc_base + 0x290) +#define SPEAR1310_RAS_CLK_SYNT3 (misc_base + 0x298) /* Check Fractional synthesizer reg masks */ -#define SPEAR1310_PERIP1_CLK_ENB (VA_MISC_BASE + 0x300) +#define SPEAR1310_PERIP1_CLK_ENB (misc_base + 0x300) /* PERIP1_CLK_ENB register masks */ #define SPEAR1310_RTC_CLK_ENB 31 #define SPEAR1310_ADC_CLK_ENB 30 @@ -138,7 +136,7 @@ #define SPEAR1310_SYSROM_CLK_ENB 1 #define SPEAR1310_BUS_CLK_ENB 0 -#define SPEAR1310_PERIP2_CLK_ENB (VA_MISC_BASE + 0x304) +#define SPEAR1310_PERIP2_CLK_ENB (misc_base + 0x304) /* PERIP2_CLK_ENB register masks */ #define SPEAR1310_THSENS_CLK_ENB 8 #define SPEAR1310_I2S_REF_PAD_CLK_ENB 7 @@ -150,7 +148,7 @@ #define SPEAR1310_DDR_CORE_CLK_ENB 1 #define SPEAR1310_DDR_CTRL_CLK_ENB 0 -#define SPEAR1310_RAS_CLK_ENB (VA_MISC_BASE + 0x310) +#define SPEAR1310_RAS_CLK_ENB (misc_base + 0x310) /* RAS_CLK_ENB register masks */ #define SPEAR1310_SYNT3_CLK_ENB 17 #define SPEAR1310_SYNT2_CLK_ENB 16 @@ -172,7 +170,7 @@ #define SPEAR1310_ACLK_CLK_ENB 0 /* RAS Area Control Register */ -#define SPEAR1310_RAS_CTRL_REG0 (VA_SPEAR1310_RAS_BASE + 0x000) +#define SPEAR1310_RAS_CTRL_REG0 (ras_base + 0x000) #define SPEAR1310_SSP1_CLK_MASK 3 #define SPEAR1310_SSP1_CLK_SHIFT 26 #define SPEAR1310_TDM_CLK_MASK 1 @@ -197,12 +195,12 @@ #define SPEAR1310_PCI_CLK_MASK 1 #define SPEAR1310_PCI_CLK_SHIFT 0 -#define SPEAR1310_RAS_CTRL_REG1 (VA_SPEAR1310_RAS_BASE + 0x004) +#define SPEAR1310_RAS_CTRL_REG1 (ras_base + 0x004) #define SPEAR1310_PHY_CLK_MASK 0x3 #define SPEAR1310_RMII_PHY_CLK_SHIFT 0 #define SPEAR1310_SMII_RGMII_PHY_CLK_SHIFT 2 -#define SPEAR1310_RAS_SW_CLK_CTRL (VA_SPEAR1310_RAS_BASE + 0x0148) +#define SPEAR1310_RAS_SW_CLK_CTRL (ras_base + 0x0148) #define SPEAR1310_CAN1_CLK_ENB 25 #define SPEAR1310_CAN0_CLK_ENB 24 #define SPEAR1310_GPT64_CLK_ENB 23 @@ -385,7 +383,7 @@ static const char *ssp1_parents[] = { "ras_apb_clk", "gen_syn1_clk", static const char *pci_parents[] = { "ras_pll3_clk", "gen_syn2_clk", }; static const char *tdm_parents[] = { "ras_pll3_clk", "gen_syn1_clk", }; -void __init spear1310_clk_init(void) +void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) { struct clk *clk, *clk1; diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 82abea366b7..9d0b3949db3 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -17,18 +17,17 @@ #include <linux/io.h> #include <linux/of_platform.h> #include <linux/spinlock_types.h> -#include <mach/spear.h> #include "clk.h" /* Clock Configuration Registers */ -#define SPEAR1340_SYS_CLK_CTRL (VA_MISC_BASE + 0x200) +#define SPEAR1340_SYS_CLK_CTRL (misc_base + 0x200) #define SPEAR1340_HCLK_SRC_SEL_SHIFT 27 #define SPEAR1340_HCLK_SRC_SEL_MASK 1 #define SPEAR1340_SCLK_SRC_SEL_SHIFT 23 #define SPEAR1340_SCLK_SRC_SEL_MASK 3 /* PLL related registers and bit values */ -#define SPEAR1340_PLL_CFG (VA_MISC_BASE + 0x210) +#define SPEAR1340_PLL_CFG (misc_base + 0x210) /* PLL_CFG bit values */ #define SPEAR1340_CLCD_SYNT_CLK_MASK 1 #define SPEAR1340_CLCD_SYNT_CLK_SHIFT 31 @@ -40,15 +39,15 @@ #define SPEAR1340_PLL2_CLK_SHIFT 22 #define SPEAR1340_PLL1_CLK_SHIFT 20 -#define SPEAR1340_PLL1_CTR (VA_MISC_BASE + 0x214) -#define SPEAR1340_PLL1_FRQ (VA_MISC_BASE + 0x218) -#define SPEAR1340_PLL2_CTR (VA_MISC_BASE + 0x220) -#define SPEAR1340_PLL2_FRQ (VA_MISC_BASE + 0x224) -#define SPEAR1340_PLL3_CTR (VA_MISC_BASE + 0x22C) -#define SPEAR1340_PLL3_FRQ (VA_MISC_BASE + 0x230) -#define SPEAR1340_PLL4_CTR (VA_MISC_BASE + 0x238) -#define SPEAR1340_PLL4_FRQ (VA_MISC_BASE + 0x23C) -#define SPEAR1340_PERIP_CLK_CFG (VA_MISC_BASE + 0x244) +#define SPEAR1340_PLL1_CTR (misc_base + 0x214) +#define SPEAR1340_PLL1_FRQ (misc_base + 0x218) +#define SPEAR1340_PLL2_CTR (misc_base + 0x220) +#define SPEAR1340_PLL2_FRQ (misc_base + 0x224) +#define SPEAR1340_PLL3_CTR (misc_base + 0x22C) +#define SPEAR1340_PLL3_FRQ (misc_base + 0x230) +#define SPEAR1340_PLL4_CTR (misc_base + 0x238) +#define SPEAR1340_PLL4_FRQ (misc_base + 0x23C) +#define SPEAR1340_PERIP_CLK_CFG (misc_base + 0x244) /* PERIP_CLK_CFG bit values */ #define SPEAR1340_SPDIF_CLK_MASK 1 #define SPEAR1340_SPDIF_OUT_CLK_SHIFT 15 @@ -66,13 +65,13 @@ #define SPEAR1340_C3_CLK_MASK 1 #define SPEAR1340_C3_CLK_SHIFT 1 -#define SPEAR1340_GMAC_CLK_CFG (VA_MISC_BASE + 0x248) +#define SPEAR1340_GMAC_CLK_CFG (misc_base + 0x248) #define SPEAR1340_GMAC_PHY_CLK_MASK 1 #define SPEAR1340_GMAC_PHY_CLK_SHIFT 2 #define SPEAR1340_GMAC_PHY_INPUT_CLK_MASK 2 #define SPEAR1340_GMAC_PHY_INPUT_CLK_SHIFT 0 -#define SPEAR1340_I2S_CLK_CFG (VA_MISC_BASE + 0x24C) +#define SPEAR1340_I2S_CLK_CFG (misc_base + 0x24C) /* I2S_CLK_CFG register mask */ #define SPEAR1340_I2S_SCLK_X_MASK 0x1F #define SPEAR1340_I2S_SCLK_X_SHIFT 27 @@ -90,21 +89,21 @@ #define SPEAR1340_I2S_SRC_CLK_MASK 2 #define SPEAR1340_I2S_SRC_CLK_SHIFT 0 -#define SPEAR1340_C3_CLK_SYNT (VA_MISC_BASE + 0x250) -#define SPEAR1340_UART0_CLK_SYNT (VA_MISC_BASE + 0x254) -#define SPEAR1340_UART1_CLK_SYNT (VA_MISC_BASE + 0x258) -#define SPEAR1340_GMAC_CLK_SYNT (VA_MISC_BASE + 0x25C) -#define SPEAR1340_SDHCI_CLK_SYNT (VA_MISC_BASE + 0x260) -#define SPEAR1340_CFXD_CLK_SYNT (VA_MISC_BASE + 0x264) -#define SPEAR1340_ADC_CLK_SYNT (VA_MISC_BASE + 0x270) -#define SPEAR1340_AMBA_CLK_SYNT (VA_MISC_BASE + 0x274) -#define SPEAR1340_CLCD_CLK_SYNT (VA_MISC_BASE + 0x27C) -#define SPEAR1340_SYS_CLK_SYNT (VA_MISC_BASE + 0x284) -#define SPEAR1340_GEN_CLK_SYNT0 (VA_MISC_BASE + 0x28C) -#define SPEAR1340_GEN_CLK_SYNT1 (VA_MISC_BASE + 0x294) -#define SPEAR1340_GEN_CLK_SYNT2 (VA_MISC_BASE + 0x29C) -#define SPEAR1340_GEN_CLK_SYNT3 (VA_MISC_BASE + 0x304) -#define SPEAR1340_PERIP1_CLK_ENB (VA_MISC_BASE + 0x30C) +#define SPEAR1340_C3_CLK_SYNT (misc_base + 0x250) +#define SPEAR1340_UART0_CLK_SYNT (misc_base + 0x254) +#define SPEAR1340_UART1_CLK_SYNT (misc_base + 0x258) +#define SPEAR1340_GMAC_CLK_SYNT (misc_base + 0x25C) +#define SPEAR1340_SDHCI_CLK_SYNT (misc_base + 0x260) +#define SPEAR1340_CFXD_CLK_SYNT (misc_base + 0x264) +#define SPEAR1340_ADC_CLK_SYNT (misc_base + 0x270) +#define SPEAR1340_AMBA_CLK_SYNT (misc_base + 0x274) +#define SPEAR1340_CLCD_CLK_SYNT (misc_base + 0x27C) +#define SPEAR1340_SYS_CLK_SYNT (misc_base + 0x284) +#define SPEAR1340_GEN_CLK_SYNT0 (misc_base + 0x28C) +#define SPEAR1340_GEN_CLK_SYNT1 (misc_base + 0x294) +#define SPEAR1340_GEN_CLK_SYNT2 (misc_base + 0x29C) +#define SPEAR1340_GEN_CLK_SYNT3 (misc_base + 0x304) +#define SPEAR1340_PERIP1_CLK_ENB (misc_base + 0x30C) #define SPEAR1340_RTC_CLK_ENB 31 #define SPEAR1340_ADC_CLK_ENB 30 #define SPEAR1340_C3_CLK_ENB 29 @@ -133,7 +132,7 @@ #define SPEAR1340_SYSROM_CLK_ENB 1 #define SPEAR1340_BUS_CLK_ENB 0 -#define SPEAR1340_PERIP2_CLK_ENB (VA_MISC_BASE + 0x310) +#define SPEAR1340_PERIP2_CLK_ENB (misc_base + 0x310) #define SPEAR1340_THSENS_CLK_ENB 8 #define SPEAR1340_I2S_REF_PAD_CLK_ENB 7 #define SPEAR1340_ACP_CLK_ENB 6 @@ -144,7 +143,7 @@ #define SPEAR1340_DDR_CORE_CLK_ENB 1 #define SPEAR1340_DDR_CTRL_CLK_ENB 0 -#define SPEAR1340_PERIP3_CLK_ENB (VA_MISC_BASE + 0x314) +#define SPEAR1340_PERIP3_CLK_ENB (misc_base + 0x314) #define SPEAR1340_PLGPIO_CLK_ENB 18 #define SPEAR1340_VIDEO_DEC_CLK_ENB 16 #define SPEAR1340_VIDEO_ENC_CLK_ENB 15 @@ -441,7 +440,7 @@ static const char *gen_synth0_1_parents[] = { "vco1div4_clk", "vco3div2_clk", static const char *gen_synth2_3_parents[] = { "vco1div4_clk", "vco2div2_clk", "pll2_clk", }; -void __init spear1340_clk_init(void) +void __init spear1340_clk_init(void __iomem *misc_base) { struct clk *clk, *clk1; @@ -960,47 +959,47 @@ void __init spear1340_clk_init(void) SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0100000.spdif-in"); - clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0, + clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "acp_clk"); - clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0, + clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "e2800000.gpio"); - clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0, + clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "video_dec"); - clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0, + clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "video_enc"); - clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0, + clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "spear_vip"); - clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0, + clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0200000.cam0"); - clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0, + clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0300000.cam1"); - clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0, + clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0400000.cam2"); - clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0, + clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0500000.cam3"); diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c index 33d3ac588da..f9ec43fd132 100644 --- a/drivers/clk/spear/spear3xx_clock.c +++ b/drivers/clk/spear/spear3xx_clock.c @@ -15,21 +15,20 @@ #include <linux/io.h> #include <linux/of_platform.h> #include <linux/spinlock_types.h> -#include <mach/misc_regs.h> #include "clk.h" static DEFINE_SPINLOCK(_lock); -#define PLL1_CTR (MISC_BASE + 0x008) -#define PLL1_FRQ (MISC_BASE + 0x00C) -#define PLL2_CTR (MISC_BASE + 0x014) -#define PLL2_FRQ (MISC_BASE + 0x018) -#define PLL_CLK_CFG (MISC_BASE + 0x020) +#define PLL1_CTR (misc_base + 0x008) +#define PLL1_FRQ (misc_base + 0x00C) +#define PLL2_CTR (misc_base + 0x014) +#define PLL2_FRQ (misc_base + 0x018) +#define PLL_CLK_CFG (misc_base + 0x020) /* PLL_CLK_CFG register masks */ #define MCTR_CLK_SHIFT 28 #define MCTR_CLK_MASK 3 -#define CORE_CLK_CFG (MISC_BASE + 0x024) +#define CORE_CLK_CFG (misc_base + 0x024) /* CORE CLK CFG register masks */ #define GEN_SYNTH2_3_CLK_SHIFT 18 #define GEN_SYNTH2_3_CLK_MASK 1 @@ -39,7 +38,7 @@ static DEFINE_SPINLOCK(_lock); #define PCLK_RATIO_SHIFT 8 #define PCLK_RATIO_MASK 2 -#define PERIP_CLK_CFG (MISC_BASE + 0x028) +#define PERIP_CLK_CFG (misc_base + 0x028) /* PERIP_CLK_CFG register masks */ #define UART_CLK_SHIFT 4 #define UART_CLK_MASK 1 @@ -50,7 +49,7 @@ static DEFINE_SPINLOCK(_lock); #define GPT2_CLK_SHIFT 12 #define GPT_CLK_MASK 1 -#define PERIP1_CLK_ENB (MISC_BASE + 0x02C) +#define PERIP1_CLK_ENB (misc_base + 0x02C) /* PERIP1_CLK_ENB register masks */ #define UART_CLK_ENB 3 #define SSP_CLK_ENB 5 @@ -69,7 +68,7 @@ static DEFINE_SPINLOCK(_lock); #define USBH_CLK_ENB 25 #define C3_CLK_ENB 31 -#define RAS_CLK_ENB (MISC_BASE + 0x034) +#define RAS_CLK_ENB (misc_base + 0x034) #define RAS_AHB_CLK_ENB 0 #define RAS_PLL1_CLK_ENB 1 #define RAS_APB_CLK_ENB 2 @@ -82,20 +81,20 @@ static DEFINE_SPINLOCK(_lock); #define RAS_SYNT2_CLK_ENB 10 #define RAS_SYNT3_CLK_ENB 11 -#define PRSC0_CLK_CFG (MISC_BASE + 0x044) -#define PRSC1_CLK_CFG (MISC_BASE + 0x048) -#define PRSC2_CLK_CFG (MISC_BASE + 0x04C) -#define AMEM_CLK_CFG (MISC_BASE + 0x050) +#define PRSC0_CLK_CFG (misc_base + 0x044) +#define PRSC1_CLK_CFG (misc_base + 0x048) +#define PRSC2_CLK_CFG (misc_base + 0x04C) +#define AMEM_CLK_CFG (misc_base + 0x050) #define AMEM_CLK_ENB 0 -#define CLCD_CLK_SYNT (MISC_BASE + 0x05C) -#define FIRDA_CLK_SYNT (MISC_BASE + 0x060) -#define UART_CLK_SYNT (MISC_BASE + 0x064) -#define GMAC_CLK_SYNT (MISC_BASE + 0x068) -#define GEN0_CLK_SYNT (MISC_BASE + 0x06C) -#define GEN1_CLK_SYNT (MISC_BASE + 0x070) -#define GEN2_CLK_SYNT (MISC_BASE + 0x074) -#define GEN3_CLK_SYNT (MISC_BASE + 0x078) +#define CLCD_CLK_SYNT (misc_base + 0x05C) +#define FIRDA_CLK_SYNT (misc_base + 0x060) +#define UART_CLK_SYNT (misc_base + 0x064) +#define GMAC_CLK_SYNT (misc_base + 0x068) +#define GEN0_CLK_SYNT (misc_base + 0x06C) +#define GEN1_CLK_SYNT (misc_base + 0x070) +#define GEN2_CLK_SYNT (misc_base + 0x074) +#define GEN3_CLK_SYNT (misc_base + 0x078) /* pll rate configuration table, in ascending order of rates */ static struct pll_rate_tbl pll_rtbl[] = { @@ -211,6 +210,17 @@ static inline void spear310_clk_init(void) { } /* array of all spear 320 clock lookups */ #ifdef CONFIG_MACH_SPEAR320 + +#define SPEAR320_CONTROL_REG (soc_config_base + 0x0000) +#define SPEAR320_EXT_CTRL_REG (soc_config_base + 0x0018) + + #define SPEAR320_UARTX_PCLK_MASK 0x1 + #define SPEAR320_UART2_PCLK_SHIFT 8 + #define SPEAR320_UART3_PCLK_SHIFT 9 + #define SPEAR320_UART4_PCLK_SHIFT 10 + #define SPEAR320_UART5_PCLK_SHIFT 11 + #define SPEAR320_UART6_PCLK_SHIFT 12 + #define SPEAR320_RS485_PCLK_SHIFT 13 #define SMII_PCLK_SHIFT 18 #define SMII_PCLK_MASK 2 #define SMII_PCLK_VAL_PAD 0x0 @@ -235,7 +245,7 @@ static const char *smii0_parents[] = { "smii_125m_pad", "ras_pll2_clk", "ras_syn0_gclk", }; static const char *uartx_parents[] = { "ras_syn1_gclk", "ras_apb_clk", }; -static void __init spear320_clk_init(void) +static void __init spear320_clk_init(void __iomem *soc_config_base) { struct clk *clk; @@ -362,7 +372,7 @@ static void __init spear320_clk_init(void) static inline void spear320_clk_init(void) { } #endif -void __init spear3xx_clk_init(void) +void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_base) { struct clk *clk, *clk1; @@ -634,5 +644,5 @@ void __init spear3xx_clk_init(void) else if (of_machine_is_compatible("st,spear310")) spear310_clk_init(); else if (of_machine_is_compatible("st,spear320")) - spear320_clk_init(); + spear320_clk_init(soc_config_base); } diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c index e862a333ad3..9406f2426d6 100644 --- a/drivers/clk/spear/spear6xx_clock.c +++ b/drivers/clk/spear/spear6xx_clock.c @@ -13,28 +13,27 @@ #include <linux/clkdev.h> #include <linux/io.h> #include <linux/spinlock_types.h> -#include <mach/misc_regs.h> #include "clk.h" static DEFINE_SPINLOCK(_lock); -#define PLL1_CTR (MISC_BASE + 0x008) -#define PLL1_FRQ (MISC_BASE + 0x00C) -#define PLL2_CTR (MISC_BASE + 0x014) -#define PLL2_FRQ (MISC_BASE + 0x018) -#define PLL_CLK_CFG (MISC_BASE + 0x020) +#define PLL1_CTR (misc_base + 0x008) +#define PLL1_FRQ (misc_base + 0x00C) +#define PLL2_CTR (misc_base + 0x014) +#define PLL2_FRQ (misc_base + 0x018) +#define PLL_CLK_CFG (misc_base + 0x020) /* PLL_CLK_CFG register masks */ #define MCTR_CLK_SHIFT 28 #define MCTR_CLK_MASK 3 -#define CORE_CLK_CFG (MISC_BASE + 0x024) +#define CORE_CLK_CFG (misc_base + 0x024) /* CORE CLK CFG register masks */ #define HCLK_RATIO_SHIFT 10 #define HCLK_RATIO_MASK 2 #define PCLK_RATIO_SHIFT 8 #define PCLK_RATIO_MASK 2 -#define PERIP_CLK_CFG (MISC_BASE + 0x028) +#define PERIP_CLK_CFG (misc_base + 0x028) /* PERIP_CLK_CFG register masks */ #define CLCD_CLK_SHIFT 2 #define CLCD_CLK_MASK 2 @@ -48,7 +47,7 @@ static DEFINE_SPINLOCK(_lock); #define GPT3_CLK_SHIFT 12 #define GPT_CLK_MASK 1 -#define PERIP1_CLK_ENB (MISC_BASE + 0x02C) +#define PERIP1_CLK_ENB (misc_base + 0x02C) /* PERIP1_CLK_ENB register masks */ #define UART0_CLK_ENB 3 #define UART1_CLK_ENB 4 @@ -74,13 +73,13 @@ static DEFINE_SPINLOCK(_lock); #define USBH0_CLK_ENB 25 #define USBH1_CLK_ENB 26 -#define PRSC0_CLK_CFG (MISC_BASE + 0x044) -#define PRSC1_CLK_CFG (MISC_BASE + 0x048) -#define PRSC2_CLK_CFG (MISC_BASE + 0x04C) +#define PRSC0_CLK_CFG (misc_base + 0x044) +#define PRSC1_CLK_CFG (misc_base + 0x048) +#define PRSC2_CLK_CFG (misc_base + 0x04C) -#define CLCD_CLK_SYNT (MISC_BASE + 0x05C) -#define FIRDA_CLK_SYNT (MISC_BASE + 0x060) -#define UART_CLK_SYNT (MISC_BASE + 0x064) +#define CLCD_CLK_SYNT (misc_base + 0x05C) +#define FIRDA_CLK_SYNT (misc_base + 0x060) +#define UART_CLK_SYNT (misc_base + 0x064) /* vco rate configuration table, in ascending order of rates */ static struct pll_rate_tbl pll_rtbl[] = { @@ -115,7 +114,7 @@ static struct gpt_rate_tbl gpt_rtbl[] = { {.mscale = 1, .nscale = 0}, /* 83 MHz */ }; -void __init spear6xx_clk_init(void) +void __init spear6xx_clk_init(void __iomem *misc_base) { struct clk *clk, *clk1; diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile new file mode 100644 index 00000000000..b5bac917612 --- /dev/null +++ b/drivers/clk/sunxi/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c new file mode 100644 index 00000000000..88523f91d9b --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013 Emilio López <emilio@elopez.com.ar> + * + * 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. + * + * Adjustable factor-based clock implementation + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/string.h> + +#include <linux/delay.h> + +#include "clk-factors.h" + +/* + * DOC: basic adjustable factor-based clock that cannot gate + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. + * clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1) + * parent - fixed parent. No clk_set_parent support + */ + +struct clk_factors { + struct clk_hw hw; + void __iomem *reg; + struct clk_factors_config *config; + void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); + spinlock_t *lock; +}; + +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + +#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos)) +#define CLRMASK(len, pos) (~(SETMASK(len, pos))) +#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) + +#define FACTOR_SET(bit, len, reg, val) \ + (((reg) & CLRMASK(len, bit)) | (val << (bit))) + +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u8 n = 1, k = 0, p = 0, m = 0; + u32 reg; + unsigned long rate; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Get each individual factor if applicable */ + if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE) + n = FACTOR_GET(config->nshift, config->nwidth, reg); + if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE) + k = FACTOR_GET(config->kshift, config->kwidth, reg); + if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE) + m = FACTOR_GET(config->mshift, config->mwidth, reg); + if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE) + p = FACTOR_GET(config->pshift, config->pwidth, reg); + + /* Calculate the rate */ + rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + + return rate; +} + +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_factors *factors = to_clk_factors(hw); + factors->get_factors((u32 *)&rate, (u32)*parent_rate, + NULL, NULL, NULL, NULL); + + return rate; +} + +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u8 n, k, m, p; + u32 reg; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + unsigned long flags = 0; + + factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Set up the new factors - macros do not do anything if width is 0 */ + reg = FACTOR_SET(config->nshift, config->nwidth, reg, n); + reg = FACTOR_SET(config->kshift, config->kwidth, reg, k); + reg = FACTOR_SET(config->mshift, config->mwidth, reg, m); + reg = FACTOR_SET(config->pshift, config->pwidth, reg, p); + + /* Apply them now */ + writel(reg, factors->reg); + + /* delay 500us so pll stabilizes */ + __delay((rate >> 20) * 500 / 2); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); + + return 0; +} + +static const struct clk_ops clk_factors_ops = { + .recalc_rate = clk_factors_recalc_rate, + .round_rate = clk_factors_round_rate, + .set_rate = clk_factors_set_rate, +}; + +/** + * clk_register_factors - register a factors clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust factors + * @config: shift and width of factors n, k, m and p + * @get_factors: function to calculate the factors for a given frequency + * @lock: shared register lock for this clock + */ +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors)(u32 *rate, u32 parent, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock) +{ + struct clk_factors *factors; + struct clk *clk; + struct clk_init_data init; + + /* allocate the factors */ + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) { + pr_err("%s: could not allocate factors clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &clk_factors_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_factors assignments */ + factors->reg = reg; + factors->config = config; + factors->lock = lock; + factors->hw.init = &init; + factors->get_factors = get_factors; + + /* register the clock */ + clk = clk_register(dev, &factors->hw); + + if (IS_ERR(clk)) + kfree(factors); + + return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h new file mode 100644 index 00000000000..f49851cc438 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.h @@ -0,0 +1,27 @@ +#ifndef __MACH_SUNXI_CLK_FACTORS_H +#define __MACH_SUNXI_CLK_FACTORS_H + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> + +#define SUNXI_FACTORS_NOT_APPLICABLE (0) + +struct clk_factors_config { + u8 nshift; + u8 nwidth; + u8 kshift; + u8 kwidth; + u8 mshift; + u8 mwidth; + u8 pshift; + u8 pwidth; +}; + +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors) (u32 *rate, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock); +#endif diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c new file mode 100644 index 00000000000..8492ad1d536 --- /dev/null +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -0,0 +1,469 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López <emilio@elopez.com.ar> + * + * 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. + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/sunxi.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk-factors.h" + +static DEFINE_SPINLOCK(clk_lock); + +/** + * sunxi_osc_clk_setup() - Setup function for gatable oscillator + */ + +#define SUNXI_OSC24M_GATE 0 + +static void __init sunxi_osc_clk_setup(struct device_node *node) +{ + struct clk *clk; + struct clk_fixed_rate *fixed; + struct clk_gate *gate; + const char *clk_name = node->name; + u32 rate; + + /* allocate fixed-rate and gate clock structs */ + fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); + if (!fixed) + return; + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) { + kfree(fixed); + return; + } + + if (of_property_read_u32(node, "clock-frequency", &rate)) + return; + + /* set up gate and fixed rate properties */ + gate->reg = of_iomap(node, 0); + gate->bit_idx = SUNXI_OSC24M_GATE; + gate->lock = &clk_lock; + fixed->fixed_rate = rate; + + clk = clk_register_composite(NULL, clk_name, + NULL, 0, + NULL, NULL, + &fixed->hw, &clk_fixed_rate_ops, + &gate->hw, &clk_gate_ops, + CLK_IS_ROOT); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1 + * PLL1 rate is calculated as follows + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + * parent_rate is always 24Mhz + */ + +static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* Normalize value to a 6M multiple */ + div = *freq / 6000000; + *freq = 6000000 * div; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + /* m is always zero for pll1 */ + *m = 0; + + /* k is 1 only on these cases */ + if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) + *k = 1; + else + *k = 0; + + /* p will be 3 for divs under 10 */ + if (div < 10) + *p = 3; + + /* p will be 2 for divs between 10 - 20 and odd divs under 32 */ + else if (div < 20 || (div < 32 && (div & 1))) + *p = 2; + + /* p will be 1 for even divs under 32, divs under 40 and odd pairs + * of divs between 40-62 */ + else if (div < 40 || (div < 64 && (div & 2))) + *p = 1; + + /* any other entries have p = 0 */ + else + *p = 0; + + /* calculate a suitable n based on k and p */ + div <<= *p; + div /= (*k + 1); + *n = div / 4; +} + + + +/** + * sunxi_get_apb1_factors() - calculates m, p factors for APB1 + * APB1 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 calcm, calcp; + + if (parent_rate < *freq) + *freq = parent_rate; + + parent_rate = (parent_rate + (*freq - 1)) / *freq; + + /* Invalid rate! */ + if (parent_rate > 32) + return; + + if (parent_rate <= 4) + calcp = 0; + else if (parent_rate <= 8) + calcp = 1; + else if (parent_rate <= 16) + calcp = 2; + else + calcp = 3; + + calcm = (parent_rate >> calcp) - 1; + + *freq = (parent_rate >> calcp) / (calcm + 1); + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *m = calcm; + *p = calcp; +} + + + +/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +struct factors_data { + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); +}; + +static struct clk_factors_config pll1_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, + .mshift = 0, + .mwidth = 2, + .pshift = 16, + .pwidth = 2, +}; + +static struct clk_factors_config apb1_config = { + .mshift = 0, + .mwidth = 5, + .pshift = 16, + .pwidth = 2, +}; + +static const __initconst struct factors_data pll1_data = { + .table = &pll1_config, + .getter = sunxi_get_pll1_factors, +}; + +static const __initconst struct factors_data apb1_data = { + .table = &apb1_config, + .getter = sunxi_get_apb1_factors, +}; + +static void __init sunxi_factors_clk_setup(struct device_node *node, + struct factors_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_factors(NULL, clk_name, parent, 0, reg, + data->table, data->getter, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_mux_clk_setup() - Setup function for muxes + */ + +#define SUNXI_MUX_GATE_WIDTH 2 + +struct mux_data { + u8 shift; +}; + +static const __initconst struct mux_data cpu_data = { + .shift = 16, +}; + +static const __initconst struct mux_data apb1_mux_data = { + .shift = 24, +}; + +static void __init sunxi_mux_clk_setup(struct device_node *node, + struct mux_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parents[5]; + void *reg; + int i = 0; + + reg = of_iomap(node, 0); + + while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg, + data->shift, SUNXI_MUX_GATE_WIDTH, + 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_divider_clk_setup() - Setup function for simple divider clocks + */ + +#define SUNXI_DIVISOR_WIDTH 2 + +struct div_data { + u8 shift; + u8 pow; +}; + +static const __initconst struct div_data axi_data = { + .shift = 0, + .pow = 0, +}; + +static const __initconst struct div_data ahb_data = { + .shift = 4, + .pow = 1, +}; + +static const __initconst struct div_data apb0_data = { + .shift = 8, + .pow = 1, +}; + +static void __init sunxi_divider_clk_setup(struct device_node *node, + struct div_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *clk_parent; + void *reg; + + reg = of_iomap(node, 0); + + clk_parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, + reg, data->shift, SUNXI_DIVISOR_WIDTH, + data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, + &clk_lock); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks + */ + +#define SUNXI_GATES_MAX_SIZE 64 + +struct gates_data { + DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); +}; + +static const __initconst struct gates_data axi_gates_data = { + .mask = {1}, +}; + +static const __initconst struct gates_data ahb_gates_data = { + .mask = {0x7F77FFF, 0x14FB3F}, +}; + +static const __initconst struct gates_data apb0_gates_data = { + .mask = {0x4EF}, +}; + +static const __initconst struct gates_data apb1_gates_data = { + .mask = {0xFF00F7}, +}; + +static void __init sunxi_gates_clk_setup(struct device_node *node, + struct gates_data *data) +{ + struct clk_onecell_data *clk_data; + const char *clk_parent; + const char *clk_name; + void *reg; + int qty; + int i = 0; + int j = 0; + int ignore; + + reg = of_iomap(node, 0); + + clk_parent = of_clk_get_parent_name(node, 0); + + /* Worst-case size approximation and memory allocation */ + qty = find_last_bit(data->mask, SUNXI_GATES_MAX_SIZE); + clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!clk_data) + return; + clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL); + if (!clk_data->clks) { + kfree(clk_data); + return; + } + + for_each_set_bit(i, data->mask, SUNXI_GATES_MAX_SIZE) { + of_property_read_string_index(node, "clock-output-names", + j, &clk_name); + + /* No driver claims this clock, but it should remain gated */ + ignore = !strcmp("ahb_sdram", clk_name) ? CLK_IGNORE_UNUSED : 0; + + clk_data->clks[i] = clk_register_gate(NULL, clk_name, + clk_parent, ignore, + reg + 4 * (i/32), i % 32, + 0, &clk_lock); + WARN_ON(IS_ERR(clk_data->clks[i])); + + j++; + } + + /* Adjust to the real max */ + clk_data->clk_num = i; + + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +/* Matches for of_clk_init */ +static const __initconst struct of_device_id clk_match[] = { + {.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,}, + {} +}; + +/* Matches for factors clocks */ +static const __initconst struct of_device_id clk_factors_match[] = { + {.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,}, + {.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,}, + {} +}; + +/* Matches for divider clocks */ +static const __initconst struct of_device_id clk_div_match[] = { + {.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,}, + {.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,}, + {.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,}, + {} +}; + +/* Matches for mux clocks */ +static const __initconst struct of_device_id clk_mux_match[] = { + {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,}, + {} +}; + +/* Matches for gate clocks */ +static const __initconst struct of_device_id clk_gates_match[] = { + {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,}, + {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,}, + {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,}, + {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,}, + {} +}; + +static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, + void *function) +{ + struct device_node *np; + const struct div_data *data; + const struct of_device_id *match; + void (*setup_function)(struct device_node *, const void *) = function; + + for_each_matching_node(np, clk_match) { + match = of_match_node(clk_match, np); + data = match->data; + setup_function(np, data); + } +} + +void __init sunxi_init_clocks(void) +{ + /* Register all the simple sunxi clocks on DT */ + of_clk_init(clk_match); + + /* Register factor clocks */ + of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); + + /* Register divider clocks */ + of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); + + /* Register mux clocks */ + of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); + + /* Register gate clocks */ + of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup); +} diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 2b41b0f4f73..f49fac2d193 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -9,3 +9,4 @@ obj-y += clk-super.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index 6dd533251e7..bafee9895a2 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -41,7 +41,9 @@ static DEFINE_SPINLOCK(periph_ref_lock); #define write_rst_clr(val, gate) \ writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg)) -#define periph_clk_to_bit(periph) (1 << (gate->clk_num % 32)) +#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32)) + +#define LVL2_CLK_GATE_OVRE 0x554 /* Peripheral gate clock ops */ static int clk_periph_is_enabled(struct clk_hw *hw) @@ -83,6 +85,13 @@ static int clk_periph_enable(struct clk_hw *hw) } } + if (gate->flags & TEGRA_PERIPH_WAR_1005168) { + writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); + writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE); + udelay(1); + writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); + } + spin_unlock_irqrestore(&periph_ref_lock, flags); return 0; diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 788486e6331..b2309d37a96 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -16,6 +16,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/export.h> #include <linux/slab.h> #include <linux/err.h> @@ -128,6 +129,7 @@ void tegra_periph_reset_deassert(struct clk *c) tegra_periph_reset(gate, 0); } +EXPORT_SYMBOL(tegra_periph_reset_deassert); void tegra_periph_reset_assert(struct clk *c) { @@ -147,6 +149,7 @@ void tegra_periph_reset_assert(struct clk *c) tegra_periph_reset(gate, 1); } +EXPORT_SYMBOL(tegra_periph_reset_assert); const struct clk_ops tegra_clk_periph_ops = { .get_parent = clk_periph_get_parent, @@ -170,14 +173,15 @@ const struct clk_ops tegra_clk_periph_nodiv_ops = { static struct clk *_tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, - void __iomem *clk_base, u32 offset, bool div) + void __iomem *clk_base, u32 offset, bool div, + unsigned long flags) { struct clk *clk; struct clk_init_data init; init.name = name; init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops; - init.flags = div ? 0 : CLK_SET_RATE_PARENT; + init.flags = flags; init.parent_names = parent_names; init.num_parents = num_parents; @@ -202,10 +206,10 @@ static struct clk *_tegra_clk_register_periph(const char *name, struct clk *tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, - u32 offset) + u32 offset, unsigned long flags) { return _tegra_clk_register_periph(name, parent_names, num_parents, - periph, clk_base, offset, true); + periph, clk_base, offset, true, flags); } struct clk *tegra_clk_register_periph_nodiv(const char *name, @@ -214,5 +218,5 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name, u32 offset) { return _tegra_clk_register_periph(name, parent_names, num_parents, - periph, clk_base, offset, false); + periph, clk_base, offset, false, CLK_SET_RATE_PARENT); } diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 165f24734c1..17c2cc086eb 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -79,6 +79,48 @@ #define PLLE_SS_CTRL 0x68 #define PLLE_SS_DISABLE (7 << 10) +#define PLLE_AUX_PLLP_SEL BIT(2) +#define PLLE_AUX_ENABLE_SWCTL BIT(4) +#define PLLE_AUX_SEQ_ENABLE BIT(24) +#define PLLE_AUX_PLLRE_SEL BIT(28) + +#define PLLE_MISC_PLLE_PTS BIT(8) +#define PLLE_MISC_IDDQ_SW_VALUE BIT(13) +#define PLLE_MISC_IDDQ_SW_CTRL BIT(14) +#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4 +#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT) +#define PLLE_MISC_VREG_CTRL_SHIFT 2 +#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT) + +#define PLLCX_MISC_STROBE BIT(31) +#define PLLCX_MISC_RESET BIT(30) +#define PLLCX_MISC_SDM_DIV_SHIFT 28 +#define PLLCX_MISC_SDM_DIV_MASK (0x3 << PLLCX_MISC_SDM_DIV_SHIFT) +#define PLLCX_MISC_FILT_DIV_SHIFT 26 +#define PLLCX_MISC_FILT_DIV_MASK (0x3 << PLLCX_MISC_FILT_DIV_SHIFT) +#define PLLCX_MISC_ALPHA_SHIFT 18 +#define PLLCX_MISC_DIV_LOW_RANGE \ + ((0x1 << PLLCX_MISC_SDM_DIV_SHIFT) | \ + (0x1 << PLLCX_MISC_FILT_DIV_SHIFT)) +#define PLLCX_MISC_DIV_HIGH_RANGE \ + ((0x2 << PLLCX_MISC_SDM_DIV_SHIFT) | \ + (0x2 << PLLCX_MISC_FILT_DIV_SHIFT)) +#define PLLCX_MISC_COEF_LOW_RANGE \ + ((0x14 << PLLCX_MISC_KA_SHIFT) | (0x38 << PLLCX_MISC_KB_SHIFT)) +#define PLLCX_MISC_KA_SHIFT 2 +#define PLLCX_MISC_KB_SHIFT 9 +#define PLLCX_MISC_DEFAULT (PLLCX_MISC_COEF_LOW_RANGE | \ + (0x19 << PLLCX_MISC_ALPHA_SHIFT) | \ + PLLCX_MISC_DIV_LOW_RANGE | \ + PLLCX_MISC_RESET) +#define PLLCX_MISC1_DEFAULT 0x000d2308 +#define PLLCX_MISC2_DEFAULT 0x30211200 +#define PLLCX_MISC3_DEFAULT 0x200 + +#define PMC_PLLM_WB0_OVERRIDE 0x1dc +#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 +#define PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK BIT(27) + #define PMC_SATA_PWRGT 0x1ac #define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5) #define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4) @@ -101,6 +143,24 @@ #define divn_max(p) (divn_mask(p)) #define divp_max(p) (1 << (divp_mask(p))) + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +/* PLLXC has 4-bit PDIV, but entry 15 is not allowed in h/w */ +#define PLLXC_PDIV_MAX 14 + +/* non-monotonic mapping below is not a typo */ +static u8 pllxc_p[PLLXC_PDIV_MAX + 1] = { + /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ + /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32 +}; + +#define PLLCX_PDIV_MAX 7 +static u8 pllcx_p[PLLCX_PDIV_MAX + 1] = { + /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7 */ + /* p: */ 1, 2, 3, 4, 6, 8, 12, 16 +}; +#endif + static void clk_pll_enable_lock(struct tegra_clk_pll *pll) { u32 val; @@ -108,25 +168,36 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll) if (!(pll->flags & TEGRA_PLL_USE_LOCK)) return; + if (!(pll->flags & TEGRA_PLL_HAS_LOCK_ENABLE)) + return; + val = pll_readl_misc(pll); val |= BIT(pll->params->lock_enable_bit_idx); pll_writel_misc(val, pll); } -static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll, - void __iomem *lock_addr, u32 lock_bit_idx) +static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll) { int i; - u32 val; + u32 val, lock_mask; + void __iomem *lock_addr; if (!(pll->flags & TEGRA_PLL_USE_LOCK)) { udelay(pll->params->lock_delay); return 0; } + lock_addr = pll->clk_base; + if (pll->flags & TEGRA_PLL_LOCK_MISC) + lock_addr += pll->params->misc_reg; + else + lock_addr += pll->params->base_reg; + + lock_mask = pll->params->lock_mask; + for (i = 0; i < pll->params->lock_delay; i++) { val = readl_relaxed(lock_addr); - if (val & BIT(lock_bit_idx)) { + if ((val & lock_mask) == lock_mask) { udelay(PLL_POST_LOCK_DELAY); return 0; } @@ -155,7 +226,7 @@ static int clk_pll_is_enabled(struct clk_hw *hw) return val & PLL_BASE_ENABLE ? 1 : 0; } -static int _clk_pll_enable(struct clk_hw *hw) +static void _clk_pll_enable(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); u32 val; @@ -163,7 +234,8 @@ static int _clk_pll_enable(struct clk_hw *hw) clk_pll_enable_lock(pll); val = pll_readl_base(pll); - val &= ~PLL_BASE_BYPASS; + if (pll->flags & TEGRA_PLL_BYPASS) + val &= ~PLL_BASE_BYPASS; val |= PLL_BASE_ENABLE; pll_writel_base(val, pll); @@ -172,11 +244,6 @@ static int _clk_pll_enable(struct clk_hw *hw) val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE; writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE); } - - clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->base_reg, - pll->params->lock_bit_idx); - - return 0; } static void _clk_pll_disable(struct clk_hw *hw) @@ -185,7 +252,9 @@ static void _clk_pll_disable(struct clk_hw *hw) u32 val; val = pll_readl_base(pll); - val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); + if (pll->flags & TEGRA_PLL_BYPASS) + val &= ~PLL_BASE_BYPASS; + val &= ~PLL_BASE_ENABLE; pll_writel_base(val, pll); if (pll->flags & TEGRA_PLLM) { @@ -204,7 +273,9 @@ static int clk_pll_enable(struct clk_hw *hw) if (pll->lock) spin_lock_irqsave(pll->lock, flags); - ret = _clk_pll_enable(hw); + _clk_pll_enable(hw); + + ret = clk_pll_wait_for_lock(pll); if (pll->lock) spin_unlock_irqrestore(pll->lock, flags); @@ -241,8 +312,6 @@ static int _get_table_rate(struct clk_hw *hw, if (sel->input_rate == 0) return -EINVAL; - BUG_ON(sel->p < 1); - cfg->input_rate = sel->input_rate; cfg->output_rate = sel->output_rate; cfg->m = sel->m; @@ -257,6 +326,7 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; unsigned long cfreq; u32 p_div = 0; @@ -290,88 +360,119 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, cfg->output_rate <<= 1) p_div++; - cfg->p = 1 << p_div; cfg->m = parent_rate / cfreq; cfg->n = cfg->output_rate / cfreq; cfg->cpcon = OUT_OF_TABLE_CPCON; if (cfg->m > divm_max(pll) || cfg->n > divn_max(pll) || - cfg->p > divp_max(pll) || cfg->output_rate > pll->params->vco_max) { + (1 << p_div) > divp_max(pll) + || cfg->output_rate > pll->params->vco_max) { pr_err("%s: Failed to set %s rate %lu\n", __func__, __clk_get_name(hw->clk), rate); return -EINVAL; } + if (p_tohw) { + p_div = 1 << p_div; + while (p_tohw->pdiv) { + if (p_div <= p_tohw->pdiv) { + cfg->p = p_tohw->hw_val; + break; + } + p_tohw++; + } + if (!p_tohw->pdiv) + return -EINVAL; + } else + cfg->p = p_div; + return 0; } -static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, - unsigned long rate) +static void _update_pll_mnp(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg) { - struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned long flags = 0; - u32 divp, val, old_base; - int state; - - divp = __ffs(cfg->p); - - if (pll->flags & TEGRA_PLLU) - divp ^= 1; + u32 val; - if (pll->lock) - spin_lock_irqsave(pll->lock, flags); + val = pll_readl_base(pll); - old_base = val = pll_readl_base(pll); val &= ~((divm_mask(pll) << pll->divm_shift) | (divn_mask(pll) << pll->divn_shift) | (divp_mask(pll) << pll->divp_shift)); val |= ((cfg->m << pll->divm_shift) | (cfg->n << pll->divn_shift) | - (divp << pll->divp_shift)); - if (val == old_base) { - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); - return 0; + (cfg->p << pll->divp_shift)); + + pll_writel_base(val, pll); +} + +static void _get_pll_mnp(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg) +{ + u32 val; + + val = pll_readl_base(pll); + + cfg->m = (val >> pll->divm_shift) & (divm_mask(pll)); + cfg->n = (val >> pll->divn_shift) & (divn_mask(pll)); + cfg->p = (val >> pll->divp_shift) & (divp_mask(pll)); +} + +static void _update_pll_cpcon(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate) +{ + u32 val; + + val = pll_readl_misc(pll); + + val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); + val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; + + if (pll->flags & TEGRA_PLL_SET_LFCON) { + val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); + if (cfg->n >= PLLDU_LFCON_SET_DIVN) + val |= 1 << PLL_MISC_LFCON_SHIFT; + } else if (pll->flags & TEGRA_PLL_SET_DCCON) { + val &= ~(1 << PLL_MISC_DCCON_SHIFT); + if (rate >= (pll->params->vco_max >> 1)) + val |= 1 << PLL_MISC_DCCON_SHIFT; } + pll_writel_misc(val, pll); +} + +static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, + unsigned long rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + int state, ret = 0; + state = clk_pll_is_enabled(hw); - if (state) { + if (state) _clk_pll_disable(hw); - val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); - } - pll_writel_base(val, pll); - if (pll->flags & TEGRA_PLL_HAS_CPCON) { - val = pll_readl_misc(pll); - val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); - val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; - if (pll->flags & TEGRA_PLL_SET_LFCON) { - val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); - if (cfg->n >= PLLDU_LFCON_SET_DIVN) - val |= 0x1 << PLL_MISC_LFCON_SHIFT; - } else if (pll->flags & TEGRA_PLL_SET_DCCON) { - val &= ~(0x1 << PLL_MISC_DCCON_SHIFT); - if (rate >= (pll->params->vco_max >> 1)) - val |= 0x1 << PLL_MISC_DCCON_SHIFT; - } - pll_writel_misc(val, pll); - } + _update_pll_mnp(pll, cfg); - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); + if (pll->flags & TEGRA_PLL_HAS_CPCON) + _update_pll_cpcon(pll, cfg, rate); - if (state) - clk_pll_enable(hw); + if (state) { + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + } - return 0; + return ret; } static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll_freq_table cfg, old_cfg; + unsigned long flags = 0; + int ret = 0; if (pll->flags & TEGRA_PLL_FIXED) { if (rate != pll->fixed_rate) { @@ -387,7 +488,18 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, _calc_rate(hw, &cfg, rate, parent_rate)) return -EINVAL; - return _program_pll(hw, &cfg, rate); + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _get_pll_mnp(pll, &old_cfg); + + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p) + ret = _program_pll(hw, &cfg, rate); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; } static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -409,7 +521,7 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, return -EINVAL; output_rate *= cfg.n; - do_div(output_rate, cfg.m * cfg.p); + do_div(output_rate, cfg.m * (1 << cfg.p)); return output_rate; } @@ -418,11 +530,15 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - u32 val = pll_readl_base(pll); - u32 divn = 0, divm = 0, divp = 0; + struct tegra_clk_pll_freq_table cfg; + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + u32 val; u64 rate = parent_rate; + int pdiv; + + val = pll_readl_base(pll); - if (val & PLL_BASE_BYPASS) + if ((pll->flags & TEGRA_PLL_BYPASS) && (val & PLL_BASE_BYPASS)) return parent_rate; if ((pll->flags & TEGRA_PLL_FIXED) && !(val & PLL_BASE_OVERRIDE)) { @@ -435,16 +551,29 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, return pll->fixed_rate; } - divp = (val >> pll->divp_shift) & (divp_mask(pll)); - if (pll->flags & TEGRA_PLLU) - divp ^= 1; + _get_pll_mnp(pll, &cfg); - divn = (val >> pll->divn_shift) & (divn_mask(pll)); - divm = (val >> pll->divm_shift) & (divm_mask(pll)); - divm *= (1 << divp); + if (p_tohw) { + while (p_tohw->pdiv) { + if (cfg.p == p_tohw->hw_val) { + pdiv = p_tohw->pdiv; + break; + } + p_tohw++; + } + + if (!p_tohw->pdiv) { + WARN_ON(1); + pdiv = 1; + } + } else + pdiv = 1 << cfg.p; + + cfg.m *= pdiv; + + rate *= cfg.n; + do_div(rate, cfg.m); - rate *= divn; - do_div(rate, divm); return rate; } @@ -538,8 +667,8 @@ static int clk_plle_enable(struct clk_hw *hw) val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE); pll_writel_base(val, pll); - clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->misc_reg, - pll->params->lock_bit_idx); + clk_pll_wait_for_lock(pll); + return 0; } @@ -577,28 +706,531 @@ const struct clk_ops tegra_clk_plle_ops = { .enable = clk_plle_enable, }; -static struct clk *_tegra_clk_register_pll(const char *name, - const char *parent_name, void __iomem *clk_base, - void __iomem *pmc, unsigned long flags, - unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, - struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock, - const struct clk_ops *ops) +#ifdef CONFIG_ARCH_TEGRA_114_SOC + +static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params, + unsigned long parent_rate) +{ + if (parent_rate > pll_params->cf_max) + return 2; + else + return 1; +} + +static int clk_pll_iddq_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + + u32 val; + int ret; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + val = pll_readl(pll->params->iddq_reg, pll); + val &= ~BIT(pll->params->iddq_bit_idx); + pll_writel(val, pll->params->iddq_reg, pll); + udelay(2); + + _clk_pll_enable(hw); + + ret = clk_pll_wait_for_lock(pll); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return 0; +} + +static void clk_pll_iddq_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_disable(hw); + + val = pll_readl(pll->params->iddq_reg, pll); + val |= BIT(pll->params->iddq_bit_idx); + pll_writel(val, pll->params->iddq_reg, pll); + udelay(2); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +static int _calc_dynamic_ramp_rate(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned int p; + + if (!rate) + return -EINVAL; + + p = DIV_ROUND_UP(pll->params->vco_min, rate); + cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); + cfg->p = p; + cfg->output_rate = rate * cfg->p; + cfg->n = cfg->output_rate * cfg->m / parent_rate; + + if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) + return -EINVAL; + + return 0; +} + +static int _pll_ramp_calc_pll(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + int err = 0; + + err = _get_table_rate(hw, cfg, rate, parent_rate); + if (err < 0) + err = _calc_dynamic_ramp_rate(hw, cfg, rate, parent_rate); + else if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { + WARN_ON(1); + err = -EINVAL; + goto out; + } + + if (!cfg->p || (cfg->p > pll->params->max_p)) + err = -EINVAL; + +out: + return err; +} + +static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct tegra_clk_pll_freq_table cfg, old_cfg; + unsigned long flags = 0; + int ret = 0; + u8 old_p; + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + return ret; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _get_pll_mnp(pll, &old_cfg); + + old_p = pllxc_p[old_cfg.p]; + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_p != cfg.p) { + cfg.p -= 1; + ret = _program_pll(hw, &cfg, rate); + } + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_pll_freq_table cfg; + int ret = 0; + u64 output_rate = *prate; + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, *prate); + if (ret < 0) + return ret; + + output_rate *= cfg.n; + do_div(output_rate, cfg.m * cfg.p); + + return output_rate; +} + +static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + state = clk_pll_is_enabled(hw); + if (state) { + if (rate != clk_get_rate(hw->clk)) { + pr_err("%s: Cannot change active PLLM\n", __func__); + ret = -EINVAL; + goto out; + } + goto out; + } + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + goto out; + + cfg.p -= 1; + + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); + if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) { + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); + val = cfg.p ? (val | PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK) : + (val & ~PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK); + writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); + + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); + val &= ~(divn_mask(pll) | divm_mask(pll)); + val |= (cfg.m << pll->divm_shift) | (cfg.n << pll->divn_shift); + writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE); + } else + _update_pll_mnp(pll, &cfg); + + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void _pllcx_strobe(struct tegra_clk_pll *pll) +{ + u32 val; + + val = pll_readl_misc(pll); + val |= PLLCX_MISC_STROBE; + pll_writel_misc(val, pll); + udelay(2); + + val &= ~PLLCX_MISC_STROBE; + pll_writel_misc(val, pll); +} + +static int clk_pllc_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + int ret = 0; + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_enable(hw); + udelay(2); + + val = pll_readl_misc(pll); + val &= ~PLLCX_MISC_RESET; + pll_writel_misc(val, pll); + udelay(2); + + _pllcx_strobe(pll); + + ret = clk_pll_wait_for_lock(pll); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void _clk_pllc_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + + _clk_pll_disable(hw); + + val = pll_readl_misc(pll); + val |= PLLCX_MISC_RESET; + pll_writel_misc(val, pll); + udelay(2); +} + +static void clk_pllc_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pllc_disable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll, + unsigned long input_rate, u32 n) +{ + u32 val, n_threshold; + + switch (input_rate) { + case 12000000: + n_threshold = 70; + break; + case 13000000: + case 26000000: + n_threshold = 71; + break; + case 16800000: + n_threshold = 55; + break; + case 19200000: + n_threshold = 48; + break; + default: + pr_err("%s: Unexpected reference rate %lu\n", + __func__, input_rate); + return -EINVAL; + } + + val = pll_readl_misc(pll); + val &= ~(PLLCX_MISC_SDM_DIV_MASK | PLLCX_MISC_FILT_DIV_MASK); + val |= n <= n_threshold ? + PLLCX_MISC_DIV_LOW_RANGE : PLLCX_MISC_DIV_HIGH_RANGE; + pll_writel_misc(val, pll); + + return 0; +} + +static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + u32 val; + u16 old_m, old_n; + u8 old_p; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + goto out; + + val = pll_readl_base(pll); + old_m = (val >> pll->divm_shift) & (divm_mask(pll)); + old_n = (val >> pll->divn_shift) & (divn_mask(pll)); + old_p = pllcx_p[(val >> pll->divp_shift) & (divp_mask(pll))]; + + if (cfg.m != old_m) { + WARN_ON(1); + goto out; + } + + if (old_n == cfg.n && old_p == cfg.p) + goto out; + + cfg.p -= 1; + + state = clk_pll_is_enabled(hw); + if (state) + _clk_pllc_disable(hw); + + ret = _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n); + if (ret < 0) + goto out; + + _update_pll_mnp(pll, &cfg); + + if (state) + ret = clk_pllc_enable(hw); + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static long _pllre_calc_rate(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + u16 m, n; + u64 output_rate = parent_rate; + + m = _pll_fixed_mdiv(pll->params, parent_rate); + n = rate * m / parent_rate; + + output_rate *= n; + do_div(output_rate, m); + + if (cfg) { + cfg->m = m; + cfg->n = n; + } + + return output_rate; +} +static int clk_pllre_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg, old_cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _pllre_calc_rate(pll, &cfg, rate, parent_rate); + _get_pll_mnp(pll, &old_cfg); + cfg.p = old_cfg.p; + + if (cfg.m != old_cfg.m || cfg.n != old_cfg.n) { + state = clk_pll_is_enabled(hw); + if (state) + _clk_pll_disable(hw); + + _update_pll_mnp(pll, &cfg); + + if (state) { + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + } + } + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static unsigned long clk_pllre_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + u64 rate = parent_rate; + + _get_pll_mnp(pll, &cfg); + + rate *= cfg.n; + do_div(rate, cfg.m); + + return rate; +} + +static long clk_pllre_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + + return _pllre_calc_rate(pll, NULL, rate, *prate); +} + +static int clk_plle_tegra114_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct tegra_clk_pll_freq_table sel; + u32 val; + int ret; + unsigned long flags = 0; + unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk)); + + if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate)) + return -EINVAL; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + val = pll_readl_base(pll); + val &= ~BIT(29); /* Disable lock override */ + pll_writel_base(val, pll); + + val = pll_readl(pll->params->aux_reg, pll); + val |= PLLE_AUX_ENABLE_SWCTL; + val &= ~PLLE_AUX_SEQ_ENABLE; + pll_writel(val, pll->params->aux_reg, pll); + udelay(1); + + val = pll_readl_misc(pll); + val |= PLLE_MISC_LOCK_ENABLE; + val |= PLLE_MISC_IDDQ_SW_CTRL; + val &= ~PLLE_MISC_IDDQ_SW_VALUE; + val |= PLLE_MISC_PLLE_PTS; + val |= PLLE_MISC_VREG_BG_CTRL_MASK | PLLE_MISC_VREG_CTRL_MASK; + pll_writel_misc(val, pll); + udelay(5); + + val = pll_readl(PLLE_SS_CTRL, pll); + val |= PLLE_SS_DISABLE; + pll_writel(val, PLLE_SS_CTRL, pll); + + val = pll_readl_base(pll); + val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll)); + val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT); + val |= sel.m << pll->divm_shift; + val |= sel.n << pll->divn_shift; + val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; + pll_writel_base(val, pll); + udelay(1); + + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + + if (ret < 0) + goto out; + + /* TODO: enable hw control of xusb brick pll */ + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void clk_plle_tegra114_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_disable(hw); + + val = pll_readl_misc(pll); + val |= PLLE_MISC_IDDQ_SW_CTRL | PLLE_MISC_IDDQ_SW_VALUE; + pll_writel_misc(val, pll); + udelay(1); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} +#endif + +static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, + void __iomem *pmc, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) { struct tegra_clk_pll *pll; - struct clk *clk; - struct clk_init_data init; pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) return ERR_PTR(-ENOMEM); - init.name = name; - init.ops = ops; - init.flags = flags; - init.parent_names = (parent_name ? &parent_name : NULL); - init.num_parents = (parent_name ? 1 : 0); - pll->clk_base = clk_base; pll->pmc = pmc; @@ -615,34 +1247,336 @@ static struct clk *_tegra_clk_register_pll(const char *name, pll->divm_shift = PLL_BASE_DIVM_SHIFT; pll->divm_width = PLL_BASE_DIVM_WIDTH; + return pll; +} + +static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll, + const char *name, const char *parent_name, unsigned long flags, + const struct clk_ops *ops) +{ + struct clk_init_data init; + + init.name = name; + init.ops = ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + /* Data in .init is copied by clk_register(), so stack variable OK */ pll->hw.init = &init; - clk = clk_register(NULL, &pll->hw); - if (IS_ERR(clk)) - kfree(pll); - - return clk; + return clk_register(NULL, &pll->hw); } struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) { - return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, - flags, fixed_rate, pll_params, pll_flags, freq_table, - lock, &tegra_clk_pll_ops); + struct tegra_clk_pll *pll; + struct clk *clk; + + pll_flags |= TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pll_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; } struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) { - return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, - flags, fixed_rate, pll_params, pll_flags, freq_table, - lock, &tegra_clk_plle_ops); + struct tegra_clk_pll *pll; + struct clk *clk; + + pll_flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_plle_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +const struct clk_ops tegra_clk_pllxc_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllxc_set_rate, +}; + +const struct clk_ops tegra_clk_pllm_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllm_set_rate, +}; + +const struct clk_ops tegra_clk_pllc_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pllc_enable, + .disable = clk_pllc_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllc_set_rate, +}; + +const struct clk_ops tegra_clk_pllre_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pllre_recalc_rate, + .round_rate = clk_pllre_round_rate, + .set_rate = clk_pllre_set_rate, +}; + +const struct clk_ops tegra_clk_plle_tegra114_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_plle_tegra114_enable, + .disable = clk_plle_tegra114_disable, + .recalc_rate = clk_pll_recalc_rate, +}; + + +struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + + if (!pll_params->pdiv_tohw) + return ERR_PTR(-EINVAL); + + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllxc_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock, unsigned long parent_rate) +{ + u32 val; + struct tegra_clk_pll *pll; + struct clk *clk; + + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + /* program minimum rate by default */ + + val = pll_readl_base(pll); + if (val & PLL_BASE_ENABLE) + WARN_ON(val & pll_params->iddq_bit_idx); + else { + int m; + + m = _pll_fixed_mdiv(pll_params, parent_rate); + val = m << PLL_BASE_DIVM_SHIFT; + val |= (pll_params->vco_min / parent_rate) + << PLL_BASE_DIVN_SHIFT; + pll_writel_base(val, pll); + } + + /* disable lock override */ + + val = pll_readl_misc(pll); + val &= ~BIT(29); + pll_writel_misc(val, pll); + + pll_flags |= TEGRA_PLL_LOCK_MISC; + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllre_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + + if (!pll_params->pdiv_tohw) + return ERR_PTR(-EINVAL); + + pll_flags |= TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllm_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct clk *parent, *clk; + struct pdiv_map *p_tohw = pll_params->pdiv_tohw; + struct tegra_clk_pll *pll; + struct tegra_clk_pll_freq_table cfg; + unsigned long parent_rate; + + if (!p_tohw) + return ERR_PTR(-EINVAL); + + parent = __clk_lookup(parent_name); + if (IS_ERR(parent)) { + WARN(1, "parent clk %s of %s must be registered first\n", + name, parent_name); + return ERR_PTR(-EINVAL); + } + + pll_flags |= TEGRA_PLL_BYPASS; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + parent_rate = __clk_get_rate(parent); + + /* + * Most of PLLC register fields are shadowed, and can not be read + * directly from PLL h/w. Hence, actual PLLC boot state is unknown. + * Initialize PLL to default state: disabled, reset; shadow registers + * loaded with default parameters; dividers are preset for half of + * minimum VCO rate (the latter assured that shadowed divider settings + * are within supported range). + */ + + cfg.m = _pll_fixed_mdiv(pll_params, parent_rate); + cfg.n = cfg.m * pll_params->vco_min / parent_rate; + + while (p_tohw->pdiv) { + if (p_tohw->pdiv == 2) { + cfg.p = p_tohw->hw_val; + break; + } + p_tohw++; + } + + if (!p_tohw->pdiv) { + WARN_ON(1); + return ERR_PTR(-EINVAL); + } + + pll_writel_base(0, pll); + _update_pll_mnp(pll, &cfg); + + pll_writel_misc(PLLCX_MISC_DEFAULT, pll); + pll_writel(PLLCX_MISC1_DEFAULT, pll_params->ext_misc_reg[0], pll); + pll_writel(PLLCX_MISC2_DEFAULT, pll_params->ext_misc_reg[1], pll); + pll_writel(PLLCX_MISC3_DEFAULT, pll_params->ext_misc_reg[2], pll); + + _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllc_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_plle_tegra114(const char *name, + const char *parent_name, + void __iomem *clk_base, unsigned long flags, + unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + u32 val, val_aux; + + pll = _tegra_init_pll(clk_base, NULL, fixed_rate, pll_params, + TEGRA_PLL_HAS_LOCK_ENABLE, freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + /* ensure parent is set to pll_re_vco */ + + val = pll_readl_base(pll); + val_aux = pll_readl(pll_params->aux_reg, pll); + + if (val & PLL_BASE_ENABLE) { + if (!(val_aux & PLLE_AUX_PLLRE_SEL)) + WARN(1, "pll_e enabled with unsupported parent %s\n", + (val & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : "pll_ref"); + } else { + val_aux |= PLLE_AUX_PLLRE_SEL; + pll_writel(val, pll_params->aux_reg, pll); + } + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_plle_tegra114_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; } +#endif diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c new file mode 100644 index 00000000000..d78e16ee161 --- /dev/null +++ b/drivers/clk/tegra/clk-tegra114.c @@ -0,0 +1,2085 @@ +/* + * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. + * + * 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/io.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/delay.h> +#include <linux/clk/tegra.h> + +#include "clk.h" + +#define RST_DEVICES_L 0x004 +#define RST_DEVICES_H 0x008 +#define RST_DEVICES_U 0x00C +#define RST_DEVICES_V 0x358 +#define RST_DEVICES_W 0x35C +#define RST_DEVICES_X 0x28C +#define RST_DEVICES_SET_L 0x300 +#define RST_DEVICES_CLR_L 0x304 +#define RST_DEVICES_SET_H 0x308 +#define RST_DEVICES_CLR_H 0x30c +#define RST_DEVICES_SET_U 0x310 +#define RST_DEVICES_CLR_U 0x314 +#define RST_DEVICES_SET_V 0x430 +#define RST_DEVICES_CLR_V 0x434 +#define RST_DEVICES_SET_W 0x438 +#define RST_DEVICES_CLR_W 0x43c +#define RST_DEVICES_NUM 5 + +#define CLK_OUT_ENB_L 0x010 +#define CLK_OUT_ENB_H 0x014 +#define CLK_OUT_ENB_U 0x018 +#define CLK_OUT_ENB_V 0x360 +#define CLK_OUT_ENB_W 0x364 +#define CLK_OUT_ENB_X 0x280 +#define CLK_OUT_ENB_SET_L 0x320 +#define CLK_OUT_ENB_CLR_L 0x324 +#define CLK_OUT_ENB_SET_H 0x328 +#define CLK_OUT_ENB_CLR_H 0x32c +#define CLK_OUT_ENB_SET_U 0x330 +#define CLK_OUT_ENB_CLR_U 0x334 +#define CLK_OUT_ENB_SET_V 0x440 +#define CLK_OUT_ENB_CLR_V 0x444 +#define CLK_OUT_ENB_SET_W 0x448 +#define CLK_OUT_ENB_CLR_W 0x44c +#define CLK_OUT_ENB_SET_X 0x284 +#define CLK_OUT_ENB_CLR_X 0x288 +#define CLK_OUT_ENB_NUM 6 + +#define PLLC_BASE 0x80 +#define PLLC_MISC2 0x88 +#define PLLC_MISC 0x8c +#define PLLC2_BASE 0x4e8 +#define PLLC2_MISC 0x4ec +#define PLLC3_BASE 0x4fc +#define PLLC3_MISC 0x500 +#define PLLM_BASE 0x90 +#define PLLM_MISC 0x9c +#define PLLP_BASE 0xa0 +#define PLLP_MISC 0xac +#define PLLX_BASE 0xe0 +#define PLLX_MISC 0xe4 +#define PLLX_MISC2 0x514 +#define PLLX_MISC3 0x518 +#define PLLD_BASE 0xd0 +#define PLLD_MISC 0xdc +#define PLLD2_BASE 0x4b8 +#define PLLD2_MISC 0x4bc +#define PLLE_BASE 0xe8 +#define PLLE_MISC 0xec +#define PLLA_BASE 0xb0 +#define PLLA_MISC 0xbc +#define PLLU_BASE 0xc0 +#define PLLU_MISC 0xcc +#define PLLRE_BASE 0x4c4 +#define PLLRE_MISC 0x4c8 + +#define PLL_MISC_LOCK_ENABLE 18 +#define PLLC_MISC_LOCK_ENABLE 24 +#define PLLDU_MISC_LOCK_ENABLE 22 +#define PLLE_MISC_LOCK_ENABLE 9 +#define PLLRE_MISC_LOCK_ENABLE 30 + +#define PLLC_IDDQ_BIT 26 +#define PLLX_IDDQ_BIT 3 +#define PLLRE_IDDQ_BIT 16 + +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) +#define PLLRE_MISC_LOCK BIT(24) +#define PLLCX_BASE_LOCK (BIT(26)|BIT(27)) + +#define PLLE_AUX 0x48c +#define PLLC_OUT 0x84 +#define PLLM_OUT 0x94 +#define PLLP_OUTA 0xa4 +#define PLLP_OUTB 0xa8 +#define PLLA_OUT 0xb4 + +#define AUDIO_SYNC_CLK_I2S0 0x4a0 +#define AUDIO_SYNC_CLK_I2S1 0x4a4 +#define AUDIO_SYNC_CLK_I2S2 0x4a8 +#define AUDIO_SYNC_CLK_I2S3 0x4ac +#define AUDIO_SYNC_CLK_I2S4 0x4b0 +#define AUDIO_SYNC_CLK_SPDIF 0x4b4 + +#define AUDIO_SYNC_DOUBLER 0x49c + +#define PMC_CLK_OUT_CNTRL 0x1a8 +#define PMC_DPD_PADS_ORIDE 0x1c +#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20 +#define PMC_CTRL 0 +#define PMC_CTRL_BLINK_ENB 7 + +#define OSC_CTRL 0x50 +#define OSC_CTRL_OSC_FREQ_SHIFT 28 +#define OSC_CTRL_PLL_REF_DIV_SHIFT 26 + +#define PLLXC_SW_MAX_P 6 + +#define CCLKG_BURST_POLICY 0x368 +#define CCLKLP_BURST_POLICY 0x370 +#define SCLK_BURST_POLICY 0x028 +#define SYSTEM_CLK_RATE 0x030 + +#define UTMIP_PLL_CFG2 0x488 +#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6) +#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4) + +#define UTMIP_PLL_CFG1 0x484 +#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6) +#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14) +#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12) + +#define UTMIPLL_HW_PWRDN_CFG0 0x52c +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24) +#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET BIT(6) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE BIT(5) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL BIT(4) +#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE BIT(1) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0) + +#define CLK_SOURCE_I2S0 0x1d8 +#define CLK_SOURCE_I2S1 0x100 +#define CLK_SOURCE_I2S2 0x104 +#define CLK_SOURCE_NDFLASH 0x160 +#define CLK_SOURCE_I2S3 0x3bc +#define CLK_SOURCE_I2S4 0x3c0 +#define CLK_SOURCE_SPDIF_OUT 0x108 +#define CLK_SOURCE_SPDIF_IN 0x10c +#define CLK_SOURCE_PWM 0x110 +#define CLK_SOURCE_ADX 0x638 +#define CLK_SOURCE_AMX 0x63c +#define CLK_SOURCE_HDA 0x428 +#define CLK_SOURCE_HDA2CODEC_2X 0x3e4 +#define CLK_SOURCE_SBC1 0x134 +#define CLK_SOURCE_SBC2 0x118 +#define CLK_SOURCE_SBC3 0x11c +#define CLK_SOURCE_SBC4 0x1b4 +#define CLK_SOURCE_SBC5 0x3c8 +#define CLK_SOURCE_SBC6 0x3cc +#define CLK_SOURCE_SATA_OOB 0x420 +#define CLK_SOURCE_SATA 0x424 +#define CLK_SOURCE_NDSPEED 0x3f8 +#define CLK_SOURCE_VFIR 0x168 +#define CLK_SOURCE_SDMMC1 0x150 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC3 0x1bc +#define CLK_SOURCE_SDMMC4 0x164 +#define CLK_SOURCE_VDE 0x1c8 +#define CLK_SOURCE_CSITE 0x1d4 +#define CLK_SOURCE_LA 0x1f8 +#define CLK_SOURCE_TRACE 0x634 +#define CLK_SOURCE_OWR 0x1cc +#define CLK_SOURCE_NOR 0x1d0 +#define CLK_SOURCE_MIPI 0x174 +#define CLK_SOURCE_I2C1 0x124 +#define CLK_SOURCE_I2C2 0x198 +#define CLK_SOURCE_I2C3 0x1b8 +#define CLK_SOURCE_I2C4 0x3c4 +#define CLK_SOURCE_I2C5 0x128 +#define CLK_SOURCE_UARTA 0x178 +#define CLK_SOURCE_UARTB 0x17c +#define CLK_SOURCE_UARTC 0x1a0 +#define CLK_SOURCE_UARTD 0x1c0 +#define CLK_SOURCE_UARTE 0x1c4 +#define CLK_SOURCE_UARTA_DBG 0x178 +#define CLK_SOURCE_UARTB_DBG 0x17c +#define CLK_SOURCE_UARTC_DBG 0x1a0 +#define CLK_SOURCE_UARTD_DBG 0x1c0 +#define CLK_SOURCE_UARTE_DBG 0x1c4 +#define CLK_SOURCE_3D 0x158 +#define CLK_SOURCE_2D 0x15c +#define CLK_SOURCE_VI_SENSOR 0x1a8 +#define CLK_SOURCE_VI 0x148 +#define CLK_SOURCE_EPP 0x16c +#define CLK_SOURCE_MSENC 0x1f0 +#define CLK_SOURCE_TSEC 0x1f4 +#define CLK_SOURCE_HOST1X 0x180 +#define CLK_SOURCE_HDMI 0x18c +#define CLK_SOURCE_DISP1 0x138 +#define CLK_SOURCE_DISP2 0x13c +#define CLK_SOURCE_CILAB 0x614 +#define CLK_SOURCE_CILCD 0x618 +#define CLK_SOURCE_CILE 0x61c +#define CLK_SOURCE_DSIALP 0x620 +#define CLK_SOURCE_DSIBLP 0x624 +#define CLK_SOURCE_TSENSOR 0x3b8 +#define CLK_SOURCE_D_AUDIO 0x3d0 +#define CLK_SOURCE_DAM0 0x3d8 +#define CLK_SOURCE_DAM1 0x3dc +#define CLK_SOURCE_DAM2 0x3e0 +#define CLK_SOURCE_ACTMON 0x3e8 +#define CLK_SOURCE_EXTERN1 0x3ec +#define CLK_SOURCE_EXTERN2 0x3f0 +#define CLK_SOURCE_EXTERN3 0x3f4 +#define CLK_SOURCE_I2CSLOW 0x3fc +#define CLK_SOURCE_SE 0x42c +#define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_SOC_THERM 0x644 +#define CLK_SOURCE_XUSB_HOST_SRC 0x600 +#define CLK_SOURCE_XUSB_FALCON_SRC 0x604 +#define CLK_SOURCE_XUSB_FS_SRC 0x608 +#define CLK_SOURCE_XUSB_SS_SRC 0x610 +#define CLK_SOURCE_XUSB_DEV_SRC 0x60c +#define CLK_SOURCE_EMC 0x19c + +static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; + +static void __iomem *clk_base; +static void __iomem *pmc_base; + +static DEFINE_SPINLOCK(pll_d_lock); +static DEFINE_SPINLOCK(pll_d2_lock); +static DEFINE_SPINLOCK(pll_u_lock); +static DEFINE_SPINLOCK(pll_div_lock); +static DEFINE_SPINLOCK(pll_re_lock); +static DEFINE_SPINLOCK(clk_doubler_lock); +static DEFINE_SPINLOCK(clk_out_lock); +static DEFINE_SPINLOCK(sysrate_lock); + +static struct pdiv_map pllxc_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 3, .hw_val = 2 }, + { .pdiv = 4, .hw_val = 3 }, + { .pdiv = 5, .hw_val = 4 }, + { .pdiv = 6, .hw_val = 5 }, + { .pdiv = 8, .hw_val = 6 }, + { .pdiv = 10, .hw_val = 7 }, + { .pdiv = 12, .hw_val = 8 }, + { .pdiv = 16, .hw_val = 9 }, + { .pdiv = 12, .hw_val = 10 }, + { .pdiv = 16, .hw_val = 11 }, + { .pdiv = 20, .hw_val = 12 }, + { .pdiv = 24, .hw_val = 13 }, + { .pdiv = 32, .hw_val = 14 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { + { 12000000, 624000000, 104, 0, 2}, + { 12000000, 600000000, 100, 0, 2}, + { 13000000, 600000000, 92, 0, 2}, /* actual: 598.0 MHz */ + { 16800000, 600000000, 71, 0, 2}, /* actual: 596.4 MHz */ + { 19200000, 600000000, 62, 0, 2}, /* actual: 595.2 MHz */ + { 26000000, 600000000, 92, 1, 2}, /* actual: 598.0 MHz */ + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_params pll_c_params = { + .input_min = 12000000, + .input_max = 800000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 600000000, + .vco_max = 1400000000, + .base_reg = PLLC_BASE, + .misc_reg = PLLC_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLC_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLC_MISC, + .iddq_bit_idx = PLLC_IDDQ_BIT, + .max_p = PLLXC_SW_MAX_P, + .dyn_ramp_reg = PLLC_MISC2, + .stepa_shift = 17, + .stepb_shift = 9, + .pdiv_tohw = pllxc_p, +}; + +static struct pdiv_map pllc_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 4, .hw_val = 3 }, + { .pdiv = 8, .hw_val = 5 }, + { .pdiv = 16, .hw_val = 7 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_cx_freq_table[] = { + {12000000, 600000000, 100, 0, 2}, + {13000000, 600000000, 92, 0, 2}, /* actual: 598.0 MHz */ + {16800000, 600000000, 71, 0, 2}, /* actual: 596.4 MHz */ + {19200000, 600000000, 62, 0, 2}, /* actual: 595.2 MHz */ + {26000000, 600000000, 92, 1, 2}, /* actual: 598.0 MHz */ + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_c2_params = { + .input_min = 12000000, + .input_max = 48000000, + .cf_min = 12000000, + .cf_max = 19200000, + .vco_min = 600000000, + .vco_max = 1200000000, + .base_reg = PLLC2_BASE, + .misc_reg = PLLC2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .pdiv_tohw = pllc_p, + .ext_misc_reg[0] = 0x4f0, + .ext_misc_reg[1] = 0x4f4, + .ext_misc_reg[2] = 0x4f8, +}; + +static struct tegra_clk_pll_params pll_c3_params = { + .input_min = 12000000, + .input_max = 48000000, + .cf_min = 12000000, + .cf_max = 19200000, + .vco_min = 600000000, + .vco_max = 1200000000, + .base_reg = PLLC3_BASE, + .misc_reg = PLLC3_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .pdiv_tohw = pllc_p, + .ext_misc_reg[0] = 0x504, + .ext_misc_reg[1] = 0x508, + .ext_misc_reg[2] = 0x50c, +}; + +static struct pdiv_map pllm_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { + {12000000, 800000000, 66, 0, 1}, /* actual: 792.0 MHz */ + {13000000, 800000000, 61, 0, 1}, /* actual: 793.0 MHz */ + {16800000, 800000000, 47, 0, 1}, /* actual: 789.6 MHz */ + {19200000, 800000000, 41, 0, 1}, /* actual: 787.2 MHz */ + {26000000, 800000000, 61, 1, 1}, /* actual: 793.0 MHz */ + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_m_params = { + .input_min = 12000000, + .input_max = 500000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 400000000, + .vco_max = 1066000000, + .base_reg = PLLM_BASE, + .misc_reg = PLLM_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .max_p = 2, + .pdiv_tohw = pllm_p, +}; + +static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { + {12000000, 216000000, 432, 12, 1, 8}, + {13000000, 216000000, 432, 13, 1, 8}, + {16800000, 216000000, 360, 14, 1, 8}, + {19200000, 216000000, 360, 16, 1, 8}, + {26000000, 216000000, 432, 26, 1, 8}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_p_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 200000000, + .vco_max = 700000000, + .base_reg = PLLP_BASE, + .misc_reg = PLLP_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { + {9600000, 282240000, 147, 5, 0, 4}, + {9600000, 368640000, 192, 5, 0, 4}, + {9600000, 240000000, 200, 8, 0, 8}, + + {28800000, 282240000, 245, 25, 0, 8}, + {28800000, 368640000, 320, 25, 0, 8}, + {28800000, 240000000, 200, 24, 0, 8}, + {0, 0, 0, 0, 0, 0}, +}; + + +static struct tegra_clk_pll_params pll_a_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 200000000, + .vco_max = 700000000, + .base_reg = PLLA_BASE, + .misc_reg = PLLA_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { + {12000000, 216000000, 864, 12, 2, 12}, + {13000000, 216000000, 864, 13, 2, 12}, + {16800000, 216000000, 720, 14, 2, 12}, + {19200000, 216000000, 720, 16, 2, 12}, + {26000000, 216000000, 864, 26, 2, 12}, + + {12000000, 594000000, 594, 12, 0, 12}, + {13000000, 594000000, 594, 13, 0, 12}, + {16800000, 594000000, 495, 14, 0, 12}, + {19200000, 594000000, 495, 16, 0, 12}, + {26000000, 594000000, 594, 26, 0, 12}, + + {12000000, 1000000000, 1000, 12, 0, 12}, + {13000000, 1000000000, 1000, 13, 0, 12}, + {19200000, 1000000000, 625, 12, 0, 12}, + {26000000, 1000000000, 1000, 26, 0, 12}, + + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_d_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 500000000, + .vco_max = 1000000000, + .base_reg = PLLD_BASE, + .misc_reg = PLLD_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_d2_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 500000000, + .vco_max = 1000000000, + .base_reg = PLLD2_BASE, + .misc_reg = PLLD2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { + {12000000, 480000000, 960, 12, 0, 12}, + {13000000, 480000000, 960, 13, 0, 12}, + {16800000, 480000000, 400, 7, 0, 5}, + {19200000, 480000000, 200, 4, 0, 3}, + {26000000, 480000000, 960, 26, 0, 12}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_u_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 480000000, + .vco_max = 960000000, + .base_reg = PLLU_BASE, + .misc_reg = PLLU_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, + .pdiv_tohw = pllu_p, +}; + +static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { + /* 1 GHz */ + {12000000, 1000000000, 83, 0, 1}, /* actual: 996.0 MHz */ + {13000000, 1000000000, 76, 0, 1}, /* actual: 988.0 MHz */ + {16800000, 1000000000, 59, 0, 1}, /* actual: 991.2 MHz */ + {19200000, 1000000000, 52, 0, 1}, /* actual: 998.4 MHz */ + {26000000, 1000000000, 76, 1, 1}, /* actual: 988.0 MHz */ + + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_x_params = { + .input_min = 12000000, + .input_max = 800000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 700000000, + .vco_max = 2400000000U, + .base_reg = PLLX_BASE, + .misc_reg = PLLX_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLX_MISC3, + .iddq_bit_idx = PLLX_IDDQ_BIT, + .max_p = PLLXC_SW_MAX_P, + .dyn_ramp_reg = PLLX_MISC2, + .stepa_shift = 16, + .stepb_shift = 24, + .pdiv_tohw = pllxc_p, +}; + +static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { + /* PLLE special case: use cpcon field to store cml divider value */ + {336000000, 100000000, 100, 21, 16, 11}, + {312000000, 100000000, 200, 26, 24, 13}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_e_params = { + .input_min = 12000000, + .input_max = 1000000000, + .cf_min = 12000000, + .cf_max = 75000000, + .vco_min = 1600000000, + .vco_max = 2400000000U, + .base_reg = PLLE_BASE, + .misc_reg = PLLE_MISC, + .aux_reg = PLLE_AUX, + .lock_mask = PLLE_MISC_LOCK, + .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_re_vco_params = { + .input_min = 12000000, + .input_max = 1000000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */ + .vco_min = 300000000, + .vco_max = 600000000, + .base_reg = PLLRE_BASE, + .misc_reg = PLLRE_MISC, + .lock_mask = PLLRE_MISC_LOCK, + .lock_enable_bit_idx = PLLRE_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLRE_MISC, + .iddq_bit_idx = PLLRE_IDDQ_BIT, +}; + +/* Peripheral clock registers */ + +static struct tegra_clk_periph_regs periph_l_regs = { + .enb_reg = CLK_OUT_ENB_L, + .enb_set_reg = CLK_OUT_ENB_SET_L, + .enb_clr_reg = CLK_OUT_ENB_CLR_L, + .rst_reg = RST_DEVICES_L, + .rst_set_reg = RST_DEVICES_SET_L, + .rst_clr_reg = RST_DEVICES_CLR_L, +}; + +static struct tegra_clk_periph_regs periph_h_regs = { + .enb_reg = CLK_OUT_ENB_H, + .enb_set_reg = CLK_OUT_ENB_SET_H, + .enb_clr_reg = CLK_OUT_ENB_CLR_H, + .rst_reg = RST_DEVICES_H, + .rst_set_reg = RST_DEVICES_SET_H, + .rst_clr_reg = RST_DEVICES_CLR_H, +}; + +static struct tegra_clk_periph_regs periph_u_regs = { + .enb_reg = CLK_OUT_ENB_U, + .enb_set_reg = CLK_OUT_ENB_SET_U, + .enb_clr_reg = CLK_OUT_ENB_CLR_U, + .rst_reg = RST_DEVICES_U, + .rst_set_reg = RST_DEVICES_SET_U, + .rst_clr_reg = RST_DEVICES_CLR_U, +}; + +static struct tegra_clk_periph_regs periph_v_regs = { + .enb_reg = CLK_OUT_ENB_V, + .enb_set_reg = CLK_OUT_ENB_SET_V, + .enb_clr_reg = CLK_OUT_ENB_CLR_V, + .rst_reg = RST_DEVICES_V, + .rst_set_reg = RST_DEVICES_SET_V, + .rst_clr_reg = RST_DEVICES_CLR_V, +}; + +static struct tegra_clk_periph_regs periph_w_regs = { + .enb_reg = CLK_OUT_ENB_W, + .enb_set_reg = CLK_OUT_ENB_SET_W, + .enb_clr_reg = CLK_OUT_ENB_CLR_W, + .rst_reg = RST_DEVICES_W, + .rst_set_reg = RST_DEVICES_SET_W, + .rst_clr_reg = RST_DEVICES_CLR_W, +}; + +/* possible OSC frequencies in Hz */ +static unsigned long tegra114_input_freq[] = { + [0] = 13000000, + [1] = 16800000, + [4] = 19200000, + [5] = 38400000, + [8] = 12000000, + [9] = 48000000, + [12] = 260000000, +}; + +#define MASK(x) (BIT(x) - 1) + +#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_MUX_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id, flags)\ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, flags) + +#define TEGRA_INIT_DATA_MUX8(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 29, MASK(3), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_INT_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id, flags)\ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, flags) + +#define TEGRA_INIT_DATA_INT8(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_UART(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 16, 1, TEGRA_DIVIDER_UART, _regs,\ + _clk_num, periph_clk_enb_refcnt, 0, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_I2C(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 16, 0, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, 0, _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \ + _mux_shift, _mux_mask, _clk_num, _regs, \ + _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + _mux_shift, _mux_mask, 0, 0, 0, 0, 0, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_XUSB(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset, \ + 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_AUDIO(_name, _con_id, _dev_id, _offset, _clk_num,\ + _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, mux_d_audio_clk, \ + _offset, 16, 0xE01F, 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags , _clk_id, \ + mux_d_audio_clk_idx, 0) + +enum tegra114_clk { + rtc = 4, timer = 5, uarta = 6, sdmmc2 = 9, i2s1 = 11, i2c1 = 12, + ndflash = 13, sdmmc1 = 14, sdmmc4 = 15, pwm = 17, i2s2 = 18, epp = 19, + gr_2d = 21, usbd = 22, isp = 23, gr_3d = 24, disp2 = 26, disp1 = 27, + host1x = 28, vcp = 29, i2s0 = 30, apbdma = 34, kbc = 36, kfuse = 40, + sbc1 = 41, nor = 42, sbc2 = 44, sbc3 = 46, i2c5 = 47, dsia = 48, + mipi = 50, hdmi = 51, csi = 52, i2c2 = 54, uartc = 55, mipi_cal = 56, + emc, usb2, usb3, vde = 61, bsea = 62, bsev = 63, uartd = 65, + i2c3 = 67, sbc4 = 68, sdmmc3 = 69, owr = 71, csite = 73, + la = 76, trace = 77, soc_therm = 78, dtv = 79, ndspeed = 80, + i2cslow = 81, dsib = 82, tsec = 83, xusb_host = 89, msenc = 91, + csus = 92, mselect = 99, tsensor = 100, i2s3 = 101, i2s4 = 102, + i2c4 = 103, sbc5 = 104, sbc6 = 105, d_audio, apbif = 107, dam0, dam1, + dam2, hda2codec_2x = 111, audio0_2x = 113, audio1_2x, audio2_2x, + audio3_2x, audio4_2x, spdif_2x, actmon = 119, extern1 = 120, + extern2 = 121, extern3 = 122, hda = 125, se = 127, hda2hdmi = 128, + cilab = 144, cilcd = 145, cile = 146, dsialp = 147, dsiblp = 148, + dds = 150, dp2 = 152, amx = 153, adx = 154, xusb_ss = 156, uartb = 192, + vfir, spdif_in, spdif_out, vi, vi_sensor, fuse, fuse_burn, clk_32k, + clk_m, clk_m_div2, clk_m_div4, pll_ref, pll_c, pll_c_out1, pll_c2, + pll_c3, pll_m, pll_m_out1, pll_p, pll_p_out1, pll_p_out2, pll_p_out3, + pll_p_out4, pll_a, pll_a_out0, pll_d, pll_d_out0, pll_d2, pll_d2_out0, + pll_u, pll_u_480M, pll_u_60M, pll_u_48M, pll_u_12M, pll_x, pll_x_out0, + pll_re_vco, pll_re_out, pll_e_out0, spdif_in_sync, i2s0_sync, + i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync, vimclk_sync, audio0, + audio1, audio2, audio3, audio4, spdif, clk_out_1, clk_out_2, clk_out_3, + blink, xusb_host_src = 252, xusb_falcon_src, xusb_fs_src, xusb_ss_src, + xusb_dev_src, xusb_dev, xusb_hs_src, sclk, hclk, pclk, cclk_g, cclk_lp, + + /* Mux clocks */ + + audio0_mux = 300, audio1_mux, audio2_mux, audio3_mux, audio4_mux, + spdif_mux, clk_out_1_mux, clk_out_2_mux, clk_out_3_mux, dsia_mux, + dsib_mux, clk_max, +}; + +struct utmi_clk_param { + /* Oscillator Frequency in KHz */ + u32 osc_frequency; + /* UTMIP PLL Enable Delay Count */ + u8 enable_delay_count; + /* UTMIP PLL Stable count */ + u8 stable_count; + /* UTMIP PLL Active delay count */ + u8 active_delay_count; + /* UTMIP PLL Xtal frequency count */ + u8 xtal_freq_count; +}; + +static const struct utmi_clk_param utmi_parameters[] = { + {.osc_frequency = 13000000, .enable_delay_count = 0x02, + .stable_count = 0x33, .active_delay_count = 0x05, + .xtal_freq_count = 0x7F}, + {.osc_frequency = 19200000, .enable_delay_count = 0x03, + .stable_count = 0x4B, .active_delay_count = 0x06, + .xtal_freq_count = 0xBB}, + {.osc_frequency = 12000000, .enable_delay_count = 0x02, + .stable_count = 0x2F, .active_delay_count = 0x04, + .xtal_freq_count = 0x76}, + {.osc_frequency = 26000000, .enable_delay_count = 0x04, + .stable_count = 0x66, .active_delay_count = 0x09, + .xtal_freq_count = 0xFE}, + {.osc_frequency = 16800000, .enable_delay_count = 0x03, + .stable_count = 0x41, .active_delay_count = 0x0A, + .xtal_freq_count = 0xA4}, +}; + +/* peripheral mux definitions */ + +#define MUX_I2S_SPDIF(_id) \ +static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \ + #_id, "pll_p",\ + "clk_m"}; +MUX_I2S_SPDIF(audio0) +MUX_I2S_SPDIF(audio1) +MUX_I2S_SPDIF(audio2) +MUX_I2S_SPDIF(audio3) +MUX_I2S_SPDIF(audio4) +MUX_I2S_SPDIF(audio) + +#define mux_pllaout0_audio0_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio1_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio2_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio3_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio4_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio_2x_pllp_clkm_idx NULL + +static const char *mux_pllp_pllc_pllm_clkm[] = { + "pll_p", "pll_c", "pll_m", "clk_m" +}; +#define mux_pllp_pllc_pllm_clkm_idx NULL + +static const char *mux_pllp_pllc_pllm[] = { "pll_p", "pll_c", "pll_m" }; +#define mux_pllp_pllc_pllm_idx NULL + +static const char *mux_pllp_pllc_clk32_clkm[] = { + "pll_p", "pll_c", "clk_32k", "clk_m" +}; +#define mux_pllp_pllc_clk32_clkm_idx NULL + +static const char *mux_plla_pllc_pllp_clkm[] = { + "pll_a_out0", "pll_c", "pll_p", "clk_m" +}; +#define mux_plla_pllc_pllp_clkm_idx mux_pllp_pllc_pllm_clkm_idx + +static const char *mux_pllp_pllc2_c_c3_pllm_clkm[] = { + "pll_p", "pll_c2", "pll_c", "pll_c3", "pll_m", "clk_m" +}; +static u32 mux_pllp_pllc2_c_c3_pllm_clkm_idx[] = { + [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, +}; + +static const char *mux_pllp_clkm[] = { + "pll_p", "clk_m" +}; +static u32 mux_pllp_clkm_idx[] = { + [0] = 0, [1] = 3, +}; + +static const char *mux_pllm_pllc2_c_c3_pllp_plla[] = { + "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0" +}; +#define mux_pllm_pllc2_c_c3_pllp_plla_idx mux_pllp_pllc2_c_c3_pllm_clkm_idx + +static const char *mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = { + "pll_p", "pll_m", "pll_d_out0", "pll_a_out0", "pll_c", + "pll_d2_out0", "clk_m" +}; +#define mux_pllp_pllm_plld_plla_pllc_plld2_clkm_idx NULL + +static const char *mux_pllm_pllc_pllp_plla[] = { + "pll_m", "pll_c", "pll_p", "pll_a_out0" +}; +#define mux_pllm_pllc_pllp_plla_idx mux_pllp_pllc_pllm_clkm_idx + +static const char *mux_pllp_pllc_clkm[] = { + "pll_p", "pll_c", "pll_m" +}; +static u32 mux_pllp_pllc_clkm_idx[] = { + [0] = 0, [1] = 1, [2] = 3, +}; + +static const char *mux_pllp_pllc_clkm_clk32[] = { + "pll_p", "pll_c", "clk_m", "clk_32k" +}; +#define mux_pllp_pllc_clkm_clk32_idx NULL + +static const char *mux_plla_clk32_pllp_clkm_plle[] = { + "pll_a_out0", "clk_32k", "pll_p", "clk_m", "pll_e_out0" +}; +#define mux_plla_clk32_pllp_clkm_plle_idx NULL + +static const char *mux_clkm_pllp_pllc_pllre[] = { + "clk_m", "pll_p", "pll_c", "pll_re_out" +}; +static u32 mux_clkm_pllp_pllc_pllre_idx[] = { + [0] = 0, [1] = 1, [2] = 3, [3] = 5, +}; + +static const char *mux_clkm_48M_pllp_480M[] = { + "clk_m", "pll_u_48M", "pll_p", "pll_u_480M" +}; +#define mux_clkm_48M_pllp_480M_idx NULL + +static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = { + "clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref" +}; +static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = { + [0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7, +}; + +static const char *mux_plld_out0_plld2_out0[] = { + "pll_d_out0", "pll_d2_out0", +}; +#define mux_plld_out0_plld2_out0_idx NULL + +static const char *mux_d_audio_clk[] = { + "pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync", + "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", +}; +static u32 mux_d_audio_clk_idx[] = { + [0] = 0, [1] = 0x8000, [2] = 0xc000, [3] = 0xE000, [4] = 0xE001, + [5] = 0xE002, [6] = 0xE003, [7] = 0xE004, [8] = 0xE005, [9] = 0xE007, +}; + +static const char *mux_pllmcp_clkm[] = { + "pll_m_out0", "pll_c_out0", "pll_p_out0", "clk_m", "pll_m_ud", +}; + +static const struct clk_div_table pll_re_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 5 }, + { .val = 5, .div = 6 }, + { .val = 0, .div = 0 }, +}; + +static struct clk *clks[clk_max]; +static struct clk_onecell_data clk_data; + +static unsigned long osc_freq; +static unsigned long pll_ref_freq; + +static int __init tegra114_osc_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + u32 val, pll_ref_div; + + val = readl_relaxed(clk_base + OSC_CTRL); + + osc_freq = tegra114_input_freq[val >> OSC_CTRL_OSC_FREQ_SHIFT]; + if (!osc_freq) { + WARN_ON(1); + return -EINVAL; + } + + /* clk_m */ + clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT, + osc_freq); + clk_register_clkdev(clk, "clk_m", NULL); + clks[clk_m] = clk; + + /* pll_ref */ + val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; + pll_ref_div = 1 << val; + clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m", + CLK_SET_RATE_PARENT, 1, pll_ref_div); + clk_register_clkdev(clk, "pll_ref", NULL); + clks[pll_ref] = clk; + + pll_ref_freq = osc_freq / pll_ref_div; + + return 0; +} + +static void __init tegra114_fixed_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* clk_32k */ + clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT, + 32768); + clk_register_clkdev(clk, "clk_32k", NULL); + clks[clk_32k] = clk; + + /* clk_m_div2 */ + clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "clk_m_div2", NULL); + clks[clk_m_div2] = clk; + + /* clk_m_div4 */ + clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m", + CLK_SET_RATE_PARENT, 1, 4); + clk_register_clkdev(clk, "clk_m_div4", NULL); + clks[clk_m_div4] = clk; + +} + +static __init void tegra114_utmi_param_configure(void __iomem *clk_base) +{ + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) { + if (osc_freq == utmi_parameters[i].osc_frequency) + break; + } + + if (i >= ARRAY_SIZE(utmi_parameters)) { + pr_err("%s: Unexpected oscillator freq %lu\n", __func__, + osc_freq); + return; + } + + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL stable and active counts */ + /* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */ + reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); + reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count); + + reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); + + reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i]. + active_delay_count); + + /* Remove power downs from UTMIP PLL control bits */ + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN; + + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL delay and oscillator frequency counts */ + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + + reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i]. + enable_delay_count); + + reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); + reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(utmi_parameters[i]. + xtal_freq_count); + + /* Remove power downs from UTMIP PLL control bits */ + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + + /* Setup HW control of UTMIPLL */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL; + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + + udelay(1); + + /* Setup SW override of UTMIPLL assuming USB2.0 + ports are assigned to USB2 */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + + udelay(1); + + /* Enable HW control UTMIPLL */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); +} + +static void __init _clip_vco_min(struct tegra_clk_pll_params *pll_params) +{ + pll_params->vco_min = + DIV_ROUND_UP(pll_params->vco_min, pll_ref_freq) * pll_ref_freq; +} + +static int __init _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params, + void __iomem *clk_base) +{ + u32 val; + u32 step_a, step_b; + + switch (pll_ref_freq) { + case 12000000: + case 13000000: + case 26000000: + step_a = 0x2B; + step_b = 0x0B; + break; + case 16800000: + step_a = 0x1A; + step_b = 0x09; + break; + case 19200000: + step_a = 0x12; + step_b = 0x08; + break; + default: + pr_err("%s: Unexpected reference rate %lu\n", + __func__, pll_ref_freq); + WARN_ON(1); + return -EINVAL; + } + + val = step_a << pll_params->stepa_shift; + val |= step_b << pll_params->stepb_shift; + writel_relaxed(val, clk_base + pll_params->dyn_ramp_reg); + + return 0; +} + +static void __init _init_iddq(struct tegra_clk_pll_params *pll_params, + void __iomem *clk_base) +{ + u32 val, val_iddq; + + val = readl_relaxed(clk_base + pll_params->base_reg); + val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg); + + if (val & BIT(30)) + WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx)); + else { + val_iddq |= BIT(pll_params->iddq_bit_idx); + writel_relaxed(val_iddq, clk_base + pll_params->iddq_reg); + } +} + +static void __init tegra114_pll_init(void __iomem *clk_base, + void __iomem *pmc) +{ + u32 val; + struct clk *clk; + + /* PLLC */ + _clip_vco_min(&pll_c_params); + if (_setup_dynamic_ramp(&pll_c_params, clk_base) >= 0) { + _init_iddq(&pll_c_params, clk_base); + clk = tegra_clk_register_pllxc("pll_c", "pll_ref", clk_base, + pmc, 0, 0, &pll_c_params, TEGRA_PLL_USE_LOCK, + pll_c_freq_table, NULL); + clk_register_clkdev(clk, "pll_c", NULL); + clks[pll_c] = clk; + + /* PLLC_OUT1 */ + clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c", + clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div", + clk_base + PLLC_OUT, 1, 0, + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_c_out1", NULL); + clks[pll_c_out1] = clk; + } + + /* PLLC2 */ + _clip_vco_min(&pll_c2_params); + clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0, 0, + &pll_c2_params, TEGRA_PLL_USE_LOCK, + pll_cx_freq_table, NULL); + clk_register_clkdev(clk, "pll_c2", NULL); + clks[pll_c2] = clk; + + /* PLLC3 */ + _clip_vco_min(&pll_c3_params); + clk = tegra_clk_register_pllc("pll_c3", "pll_ref", clk_base, pmc, 0, 0, + &pll_c3_params, TEGRA_PLL_USE_LOCK, + pll_cx_freq_table, NULL); + clk_register_clkdev(clk, "pll_c3", NULL); + clks[pll_c3] = clk; + + /* PLLP */ + clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, pmc, 0, + 408000000, &pll_p_params, + TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK, + pll_p_freq_table, NULL); + clk_register_clkdev(clk, "pll_p", NULL); + clks[pll_p] = clk; + + /* PLLP_OUT1 */ + clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p", + clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div", + clk_base + PLLP_OUTA, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out1", NULL); + clks[pll_p_out1] = clk; + + /* PLLP_OUT2 */ + clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p", + clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div", + clk_base + PLLP_OUTA, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out2", NULL); + clks[pll_p_out2] = clk; + + /* PLLP_OUT3 */ + clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p", + clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div", + clk_base + PLLP_OUTB, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out3", NULL); + clks[pll_p_out3] = clk; + + /* PLLP_OUT4 */ + clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p", + clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div", + clk_base + PLLP_OUTB, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out4", NULL); + clks[pll_p_out4] = clk; + + /* PLLM */ + _clip_vco_min(&pll_m_params); + clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc, + CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0, + &pll_m_params, TEGRA_PLL_USE_LOCK, + pll_m_freq_table, NULL); + clk_register_clkdev(clk, "pll_m", NULL); + clks[pll_m] = clk; + + /* PLLM_OUT1 */ + clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m", + clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div", + clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_m_out1", NULL); + clks[pll_m_out1] = clk; + + /* PLLM_UD */ + clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m", + CLK_SET_RATE_PARENT, 1, 1); + + /* PLLX */ + _clip_vco_min(&pll_x_params); + if (_setup_dynamic_ramp(&pll_x_params, clk_base) >= 0) { + _init_iddq(&pll_x_params, clk_base); + clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, + pmc, CLK_IGNORE_UNUSED, 0, &pll_x_params, + TEGRA_PLL_USE_LOCK, pll_x_freq_table, NULL); + clk_register_clkdev(clk, "pll_x", NULL); + clks[pll_x] = clk; + } + + /* PLLX_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_x_out0", NULL); + clks[pll_x_out0] = clk; + + /* PLLU */ + val = readl(clk_base + pll_u_params.base_reg); + val &= ~BIT(24); /* disable PLLU_OVERRIDE */ + writel(val, clk_base + pll_u_params.base_reg); + + clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, pmc, 0, + 0, &pll_u_params, TEGRA_PLLU | + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_u_freq_table, &pll_u_lock); + clk_register_clkdev(clk, "pll_u", NULL); + clks[pll_u] = clk; + + tegra114_utmi_param_configure(clk_base); + + /* PLLU_480M */ + clk = clk_register_gate(NULL, "pll_u_480M", "pll_u", + CLK_SET_RATE_PARENT, clk_base + PLLU_BASE, + 22, 0, &pll_u_lock); + clk_register_clkdev(clk, "pll_u_480M", NULL); + clks[pll_u_480M] = clk; + + /* PLLU_60M */ + clk = clk_register_fixed_factor(NULL, "pll_u_60M", "pll_u", + CLK_SET_RATE_PARENT, 1, 8); + clk_register_clkdev(clk, "pll_u_60M", NULL); + clks[pll_u_60M] = clk; + + /* PLLU_48M */ + clk = clk_register_fixed_factor(NULL, "pll_u_48M", "pll_u", + CLK_SET_RATE_PARENT, 1, 10); + clk_register_clkdev(clk, "pll_u_48M", NULL); + clks[pll_u_48M] = clk; + + /* PLLU_12M */ + clk = clk_register_fixed_factor(NULL, "pll_u_12M", "pll_u", + CLK_SET_RATE_PARENT, 1, 40); + clk_register_clkdev(clk, "pll_u_12M", NULL); + clks[pll_u_12M] = clk; + + /* PLLD */ + clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc, 0, + 0, &pll_d_params, + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d_lock); + clk_register_clkdev(clk, "pll_d", NULL); + clks[pll_d] = clk; + + /* PLLD_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d_out0", NULL); + clks[pll_d_out0] = clk; + + /* PLLD2 */ + clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc, 0, + 0, &pll_d2_params, + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d2_lock); + clk_register_clkdev(clk, "pll_d2", NULL); + clks[pll_d2] = clk; + + /* PLLD2_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d2_out0", NULL); + clks[pll_d2_out0] = clk; + + /* PLLA */ + clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, pmc, 0, + 0, &pll_a_params, TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_USE_LOCK, pll_a_freq_table, NULL); + clk_register_clkdev(clk, "pll_a", NULL); + clks[pll_a] = clk; + + /* PLLA_OUT0 */ + clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a", + clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div", + clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_a_out0", NULL); + clks[pll_a_out0] = clk; + + /* PLLRE */ + _clip_vco_min(&pll_re_vco_params); + clk = tegra_clk_register_pllre("pll_re_vco", "pll_ref", clk_base, pmc, + 0, 0, &pll_re_vco_params, TEGRA_PLL_USE_LOCK, + NULL, &pll_re_lock, pll_ref_freq); + clk_register_clkdev(clk, "pll_re_vco", NULL); + clks[pll_re_vco] = clk; + + clk = clk_register_divider_table(NULL, "pll_re_out", "pll_re_vco", 0, + clk_base + PLLRE_BASE, 16, 4, 0, + pll_re_div_table, &pll_re_lock); + clk_register_clkdev(clk, "pll_re_out", NULL); + clks[pll_re_out] = clk; + + /* PLLE */ + clk = tegra_clk_register_plle_tegra114("pll_e_out0", "pll_re_vco", + clk_base, 0, 100000000, &pll_e_params, + pll_e_freq_table, NULL); + clk_register_clkdev(clk, "pll_e_out0", NULL); + clks[pll_e_out0] = clk; +} + +static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync", + "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", +}; + +static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern1", +}; + +static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern2", +}; + +static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern3", +}; + +static void __init tegra114_audio_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* spdif_in_sync */ + clk = tegra_clk_register_sync_source("spdif_in_sync", 24000000, + 24000000); + clk_register_clkdev(clk, "spdif_in_sync", NULL); + clks[spdif_in_sync] = clk; + + /* i2s0_sync */ + clk = tegra_clk_register_sync_source("i2s0_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s0_sync", NULL); + clks[i2s0_sync] = clk; + + /* i2s1_sync */ + clk = tegra_clk_register_sync_source("i2s1_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s1_sync", NULL); + clks[i2s1_sync] = clk; + + /* i2s2_sync */ + clk = tegra_clk_register_sync_source("i2s2_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s2_sync", NULL); + clks[i2s2_sync] = clk; + + /* i2s3_sync */ + clk = tegra_clk_register_sync_source("i2s3_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s3_sync", NULL); + clks[i2s3_sync] = clk; + + /* i2s4_sync */ + clk = tegra_clk_register_sync_source("i2s4_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s4_sync", NULL); + clks[i2s4_sync] = clk; + + /* vimclk_sync */ + clk = tegra_clk_register_sync_source("vimclk_sync", 24000000, 24000000); + clk_register_clkdev(clk, "vimclk_sync", NULL); + clks[vimclk_sync] = clk; + + /* audio0 */ + clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, + NULL); + clks[audio0_mux] = clk; + clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S0, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio0", NULL); + clks[audio0] = clk; + + /* audio1 */ + clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, + NULL); + clks[audio1_mux] = clk; + clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S1, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio1", NULL); + clks[audio1] = clk; + + /* audio2 */ + clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, + NULL); + clks[audio2_mux] = clk; + clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S2, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio2", NULL); + clks[audio2] = clk; + + /* audio3 */ + clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, + NULL); + clks[audio3_mux] = clk; + clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S3, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio3", NULL); + clks[audio3] = clk; + + /* audio4 */ + clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, + NULL); + clks[audio4_mux] = clk; + clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S4, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio4", NULL); + clks[audio4] = clk; + + /* spdif */ + clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, + NULL); + clks[spdif_mux] = clk; + clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0, + clk_base + AUDIO_SYNC_CLK_SPDIF, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "spdif", NULL); + clks[spdif] = clk; + + /* audio0_2x */ + clk = clk_register_fixed_factor(NULL, "audio0_doubler", "audio0", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio0_div", "audio0_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 24, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio0_2x", "audio0_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 113, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio0_2x", NULL); + clks[audio0_2x] = clk; + + /* audio1_2x */ + clk = clk_register_fixed_factor(NULL, "audio1_doubler", "audio1", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio1_div", "audio1_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 25, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio1_2x", "audio1_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 114, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio1_2x", NULL); + clks[audio1_2x] = clk; + + /* audio2_2x */ + clk = clk_register_fixed_factor(NULL, "audio2_doubler", "audio2", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio2_div", "audio2_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 26, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio2_2x", "audio2_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 115, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio2_2x", NULL); + clks[audio2_2x] = clk; + + /* audio3_2x */ + clk = clk_register_fixed_factor(NULL, "audio3_doubler", "audio3", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio3_div", "audio3_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 27, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio3_2x", "audio3_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 116, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio3_2x", NULL); + clks[audio3_2x] = clk; + + /* audio4_2x */ + clk = clk_register_fixed_factor(NULL, "audio4_doubler", "audio4", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio4_div", "audio4_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 28, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio4_2x", "audio4_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 117, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio4_2x", NULL); + clks[audio4_2x] = clk; + + /* spdif_2x */ + clk = clk_register_fixed_factor(NULL, "spdif_doubler", "spdif", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("spdif_div", "spdif_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 29, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("spdif_2x", "spdif_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 118, + &periph_v_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "spdif_2x", NULL); + clks[spdif_2x] = clk; +} + +static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) +{ + struct clk *clk; + + /* clk_out_1 */ + clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, + &clk_out_lock); + clks[clk_out_1_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_1", "clk_out_1_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 2, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern1", "clk_out_1"); + clks[clk_out_1] = clk; + + /* clk_out_2 */ + clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, + &clk_out_lock); + clks[clk_out_2_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 10, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern2", "clk_out_2"); + clks[clk_out_2] = clk; + + /* clk_out_3 */ + clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, + &clk_out_lock); + clks[clk_out_3_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 18, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern3", "clk_out_3"); + clks[clk_out_3] = clk; + + /* blink */ + clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0, + pmc_base + PMC_DPD_PADS_ORIDE, + PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL); + clk = clk_register_gate(NULL, "blink", "blink_override", 0, + pmc_base + PMC_CTRL, + PMC_CTRL_BLINK_ENB, 0, NULL); + clk_register_clkdev(clk, "blink", NULL); + clks[blink] = clk; + +} + +static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", + "pll_p_out3", "pll_p_out2", "unused", + "clk_32k", "pll_m_out1" }; + +static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p", "pll_p_out4", "unused", + "unused", "pll_x" }; + +static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p", "pll_p_out4", "unused", + "unused", "pll_x", "pll_x_out0" }; + +static void __init tegra114_super_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* CCLKG */ + clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents, + ARRAY_SIZE(cclk_g_parents), + CLK_SET_RATE_PARENT, + clk_base + CCLKG_BURST_POLICY, + 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "cclk_g", NULL); + clks[cclk_g] = clk; + + /* CCLKLP */ + clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents, + ARRAY_SIZE(cclk_lp_parents), + CLK_SET_RATE_PARENT, + clk_base + CCLKLP_BURST_POLICY, + 0, 4, 8, 9, NULL); + clk_register_clkdev(clk, "cclk_lp", NULL); + clks[cclk_lp] = clk; + + /* SCLK */ + clk = tegra_clk_register_super_mux("sclk", sclk_parents, + ARRAY_SIZE(sclk_parents), + CLK_SET_RATE_PARENT, + clk_base + SCLK_BURST_POLICY, + 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "sclk", NULL); + clks[sclk] = clk; + + /* HCLK */ + clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, + clk_base + SYSTEM_CLK_RATE, 4, 2, 0, + &sysrate_lock); + clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT | + CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, + 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); + clk_register_clkdev(clk, "hclk", NULL); + clks[hclk] = clk; + + /* PCLK */ + clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, + clk_base + SYSTEM_CLK_RATE, 0, 2, 0, + &sysrate_lock); + clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT | + CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, + 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); + clk_register_clkdev(clk, "pclk", NULL); + clks[pclk] = clk; +} + +static struct tegra_periph_init_data tegra_periph_clk_list[] = { + TEGRA_INIT_DATA_MUX("i2s0", NULL, "tegra30-i2s.0", mux_pllaout0_audio0_2x_pllp_clkm, CLK_SOURCE_I2S0, 30, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s0), + TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra30-i2s.1", mux_pllaout0_audio1_2x_pllp_clkm, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1), + TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra30-i2s.2", mux_pllaout0_audio2_2x_pllp_clkm, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2), + TEGRA_INIT_DATA_MUX("i2s3", NULL, "tegra30-i2s.3", mux_pllaout0_audio3_2x_pllp_clkm, CLK_SOURCE_I2S3, 101, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s3), + TEGRA_INIT_DATA_MUX("i2s4", NULL, "tegra30-i2s.4", mux_pllaout0_audio4_2x_pllp_clkm, CLK_SOURCE_I2S4, 102, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s4), + TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra30-spdif", mux_pllaout0_audio_2x_pllp_clkm, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out), + TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra30-spdif", mux_pllp_pllc_pllm, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in), + TEGRA_INIT_DATA_MUX("pwm", NULL, "pwm", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_PWM, 17, &periph_l_regs, TEGRA_PERIPH_ON_APB, pwm), + TEGRA_INIT_DATA_MUX("adx", NULL, "adx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX, 154, &periph_w_regs, TEGRA_PERIPH_ON_APB, adx), + TEGRA_INIT_DATA_MUX("amx", NULL, "amx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX, 153, &periph_w_regs, TEGRA_PERIPH_ON_APB, amx), + TEGRA_INIT_DATA_MUX("hda", "hda", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA, 125, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda), + TEGRA_INIT_DATA_MUX("hda2codec_2x", "hda2codec", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda2codec_2x), + TEGRA_INIT_DATA_MUX("sbc1", NULL, "tegra11-spi.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1), + TEGRA_INIT_DATA_MUX("sbc2", NULL, "tegra11-spi.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2), + TEGRA_INIT_DATA_MUX("sbc3", NULL, "tegra11-spi.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3), + TEGRA_INIT_DATA_MUX("sbc4", NULL, "tegra11-spi.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4), + TEGRA_INIT_DATA_MUX("sbc5", NULL, "tegra11-spi.4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC5, 104, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc5), + TEGRA_INIT_DATA_MUX("sbc6", NULL, "tegra11-spi.5", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC6, 105, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc6), + TEGRA_INIT_DATA_MUX8("ndflash", NULL, "tegra_nand", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), + TEGRA_INIT_DATA_MUX8("ndspeed", NULL, "tegra_nand_speed", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDSPEED, 80, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), + TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir), + TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1), + TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2), + TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3), + TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4), + TEGRA_INIT_DATA_INT("vde", NULL, "vde", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde), + TEGRA_INIT_DATA_MUX_FLAGS("csite", NULL, "csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, TEGRA_PERIPH_ON_APB, csite, CLK_IGNORE_UNUSED), + TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, TEGRA_PERIPH_ON_APB, la), + TEGRA_INIT_DATA_MUX("trace", NULL, "trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, &periph_u_regs, TEGRA_PERIPH_ON_APB, trace), + TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr), + TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor), + TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi), + TEGRA_INIT_DATA_I2C("i2c1", "div-clk", "tegra11-i2c.0", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, i2c1), + TEGRA_INIT_DATA_I2C("i2c2", "div-clk", "tegra11-i2c.1", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, i2c2), + TEGRA_INIT_DATA_I2C("i2c3", "div-clk", "tegra11-i2c.2", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, i2c3), + TEGRA_INIT_DATA_I2C("i2c4", "div-clk", "tegra11-i2c.3", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, &periph_v_regs, i2c4), + TEGRA_INIT_DATA_I2C("i2c5", "div-clk", "tegra11-i2c.4", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, &periph_h_regs, i2c5), + TEGRA_INIT_DATA_UART("uarta", NULL, "tegra_uart.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, &periph_l_regs, uarta), + TEGRA_INIT_DATA_UART("uartb", NULL, "tegra_uart.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, &periph_l_regs, uartb), + TEGRA_INIT_DATA_UART("uartc", NULL, "tegra_uart.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, &periph_h_regs, uartc), + TEGRA_INIT_DATA_UART("uartd", NULL, "tegra_uart.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, &periph_u_regs, uartd), + TEGRA_INIT_DATA_INT("3d", NULL, "3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, &periph_l_regs, 0, gr_3d), + TEGRA_INIT_DATA_INT("2d", NULL, "2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr_2d), + TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor), + TEGRA_INIT_DATA_INT8("vi", "vi", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi), + TEGRA_INIT_DATA_INT8("epp", NULL, "epp", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp), + TEGRA_INIT_DATA_INT8("msenc", NULL, "msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, &periph_h_regs, TEGRA_PERIPH_WAR_1005168, msenc), + TEGRA_INIT_DATA_INT8("tsec", NULL, "tsec", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_TSEC, 83, &periph_u_regs, 0, tsec), + TEGRA_INIT_DATA_INT8("host1x", NULL, "host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x), + TEGRA_INIT_DATA_MUX8("hdmi", NULL, "hdmi", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi), + TEGRA_INIT_DATA_MUX("cilab", "cilab", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILAB, 144, &periph_w_regs, 0, cilab), + TEGRA_INIT_DATA_MUX("cilcd", "cilcd", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILCD, 145, &periph_w_regs, 0, cilcd), + TEGRA_INIT_DATA_MUX("cile", "cile", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILE, 146, &periph_w_regs, 0, cile), + TEGRA_INIT_DATA_MUX("dsialp", "dsialp", "tegradc.0", mux_pllp_pllc_clkm, CLK_SOURCE_DSIALP, 147, &periph_w_regs, 0, dsialp), + TEGRA_INIT_DATA_MUX("dsiblp", "dsiblp", "tegradc.1", mux_pllp_pllc_clkm, CLK_SOURCE_DSIBLP, 148, &periph_w_regs, 0, dsiblp), + TEGRA_INIT_DATA_MUX("tsensor", NULL, "tegra-tsensor", mux_pllp_pllc_clkm_clk32, CLK_SOURCE_TSENSOR, 100, &periph_v_regs, TEGRA_PERIPH_ON_APB, tsensor), + TEGRA_INIT_DATA_MUX("actmon", NULL, "actmon", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_ACTMON, 119, &periph_v_regs, 0, actmon), + TEGRA_INIT_DATA_MUX8("extern1", NULL, "extern1", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, &periph_v_regs, 0, extern1), + TEGRA_INIT_DATA_MUX8("extern2", NULL, "extern2", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, &periph_v_regs, 0, extern2), + TEGRA_INIT_DATA_MUX8("extern3", NULL, "extern3", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, &periph_v_regs, 0, extern3), + TEGRA_INIT_DATA_MUX("i2cslow", NULL, "i2cslow", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_I2CSLOW, 81, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2cslow), + TEGRA_INIT_DATA_INT8("se", NULL, "se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, &periph_v_regs, TEGRA_PERIPH_ON_APB, se), + TEGRA_INIT_DATA_INT_FLAGS("mselect", NULL, "mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, &periph_v_regs, 0, mselect, CLK_IGNORE_UNUSED), + TEGRA_INIT_DATA_MUX8("soc_therm", NULL, "soc_therm", mux_pllm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, &periph_u_regs, TEGRA_PERIPH_ON_APB, soc_therm), + TEGRA_INIT_DATA_XUSB("xusb_host_src", "host_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, &periph_w_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_host_src), + TEGRA_INIT_DATA_XUSB("xusb_falcon_src", "falcon_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_falcon_src), + TEGRA_INIT_DATA_XUSB("xusb_fs_src", "fs_src", "tegra_xhci", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_fs_src), + TEGRA_INIT_DATA_XUSB("xusb_ss_src", "ss_src", "tegra_xhci", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_ss_src), + TEGRA_INIT_DATA_XUSB("xusb_dev_src", "dev_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, &periph_u_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_dev_src), + TEGRA_INIT_DATA_AUDIO("d_audio", "d_audio", "tegra30-ahub", CLK_SOURCE_D_AUDIO, 106, &periph_v_regs, TEGRA_PERIPH_ON_APB, d_audio), + TEGRA_INIT_DATA_AUDIO("dam0", NULL, "tegra30-dam.0", CLK_SOURCE_DAM0, 108, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam0), + TEGRA_INIT_DATA_AUDIO("dam1", NULL, "tegra30-dam.1", CLK_SOURCE_DAM1, 109, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam1), + TEGRA_INIT_DATA_AUDIO("dam2", NULL, "tegra30-dam.2", CLK_SOURCE_DAM2, 110, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam2), +}; + +static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = { + TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, &periph_l_regs, 0, disp1), + TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, &periph_l_regs, 0, disp2), +}; + +static __init void tegra114_periph_clk_init(void __iomem *clk_base) +{ + struct tegra_periph_init_data *data; + struct clk *clk; + int i; + u32 val; + + /* apbdma */ + clk = tegra_clk_register_periph_gate("apbdma", "clk_m", 0, clk_base, + 0, 34, &periph_h_regs, + periph_clk_enb_refcnt); + clks[apbdma] = clk; + + /* rtc */ + clk = tegra_clk_register_periph_gate("rtc", "clk_32k", + TEGRA_PERIPH_ON_APB | + TEGRA_PERIPH_NO_RESET, clk_base, + 0, 4, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "rtc-tegra"); + clks[rtc] = clk; + + /* kbc */ + clk = tegra_clk_register_periph_gate("kbc", "clk_32k", + TEGRA_PERIPH_ON_APB | + TEGRA_PERIPH_NO_RESET, clk_base, + 0, 36, &periph_h_regs, + periph_clk_enb_refcnt); + clks[kbc] = clk; + + /* timer */ + clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base, + 0, 5, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "timer"); + clks[timer] = clk; + + /* kfuse */ + clk = tegra_clk_register_periph_gate("kfuse", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 40, + &periph_h_regs, periph_clk_enb_refcnt); + clks[kfuse] = clk; + + /* fuse */ + clk = tegra_clk_register_periph_gate("fuse", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 39, + &periph_h_regs, periph_clk_enb_refcnt); + clks[fuse] = clk; + + /* fuse_burn */ + clk = tegra_clk_register_periph_gate("fuse_burn", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 39, + &periph_h_regs, periph_clk_enb_refcnt); + clks[fuse_burn] = clk; + + /* apbif */ + clk = tegra_clk_register_periph_gate("apbif", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 107, + &periph_v_regs, periph_clk_enb_refcnt); + clks[apbif] = clk; + + /* hda2hdmi */ + clk = tegra_clk_register_periph_gate("hda2hdmi", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 128, + &periph_w_regs, periph_clk_enb_refcnt); + clks[hda2hdmi] = clk; + + /* vcp */ + clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0, clk_base, 0, + 29, &periph_l_regs, + periph_clk_enb_refcnt); + clks[vcp] = clk; + + /* bsea */ + clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0, clk_base, + 0, 62, &periph_h_regs, + periph_clk_enb_refcnt); + clks[bsea] = clk; + + /* bsev */ + clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0, clk_base, + 0, 63, &periph_h_regs, + periph_clk_enb_refcnt); + clks[bsev] = clk; + + /* mipi-cal */ + clk = tegra_clk_register_periph_gate("mipi-cal", "clk_m", 0, clk_base, + 0, 56, &periph_h_regs, + periph_clk_enb_refcnt); + clks[mipi_cal] = clk; + + /* usbd */ + clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base, + 0, 22, &periph_l_regs, + periph_clk_enb_refcnt); + clks[usbd] = clk; + + /* usb2 */ + clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base, + 0, 58, &periph_h_regs, + periph_clk_enb_refcnt); + clks[usb2] = clk; + + /* usb3 */ + clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base, + 0, 59, &periph_h_regs, + periph_clk_enb_refcnt); + clks[usb3] = clk; + + /* csi */ + clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base, + 0, 52, &periph_h_regs, + periph_clk_enb_refcnt); + clks[csi] = clk; + + /* isp */ + clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0, + 23, &periph_l_regs, + periph_clk_enb_refcnt); + clks[isp] = clk; + + /* csus */ + clk = tegra_clk_register_periph_gate("csus", "clk_m", + TEGRA_PERIPH_NO_RESET, clk_base, 0, 92, + &periph_u_regs, periph_clk_enb_refcnt); + clks[csus] = clk; + + /* dds */ + clk = tegra_clk_register_periph_gate("dds", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 150, + &periph_w_regs, periph_clk_enb_refcnt); + clks[dds] = clk; + + /* dp2 */ + clk = tegra_clk_register_periph_gate("dp2", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 152, + &periph_w_regs, periph_clk_enb_refcnt); + clks[dp2] = clk; + + /* dtv */ + clk = tegra_clk_register_periph_gate("dtv", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 79, + &periph_u_regs, periph_clk_enb_refcnt); + clks[dtv] = clk; + + /* dsia */ + clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + clk_base + PLLD_BASE, 25, 1, 0, &pll_d_lock); + clks[dsia_mux] = clk; + clk = tegra_clk_register_periph_gate("dsia", "dsia_mux", 0, clk_base, + 0, 48, &periph_h_regs, + periph_clk_enb_refcnt); + clks[dsia] = clk; + + /* dsib */ + clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + clk_base + PLLD2_BASE, 25, 1, 0, &pll_d2_lock); + clks[dsib_mux] = clk; + clk = tegra_clk_register_periph_gate("dsib", "dsib_mux", 0, clk_base, + 0, 82, &periph_u_regs, + periph_clk_enb_refcnt); + clks[dsib] = clk; + + /* xusb_hs_src */ + val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC); + val |= BIT(25); /* always select PLLU_60M */ + writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC); + + clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0, + 1, 1); + clks[xusb_hs_src] = clk; + + /* xusb_host */ + clk = tegra_clk_register_periph_gate("xusb_host", "xusb_host_src", 0, + clk_base, 0, 89, &periph_u_regs, + periph_clk_enb_refcnt); + clks[xusb_host] = clk; + + /* xusb_ss */ + clk = tegra_clk_register_periph_gate("xusb_ss", "xusb_ss_src", 0, + clk_base, 0, 156, &periph_w_regs, + periph_clk_enb_refcnt); + clks[xusb_host] = clk; + + /* xusb_dev */ + clk = tegra_clk_register_periph_gate("xusb_dev", "xusb_dev_src", 0, + clk_base, 0, 95, &periph_u_regs, + periph_clk_enb_refcnt); + clks[xusb_dev] = clk; + + /* emc */ + clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, + ARRAY_SIZE(mux_pllmcp_clkm), 0, + clk_base + CLK_SOURCE_EMC, + 29, 3, 0, NULL); + clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, + CLK_IGNORE_UNUSED, 57, &periph_h_regs, + periph_clk_enb_refcnt); + clks[emc] = clk; + + for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { + data = &tegra_periph_clk_list[i]; + clk = tegra_clk_register_periph(data->name, data->parent_names, + data->num_parents, &data->periph, + clk_base, data->offset, data->flags); + clks[data->clk_id] = clk; + } + + for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) { + data = &tegra_periph_nodiv_clk_list[i]; + clk = tegra_clk_register_periph_nodiv(data->name, + data->parent_names, data->num_parents, + &data->periph, clk_base, data->offset); + clks[data->clk_id] = clk; + } +} + +static struct tegra_cpu_car_ops tegra114_cpu_car_ops; + +static const struct of_device_id pmc_match[] __initconst = { + { .compatible = "nvidia,tegra114-pmc" }, + {}, +}; + +static __initdata struct tegra_clk_init_table init_table[] = { + {uarta, pll_p, 408000000, 0}, + {uartb, pll_p, 408000000, 0}, + {uartc, pll_p, 408000000, 0}, + {uartd, pll_p, 408000000, 0}, + {pll_a, clk_max, 564480000, 1}, + {pll_a_out0, clk_max, 11289600, 1}, + {extern1, pll_a_out0, 0, 1}, + {clk_out_1_mux, extern1, 0, 1}, + {clk_out_1, clk_max, 0, 1}, + {i2s0, pll_a_out0, 11289600, 0}, + {i2s1, pll_a_out0, 11289600, 0}, + {i2s2, pll_a_out0, 11289600, 0}, + {i2s3, pll_a_out0, 11289600, 0}, + {i2s4, pll_a_out0, 11289600, 0}, + {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ +}; + +static void __init tegra114_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + +void __init tegra114_clock_init(struct device_node *np) +{ + struct device_node *node; + int i; + + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("ioremap tegra114 CAR failed\n"); + return; + } + + node = of_find_matching_node(NULL, pmc_match); + if (!node) { + pr_err("Failed to find pmc node\n"); + WARN_ON(1); + return; + } + + pmc_base = of_iomap(node, 0); + if (!pmc_base) { + pr_err("Can't map pmc registers\n"); + WARN_ON(1); + return; + } + + if (tegra114_osc_clk_init(clk_base) < 0) + return; + + tegra114_fixed_clk_init(clk_base); + tegra114_pll_init(clk_base, pmc_base); + tegra114_periph_clk_init(clk_base); + tegra114_audio_clk_init(clk_base); + tegra114_pmc_clk_init(pmc_base); + tegra114_super_clk_init(clk_base); + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + if (IS_ERR(clks[i])) { + pr_err + ("Tegra114 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + } + if (!clks[i]) + clks[i] = ERR_PTR(-EINVAL); + } + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + tegra_clk_apply_init_table = tegra114_clock_apply_init_table; + + tegra_cpu_car_ops = &tegra114_cpu_car_ops; +} diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 1e2de730536..075db0c99ed 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -86,8 +86,8 @@ #define PLLE_BASE 0xe8 #define PLLE_MISC 0xec -#define PLL_BASE_LOCK 27 -#define PLLE_MISC_LOCK 11 +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) #define PLL_MISC_LOCK_ENABLE 18 #define PLLDU_MISC_LOCK_ENABLE 22 @@ -236,7 +236,7 @@ enum tegra20_clk { dvc, dsi, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2, usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, pex, owr, afi, csite, pcie_xclk, avpucq = 75, la, irama = 84, iramb, - iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev1, cdev2, + iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev2, cdev1, uartb = 96, vfir, spdif_in, spdif_out, vi, vi_sensor, tvo, cve, osc, clk_32k, clk_m, sclk, cclk, hclk, pclk, blink, pll_a, pll_a_out0, pll_c, pll_c_out1, pll_d, pll_d_out0, pll_e, pll_m, pll_m_out1, @@ -248,125 +248,125 @@ static struct clk *clks[clk_max]; static struct clk_onecell_data clk_data; static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { - { 12000000, 600000000, 600, 12, 1, 8 }, - { 13000000, 600000000, 600, 13, 1, 8 }, - { 19200000, 600000000, 500, 16, 1, 6 }, - { 26000000, 600000000, 600, 26, 1, 8 }, + { 12000000, 600000000, 600, 12, 0, 8 }, + { 13000000, 600000000, 600, 13, 0, 8 }, + { 19200000, 600000000, 500, 16, 0, 6 }, + { 26000000, 600000000, 600, 26, 0, 8 }, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { - { 12000000, 666000000, 666, 12, 1, 8}, - { 13000000, 666000000, 666, 13, 1, 8}, - { 19200000, 666000000, 555, 16, 1, 8}, - { 26000000, 666000000, 666, 26, 1, 8}, - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, + { 12000000, 666000000, 666, 12, 0, 8}, + { 13000000, 666000000, 666, 13, 0, 8}, + { 19200000, 666000000, 555, 16, 0, 8}, + { 26000000, 666000000, 666, 26, 0, 8}, + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { - { 12000000, 216000000, 432, 12, 2, 8}, - { 13000000, 216000000, 432, 13, 2, 8}, - { 19200000, 216000000, 90, 4, 2, 1}, - { 26000000, 216000000, 432, 26, 2, 8}, - { 12000000, 432000000, 432, 12, 1, 8}, - { 13000000, 432000000, 432, 13, 1, 8}, - { 19200000, 432000000, 90, 4, 1, 1}, - { 26000000, 432000000, 432, 26, 1, 8}, + { 12000000, 216000000, 432, 12, 1, 8}, + { 13000000, 216000000, 432, 13, 1, 8}, + { 19200000, 216000000, 90, 4, 1, 1}, + { 26000000, 216000000, 432, 26, 1, 8}, + { 12000000, 432000000, 432, 12, 0, 8}, + { 13000000, 432000000, 432, 13, 0, 8}, + { 19200000, 432000000, 90, 4, 0, 1}, + { 26000000, 432000000, 432, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { - { 28800000, 56448000, 49, 25, 1, 1}, - { 28800000, 73728000, 64, 25, 1, 1}, - { 28800000, 24000000, 5, 6, 1, 1}, + { 28800000, 56448000, 49, 25, 0, 1}, + { 28800000, 73728000, 64, 25, 0, 1}, + { 28800000, 24000000, 5, 6, 0, 1}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { - { 12000000, 216000000, 216, 12, 1, 4}, - { 13000000, 216000000, 216, 13, 1, 4}, - { 19200000, 216000000, 135, 12, 1, 3}, - { 26000000, 216000000, 216, 26, 1, 4}, + { 12000000, 216000000, 216, 12, 0, 4}, + { 13000000, 216000000, 216, 13, 0, 4}, + { 19200000, 216000000, 135, 12, 0, 3}, + { 26000000, 216000000, 216, 26, 0, 4}, - { 12000000, 594000000, 594, 12, 1, 8}, - { 13000000, 594000000, 594, 13, 1, 8}, - { 19200000, 594000000, 495, 16, 1, 8}, - { 26000000, 594000000, 594, 26, 1, 8}, + { 12000000, 594000000, 594, 12, 0, 8}, + { 13000000, 594000000, 594, 13, 0, 8}, + { 19200000, 594000000, 495, 16, 0, 8}, + { 26000000, 594000000, 594, 26, 0, 8}, - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { - { 12000000, 480000000, 960, 12, 2, 0}, - { 13000000, 480000000, 960, 13, 2, 0}, - { 19200000, 480000000, 200, 4, 2, 0}, - { 26000000, 480000000, 960, 26, 2, 0}, + { 12000000, 480000000, 960, 12, 0, 0}, + { 13000000, 480000000, 960, 13, 0, 0}, + { 19200000, 480000000, 200, 4, 0, 0}, + { 26000000, 480000000, 960, 26, 0, 0}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { /* 1 GHz */ - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, /* 912 MHz */ - { 12000000, 912000000, 912, 12, 1, 12}, - { 13000000, 912000000, 912, 13, 1, 12}, - { 19200000, 912000000, 760, 16, 1, 8}, - { 26000000, 912000000, 912, 26, 1, 12}, + { 12000000, 912000000, 912, 12, 0, 12}, + { 13000000, 912000000, 912, 13, 0, 12}, + { 19200000, 912000000, 760, 16, 0, 8}, + { 26000000, 912000000, 912, 26, 0, 12}, /* 816 MHz */ - { 12000000, 816000000, 816, 12, 1, 12}, - { 13000000, 816000000, 816, 13, 1, 12}, - { 19200000, 816000000, 680, 16, 1, 8}, - { 26000000, 816000000, 816, 26, 1, 12}, + { 12000000, 816000000, 816, 12, 0, 12}, + { 13000000, 816000000, 816, 13, 0, 12}, + { 19200000, 816000000, 680, 16, 0, 8}, + { 26000000, 816000000, 816, 26, 0, 12}, /* 760 MHz */ - { 12000000, 760000000, 760, 12, 1, 12}, - { 13000000, 760000000, 760, 13, 1, 12}, - { 19200000, 760000000, 950, 24, 1, 8}, - { 26000000, 760000000, 760, 26, 1, 12}, + { 12000000, 760000000, 760, 12, 0, 12}, + { 13000000, 760000000, 760, 13, 0, 12}, + { 19200000, 760000000, 950, 24, 0, 8}, + { 26000000, 760000000, 760, 26, 0, 12}, /* 750 MHz */ - { 12000000, 750000000, 750, 12, 1, 12}, - { 13000000, 750000000, 750, 13, 1, 12}, - { 19200000, 750000000, 625, 16, 1, 8}, - { 26000000, 750000000, 750, 26, 1, 12}, + { 12000000, 750000000, 750, 12, 0, 12}, + { 13000000, 750000000, 750, 13, 0, 12}, + { 19200000, 750000000, 625, 16, 0, 8}, + { 26000000, 750000000, 750, 26, 0, 12}, /* 608 MHz */ - { 12000000, 608000000, 608, 12, 1, 12}, - { 13000000, 608000000, 608, 13, 1, 12}, - { 19200000, 608000000, 380, 12, 1, 8}, - { 26000000, 608000000, 608, 26, 1, 12}, + { 12000000, 608000000, 608, 12, 0, 12}, + { 13000000, 608000000, 608, 13, 0, 12}, + { 19200000, 608000000, 380, 12, 0, 8}, + { 26000000, 608000000, 608, 26, 0, 12}, /* 456 MHz */ - { 12000000, 456000000, 456, 12, 1, 12}, - { 13000000, 456000000, 456, 13, 1, 12}, - { 19200000, 456000000, 380, 16, 1, 8}, - { 26000000, 456000000, 456, 26, 1, 12}, + { 12000000, 456000000, 456, 12, 0, 12}, + { 13000000, 456000000, 456, 13, 0, 12}, + { 19200000, 456000000, 380, 16, 0, 8}, + { 26000000, 456000000, 456, 26, 0, 12}, /* 312 MHz */ - { 12000000, 312000000, 312, 12, 1, 12}, - { 13000000, 312000000, 312, 13, 1, 12}, - { 19200000, 312000000, 260, 16, 1, 8}, - { 26000000, 312000000, 312, 26, 1, 12}, + { 12000000, 312000000, 312, 12, 0, 12}, + { 13000000, 312000000, 312, 13, 0, 12}, + { 19200000, 312000000, 260, 16, 0, 8}, + { 26000000, 312000000, 312, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { - { 12000000, 100000000, 200, 24, 1, 0 }, + { 12000000, 100000000, 200, 24, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, }; @@ -380,7 +380,7 @@ static struct tegra_clk_pll_params pll_c_params = { .vco_max = 1400000000, .base_reg = PLLC_BASE, .misc_reg = PLLC_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -394,7 +394,7 @@ static struct tegra_clk_pll_params pll_m_params = { .vco_max = 1200000000, .base_reg = PLLM_BASE, .misc_reg = PLLM_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -408,7 +408,7 @@ static struct tegra_clk_pll_params pll_p_params = { .vco_max = 1400000000, .base_reg = PLLP_BASE, .misc_reg = PLLP_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -422,7 +422,7 @@ static struct tegra_clk_pll_params pll_a_params = { .vco_max = 1400000000, .base_reg = PLLA_BASE, .misc_reg = PLLA_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -436,11 +436,17 @@ static struct tegra_clk_pll_params pll_d_params = { .vco_max = 1000000000, .base_reg = PLLD_BASE, .misc_reg = PLLD_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + static struct tegra_clk_pll_params pll_u_params = { .input_min = 2000000, .input_max = 40000000, @@ -450,9 +456,10 @@ static struct tegra_clk_pll_params pll_u_params = { .vco_max = 960000000, .base_reg = PLLU_BASE, .misc_reg = PLLU_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .pdiv_tohw = pllu_p, }; static struct tegra_clk_pll_params pll_x_params = { @@ -464,7 +471,7 @@ static struct tegra_clk_pll_params pll_x_params = { .vco_max = 1200000000, .base_reg = PLLX_BASE, .misc_reg = PLLX_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -478,7 +485,7 @@ static struct tegra_clk_pll_params pll_e_params = { .vco_max = 0, .base_reg = PLLE_BASE, .misc_reg = PLLE_MISC, - .lock_bit_idx = PLLE_MISC_LOCK, + .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 0, }; @@ -703,7 +710,7 @@ static void tegra20_pll_init(void) clks[pll_a_out0] = clk; /* PLLE */ - clk = tegra_clk_register_plle("pll_e", "pll_ref", clk_base, NULL, + clk = tegra_clk_register_plle("pll_e", "pll_ref", clk_base, pmc_base, 0, 100000000, &pll_e_params, 0, pll_e_freq_table, NULL); clk_register_clkdev(clk, "pll_e", NULL); @@ -711,8 +718,8 @@ static void tegra20_pll_init(void) } static const char *cclk_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", - "pll_p_cclk", "pll_p_out4_cclk", - "pll_p_out3_cclk", "clk_d", "pll_x" }; + "pll_p", "pll_p_out4", + "pll_p_out3", "clk_d", "pll_x" }; static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", "pll_p_out3", "pll_p_out2", "clk_d", "clk_32k", "pll_m_out1" }; @@ -721,38 +728,6 @@ static void tegra20_super_clk_init(void) { struct clk *clk; - /* - * DIV_U71 dividers for CCLK, these dividers are used only - * if parent clock is fixed rate. - */ - - /* - * Clock input to cclk divided from pll_p using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_cclk", "pll_p", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_cclk", NULL); - - /* - * Clock input to cclk divided from pll_p_out3 using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_out3_cclk", "pll_p_out3", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_out3_cclk", NULL); - - /* - * Clock input to cclk divided from pll_p_out4 using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_out4_cclk", "pll_p_out4", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_out4_cclk", NULL); - /* CCLK */ clk = tegra_clk_register_super_mux("cclk", cclk_parents, ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT, @@ -897,6 +872,14 @@ static void __init tegra20_periph_clk_init(void) struct clk *clk; int i; + /* ac97 */ + clk = tegra_clk_register_periph_gate("ac97", "pll_a_out0", + TEGRA_PERIPH_ON_APB, + clk_base, 0, 3, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra20-ac97"); + clks[ac97] = clk; + /* apbdma */ clk = tegra_clk_register_periph_gate("apbdma", "pclk", 0, clk_base, 0, 34, &periph_h_regs, @@ -1044,7 +1027,7 @@ static void __init tegra20_periph_clk_init(void) data = &tegra_periph_clk_list[i]; clk = tegra_clk_register_periph(data->name, data->parent_names, data->num_parents, &data->periph, - clk_base, data->offset); + clk_base, data->offset, data->flags); clk_register_clkdev(clk, data->con_id, data->dev_id); clks[data->clk_id] = clk; } @@ -1259,9 +1242,6 @@ static __initdata struct tegra_clk_init_table init_table[] = { {uartc, pll_p, 0, 0}, {uartd, pll_p, 0, 0}, {uarte, pll_p, 0, 0}, - {usbd, clk_max, 12000000, 0}, - {usb2, clk_max, 12000000, 0}, - {usb3, clk_max, 12000000, 0}, {pll_a, clk_max, 56448000, 1}, {pll_a_out0, clk_max, 11289600, 1}, {cdev1, clk_max, 0, 1}, @@ -1279,9 +1259,16 @@ static __initdata struct tegra_clk_init_table init_table[] = { {host1x, pll_c, 150000000, 0}, {disp1, pll_p, 600000000, 0}, {disp2, pll_p, 600000000, 0}, + {gr2d, pll_c, 300000000, 0}, + {gr3d, pll_c, 300000000, 0}, {clk_max, clk_max, 0, 0}, /* This MUST be the last entry */ }; +static void __init tegra20_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + /* * Some clocks may be used by different drivers depending on the board * configuration. List those here to register them twice in the clock lookup @@ -1348,7 +1335,7 @@ void __init tegra20_clock_init(struct device_node *np) clk_data.clk_num = ARRAY_SIZE(clks); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - tegra_init_from_table(init_table, clks, clk_max); + tegra_clk_apply_init_table = tegra20_clock_apply_init_table; tegra_cpu_car_ops = &tegra20_cpu_car_ops; } diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index ba6f51bc9f3..c6921f538e2 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -22,8 +22,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/clk/tegra.h> - -#include <mach/powergate.h> +#include <linux/tegra-powergate.h> #include "clk.h" @@ -116,8 +115,8 @@ #define PLLDU_MISC_LOCK_ENABLE 22 #define PLLE_MISC_LOCK_ENABLE 9 -#define PLL_BASE_LOCK 27 -#define PLLE_MISC_LOCK 11 +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) #define PLLE_AUX 0x48c #define PLLC_OUT 0x84 @@ -330,7 +329,7 @@ enum tegra30_clk { usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, pcie, owr, afi, csite, pciex, avpucq, la, dtv = 79, ndspeed, i2cslow, dsib, irama = 84, iramb, iramc, iramd, cram2, audio_2x = 90, csus = 92, - cdev1, cdev2, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4, + cdev2, cdev1, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4, i2c4, sbc5, sbc6, d_audio, apbif, dam0, dam1, dam2, hda2codec_2x, atomics, audio0_2x, audio1_2x, audio2_2x, audio3_2x, audio4_2x, spdif_2x, actmon, extern1, extern2, extern3, sata_oob, sata, hda, @@ -374,164 +373,170 @@ static const struct utmi_clk_param utmi_parameters[] = { }; static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { - { 12000000, 1040000000, 520, 6, 1, 8}, - { 13000000, 1040000000, 480, 6, 1, 8}, - { 16800000, 1040000000, 495, 8, 1, 8}, /* actual: 1039.5 MHz */ - { 19200000, 1040000000, 325, 6, 1, 6}, - { 26000000, 1040000000, 520, 13, 1, 8}, - - { 12000000, 832000000, 416, 6, 1, 8}, - { 13000000, 832000000, 832, 13, 1, 8}, - { 16800000, 832000000, 396, 8, 1, 8}, /* actual: 831.6 MHz */ - { 19200000, 832000000, 260, 6, 1, 8}, - { 26000000, 832000000, 416, 13, 1, 8}, - - { 12000000, 624000000, 624, 12, 1, 8}, - { 13000000, 624000000, 624, 13, 1, 8}, - { 16800000, 600000000, 520, 14, 1, 8}, - { 19200000, 624000000, 520, 16, 1, 8}, - { 26000000, 624000000, 624, 26, 1, 8}, - - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 16800000, 600000000, 500, 14, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, - - { 12000000, 520000000, 520, 12, 1, 8}, - { 13000000, 520000000, 520, 13, 1, 8}, - { 16800000, 520000000, 495, 16, 1, 8}, /* actual: 519.75 MHz */ - { 19200000, 520000000, 325, 12, 1, 6}, - { 26000000, 520000000, 520, 26, 1, 8}, - - { 12000000, 416000000, 416, 12, 1, 8}, - { 13000000, 416000000, 416, 13, 1, 8}, - { 16800000, 416000000, 396, 16, 1, 8}, /* actual: 415.8 MHz */ - { 19200000, 416000000, 260, 12, 1, 6}, - { 26000000, 416000000, 416, 26, 1, 8}, + { 12000000, 1040000000, 520, 6, 0, 8}, + { 13000000, 1040000000, 480, 6, 0, 8}, + { 16800000, 1040000000, 495, 8, 0, 8}, /* actual: 1039.5 MHz */ + { 19200000, 1040000000, 325, 6, 0, 6}, + { 26000000, 1040000000, 520, 13, 0, 8}, + + { 12000000, 832000000, 416, 6, 0, 8}, + { 13000000, 832000000, 832, 13, 0, 8}, + { 16800000, 832000000, 396, 8, 0, 8}, /* actual: 831.6 MHz */ + { 19200000, 832000000, 260, 6, 0, 8}, + { 26000000, 832000000, 416, 13, 0, 8}, + + { 12000000, 624000000, 624, 12, 0, 8}, + { 13000000, 624000000, 624, 13, 0, 8}, + { 16800000, 600000000, 520, 14, 0, 8}, + { 19200000, 624000000, 520, 16, 0, 8}, + { 26000000, 624000000, 624, 26, 0, 8}, + + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 16800000, 600000000, 500, 14, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, + + { 12000000, 520000000, 520, 12, 0, 8}, + { 13000000, 520000000, 520, 13, 0, 8}, + { 16800000, 520000000, 495, 16, 0, 8}, /* actual: 519.75 MHz */ + { 19200000, 520000000, 325, 12, 0, 6}, + { 26000000, 520000000, 520, 26, 0, 8}, + + { 12000000, 416000000, 416, 12, 0, 8}, + { 13000000, 416000000, 416, 13, 0, 8}, + { 16800000, 416000000, 396, 16, 0, 8}, /* actual: 415.8 MHz */ + { 19200000, 416000000, 260, 12, 0, 6}, + { 26000000, 416000000, 416, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { - { 12000000, 666000000, 666, 12, 1, 8}, - { 13000000, 666000000, 666, 13, 1, 8}, - { 16800000, 666000000, 555, 14, 1, 8}, - { 19200000, 666000000, 555, 16, 1, 8}, - { 26000000, 666000000, 666, 26, 1, 8}, - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 16800000, 600000000, 500, 14, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, + { 12000000, 666000000, 666, 12, 0, 8}, + { 13000000, 666000000, 666, 13, 0, 8}, + { 16800000, 666000000, 555, 14, 0, 8}, + { 19200000, 666000000, 555, 16, 0, 8}, + { 26000000, 666000000, 666, 26, 0, 8}, + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 16800000, 600000000, 500, 14, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { - { 12000000, 216000000, 432, 12, 2, 8}, - { 13000000, 216000000, 432, 13, 2, 8}, - { 16800000, 216000000, 360, 14, 2, 8}, - { 19200000, 216000000, 360, 16, 2, 8}, - { 26000000, 216000000, 432, 26, 2, 8}, + { 12000000, 216000000, 432, 12, 1, 8}, + { 13000000, 216000000, 432, 13, 1, 8}, + { 16800000, 216000000, 360, 14, 1, 8}, + { 19200000, 216000000, 360, 16, 1, 8}, + { 26000000, 216000000, 432, 26, 1, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { - { 9600000, 564480000, 294, 5, 1, 4}, - { 9600000, 552960000, 288, 5, 1, 4}, - { 9600000, 24000000, 5, 2, 1, 1}, + { 9600000, 564480000, 294, 5, 0, 4}, + { 9600000, 552960000, 288, 5, 0, 4}, + { 9600000, 24000000, 5, 2, 0, 1}, - { 28800000, 56448000, 49, 25, 1, 1}, - { 28800000, 73728000, 64, 25, 1, 1}, - { 28800000, 24000000, 5, 6, 1, 1}, + { 28800000, 56448000, 49, 25, 0, 1}, + { 28800000, 73728000, 64, 25, 0, 1}, + { 28800000, 24000000, 5, 6, 0, 1}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { - { 12000000, 216000000, 216, 12, 1, 4}, - { 13000000, 216000000, 216, 13, 1, 4}, - { 16800000, 216000000, 180, 14, 1, 4}, - { 19200000, 216000000, 180, 16, 1, 4}, - { 26000000, 216000000, 216, 26, 1, 4}, - - { 12000000, 594000000, 594, 12, 1, 8}, - { 13000000, 594000000, 594, 13, 1, 8}, - { 16800000, 594000000, 495, 14, 1, 8}, - { 19200000, 594000000, 495, 16, 1, 8}, - { 26000000, 594000000, 594, 26, 1, 8}, - - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 216000000, 216, 12, 0, 4}, + { 13000000, 216000000, 216, 13, 0, 4}, + { 16800000, 216000000, 180, 14, 0, 4}, + { 19200000, 216000000, 180, 16, 0, 4}, + { 26000000, 216000000, 216, 26, 0, 4}, + + { 12000000, 594000000, 594, 12, 0, 8}, + { 13000000, 594000000, 594, 13, 0, 8}, + { 16800000, 594000000, 495, 14, 0, 8}, + { 19200000, 594000000, 495, 16, 0, 8}, + { 26000000, 594000000, 594, 26, 0, 8}, + + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { - { 12000000, 480000000, 960, 12, 2, 12}, - { 13000000, 480000000, 960, 13, 2, 12}, - { 16800000, 480000000, 400, 7, 2, 5}, - { 19200000, 480000000, 200, 4, 2, 3}, - { 26000000, 480000000, 960, 26, 2, 12}, + { 12000000, 480000000, 960, 12, 0, 12}, + { 13000000, 480000000, 960, 13, 0, 12}, + { 16800000, 480000000, 400, 7, 0, 5}, + { 19200000, 480000000, 200, 4, 0, 3}, + { 26000000, 480000000, 960, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { /* 1.7 GHz */ - { 12000000, 1700000000, 850, 6, 1, 8}, - { 13000000, 1700000000, 915, 7, 1, 8}, /* actual: 1699.2 MHz */ - { 16800000, 1700000000, 708, 7, 1, 8}, /* actual: 1699.2 MHz */ - { 19200000, 1700000000, 885, 10, 1, 8}, /* actual: 1699.2 MHz */ - { 26000000, 1700000000, 850, 13, 1, 8}, + { 12000000, 1700000000, 850, 6, 0, 8}, + { 13000000, 1700000000, 915, 7, 0, 8}, /* actual: 1699.2 MHz */ + { 16800000, 1700000000, 708, 7, 0, 8}, /* actual: 1699.2 MHz */ + { 19200000, 1700000000, 885, 10, 0, 8}, /* actual: 1699.2 MHz */ + { 26000000, 1700000000, 850, 13, 0, 8}, /* 1.6 GHz */ - { 12000000, 1600000000, 800, 6, 1, 8}, - { 13000000, 1600000000, 738, 6, 1, 8}, /* actual: 1599.0 MHz */ - { 16800000, 1600000000, 857, 9, 1, 8}, /* actual: 1599.7 MHz */ - { 19200000, 1600000000, 500, 6, 1, 8}, - { 26000000, 1600000000, 800, 13, 1, 8}, + { 12000000, 1600000000, 800, 6, 0, 8}, + { 13000000, 1600000000, 738, 6, 0, 8}, /* actual: 1599.0 MHz */ + { 16800000, 1600000000, 857, 9, 0, 8}, /* actual: 1599.7 MHz */ + { 19200000, 1600000000, 500, 6, 0, 8}, + { 26000000, 1600000000, 800, 13, 0, 8}, /* 1.5 GHz */ - { 12000000, 1500000000, 750, 6, 1, 8}, - { 13000000, 1500000000, 923, 8, 1, 8}, /* actual: 1499.8 MHz */ - { 16800000, 1500000000, 625, 7, 1, 8}, - { 19200000, 1500000000, 625, 8, 1, 8}, - { 26000000, 1500000000, 750, 13, 1, 8}, + { 12000000, 1500000000, 750, 6, 0, 8}, + { 13000000, 1500000000, 923, 8, 0, 8}, /* actual: 1499.8 MHz */ + { 16800000, 1500000000, 625, 7, 0, 8}, + { 19200000, 1500000000, 625, 8, 0, 8}, + { 26000000, 1500000000, 750, 13, 0, 8}, /* 1.4 GHz */ - { 12000000, 1400000000, 700, 6, 1, 8}, - { 13000000, 1400000000, 969, 9, 1, 8}, /* actual: 1399.7 MHz */ - { 16800000, 1400000000, 1000, 12, 1, 8}, - { 19200000, 1400000000, 875, 12, 1, 8}, - { 26000000, 1400000000, 700, 13, 1, 8}, + { 12000000, 1400000000, 700, 6, 0, 8}, + { 13000000, 1400000000, 969, 9, 0, 8}, /* actual: 1399.7 MHz */ + { 16800000, 1400000000, 1000, 12, 0, 8}, + { 19200000, 1400000000, 875, 12, 0, 8}, + { 26000000, 1400000000, 700, 13, 0, 8}, /* 1.3 GHz */ - { 12000000, 1300000000, 975, 9, 1, 8}, - { 13000000, 1300000000, 1000, 10, 1, 8}, - { 16800000, 1300000000, 928, 12, 1, 8}, /* actual: 1299.2 MHz */ - { 19200000, 1300000000, 812, 12, 1, 8}, /* actual: 1299.2 MHz */ - { 26000000, 1300000000, 650, 13, 1, 8}, + { 12000000, 1300000000, 975, 9, 0, 8}, + { 13000000, 1300000000, 1000, 10, 0, 8}, + { 16800000, 1300000000, 928, 12, 0, 8}, /* actual: 1299.2 MHz */ + { 19200000, 1300000000, 812, 12, 0, 8}, /* actual: 1299.2 MHz */ + { 26000000, 1300000000, 650, 13, 0, 8}, /* 1.2 GHz */ - { 12000000, 1200000000, 1000, 10, 1, 8}, - { 13000000, 1200000000, 923, 10, 1, 8}, /* actual: 1199.9 MHz */ - { 16800000, 1200000000, 1000, 14, 1, 8}, - { 19200000, 1200000000, 1000, 16, 1, 8}, - { 26000000, 1200000000, 600, 13, 1, 8}, + { 12000000, 1200000000, 1000, 10, 0, 8}, + { 13000000, 1200000000, 923, 10, 0, 8}, /* actual: 1199.9 MHz */ + { 16800000, 1200000000, 1000, 14, 0, 8}, + { 19200000, 1200000000, 1000, 16, 0, 8}, + { 26000000, 1200000000, 600, 13, 0, 8}, /* 1.1 GHz */ - { 12000000, 1100000000, 825, 9, 1, 8}, - { 13000000, 1100000000, 846, 10, 1, 8}, /* actual: 1099.8 MHz */ - { 16800000, 1100000000, 982, 15, 1, 8}, /* actual: 1099.8 MHz */ - { 19200000, 1100000000, 859, 15, 1, 8}, /* actual: 1099.5 MHz */ - { 26000000, 1100000000, 550, 13, 1, 8}, + { 12000000, 1100000000, 825, 9, 0, 8}, + { 13000000, 1100000000, 846, 10, 0, 8}, /* actual: 1099.8 MHz */ + { 16800000, 1100000000, 982, 15, 0, 8}, /* actual: 1099.8 MHz */ + { 19200000, 1100000000, 859, 15, 0, 8}, /* actual: 1099.5 MHz */ + { 26000000, 1100000000, 550, 13, 0, 8}, /* 1 GHz */ - { 12000000, 1000000000, 1000, 12, 1, 8}, - { 13000000, 1000000000, 1000, 13, 1, 8}, - { 16800000, 1000000000, 833, 14, 1, 8}, /* actual: 999.6 MHz */ - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 8}, + { 12000000, 1000000000, 1000, 12, 0, 8}, + { 13000000, 1000000000, 1000, 13, 0, 8}, + { 16800000, 1000000000, 833, 14, 0, 8}, /* actual: 999.6 MHz */ + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; @@ -553,7 +558,7 @@ static struct tegra_clk_pll_params pll_c_params = { .vco_max = 1400000000, .base_reg = PLLC_BASE, .misc_reg = PLLC_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -567,7 +572,7 @@ static struct tegra_clk_pll_params pll_m_params = { .vco_max = 1200000000, .base_reg = PLLM_BASE, .misc_reg = PLLM_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -581,7 +586,7 @@ static struct tegra_clk_pll_params pll_p_params = { .vco_max = 1400000000, .base_reg = PLLP_BASE, .misc_reg = PLLP_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -595,7 +600,7 @@ static struct tegra_clk_pll_params pll_a_params = { .vco_max = 1400000000, .base_reg = PLLA_BASE, .misc_reg = PLLA_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -609,7 +614,7 @@ static struct tegra_clk_pll_params pll_d_params = { .vco_max = 1000000000, .base_reg = PLLD_BASE, .misc_reg = PLLD_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; @@ -623,7 +628,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .vco_max = 1000000000, .base_reg = PLLD2_BASE, .misc_reg = PLLD2_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; @@ -637,9 +642,10 @@ static struct tegra_clk_pll_params pll_u_params = { .vco_max = 960000000, .base_reg = PLLU_BASE, .misc_reg = PLLU_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .pdiv_tohw = pllu_p, }; static struct tegra_clk_pll_params pll_x_params = { @@ -651,7 +657,7 @@ static struct tegra_clk_pll_params pll_x_params = { .vco_max = 1700000000, .base_reg = PLLX_BASE, .misc_reg = PLLX_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -665,7 +671,7 @@ static struct tegra_clk_pll_params pll_e_params = { .vco_max = 2400000000U, .base_reg = PLLE_BASE, .misc_reg = PLLE_MISC, - .lock_bit_idx = PLLE_MISC_LOCK, + .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -1661,7 +1667,7 @@ static void __init tegra30_periph_clk_init(void) data = &tegra_periph_clk_list[i]; clk = tegra_clk_register_periph(data->name, data->parent_names, data->num_parents, &data->periph, - clk_base, data->offset); + clk_base, data->offset, data->flags); clk_register_clkdev(clk, data->con_id, data->dev_id); clks[data->clk_id] = clk; } @@ -1911,9 +1917,16 @@ static __initdata struct tegra_clk_init_table init_table[] = { {disp1, pll_p, 600000000, 0}, {disp2, pll_p, 600000000, 0}, {twd, clk_max, 0, 1}, + {gr2d, pll_c, 300000000, 0}, + {gr3d, pll_c, 300000000, 0}, {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ }; +static void __init tegra30_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + /* * Some clocks may be used by different drivers depending on the board * configuration. List those here to register them twice in the clock lookup @@ -1987,7 +2000,7 @@ void __init tegra30_clock_init(struct device_node *np) clk_data.clk_num = ARRAY_SIZE(clks); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - tegra_init_from_table(init_table, clks, clk_max); + tegra_clk_apply_init_table = tegra30_clock_apply_init_table; tegra_cpu_car_ops = &tegra30_cpu_car_ops; } diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index a603b9af0ad..923ca7ee469 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -22,7 +22,8 @@ #include "clk.h" /* Global data of Tegra CPU CAR ops */ -struct tegra_cpu_car_ops *tegra_cpu_car_ops; +static struct tegra_cpu_car_ops dummy_car_ops; +struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops; void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, struct clk *clks[], int clk_max) @@ -76,6 +77,7 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, static const struct of_device_id tegra_dt_clk_match[] = { { .compatible = "nvidia,tegra20-car", .data = tegra20_clock_init }, { .compatible = "nvidia,tegra30-car", .data = tegra30_clock_init }, + { .compatible = "nvidia,tegra114-car", .data = tegra114_clock_init }, { } }; @@ -83,3 +85,13 @@ void __init tegra_clocks_init(void) { of_clk_init(tegra_dt_clk_match); } + +tegra_clk_apply_init_table_func tegra_clk_apply_init_table; + +void __init tegra_clocks_apply_init_table(void) +{ + if (!tegra_clk_apply_init_table) + return; + + tegra_clk_apply_init_table(); +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 0744731c622..e0565620d68 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -1,4 +1,4 @@ -/* + /* * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it @@ -117,6 +117,17 @@ struct tegra_clk_pll_freq_table { }; /** + * struct pdiv_map - map post divider to hw value + * + * @pdiv: post divider + * @hw_val: value to be written to the PLL hw + */ +struct pdiv_map { + u8 pdiv; + u8 hw_val; +}; + +/** * struct clk_pll_params - PLL parameters * * @input_min: Minimum input frequency @@ -143,9 +154,18 @@ struct tegra_clk_pll_params { u32 base_reg; u32 misc_reg; u32 lock_reg; - u32 lock_bit_idx; + u32 lock_mask; u32 lock_enable_bit_idx; + u32 iddq_reg; + u32 iddq_bit_idx; + u32 aux_reg; + u32 dyn_ramp_reg; + u32 ext_misc_reg[3]; + int stepa_shift; + int stepb_shift; int lock_delay; + int max_p; + struct pdiv_map *pdiv_tohw; }; /** @@ -182,12 +202,16 @@ struct tegra_clk_pll_params { * TEGRA_PLL_FIXED - We are not supposed to change output frequency * of some plls. * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. + * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the + * base register. + * TEGRA_PLL_BYPASS - PLL has bypass bit + * TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring */ struct tegra_clk_pll { struct clk_hw hw; void __iomem *clk_base; void __iomem *pmc; - u8 flags; + u32 flags; unsigned long fixed_rate; spinlock_t *lock; u8 divn_shift; @@ -210,20 +234,64 @@ struct tegra_clk_pll { #define TEGRA_PLLM BIT(5) #define TEGRA_PLL_FIXED BIT(6) #define TEGRA_PLLE_CONFIGURE BIT(7) +#define TEGRA_PLL_LOCK_MISC BIT(8) +#define TEGRA_PLL_BYPASS BIT(9) +#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10) extern const struct clk_ops tegra_clk_pll_ops; extern const struct clk_ops tegra_clk_plle_ops; struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); + struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); +struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock, unsigned long parent_rate); + +struct clk *tegra_clk_register_plle_tegra114(const char *name, + const char *parent_name, + void __iomem *clk_base, unsigned long flags, + unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + /** * struct tegra_clk_pll_out - PLL divider down clock * @@ -290,6 +358,7 @@ struct tegra_clk_periph_regs { * TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the * bus to flush the write operation in apb bus. This flag indicates * that this peripheral is in apb bus. + * TEGRA_PERIPH_WAR_1005168 - Apply workaround for Tegra114 MSENC bug */ struct tegra_clk_periph_gate { u32 magic; @@ -309,6 +378,7 @@ struct tegra_clk_periph_gate { #define TEGRA_PERIPH_NO_RESET BIT(0) #define TEGRA_PERIPH_MANUAL_RESET BIT(1) #define TEGRA_PERIPH_ON_APB BIT(2) +#define TEGRA_PERIPH_WAR_1005168 BIT(3) void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert); extern const struct clk_ops tegra_clk_periph_gate_ops; @@ -349,21 +419,22 @@ extern const struct clk_ops tegra_clk_periph_ops; struct clk *tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, - u32 offset); + u32 offset, unsigned long flags); struct clk *tegra_clk_register_periph_nodiv(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, u32 offset); -#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags, \ +#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \ _div_shift, _div_width, _div_frac_width, \ _div_flags, _clk_num, _enb_refcnt, _regs, \ - _gate_flags) \ + _gate_flags, _table) \ { \ .mux = { \ .flags = _mux_flags, \ .shift = _mux_shift, \ - .width = _mux_width, \ + .mask = _mux_mask, \ + .table = _table, \ }, \ .divider = { \ .flags = _div_flags, \ @@ -391,28 +462,41 @@ struct tegra_periph_init_data { u32 offset; const char *con_id; const char *dev_id; + unsigned long flags; }; -#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \ - _mux_shift, _mux_width, _mux_flags, _div_shift, \ +#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, _mux_mask, _mux_flags, _div_shift, \ _div_width, _div_frac_width, _div_flags, _regs, \ - _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table,\ + _flags) \ { \ .name = _name, \ .clk_id = _clk_id, \ .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ - .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \ + .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \ _mux_flags, _div_shift, \ _div_width, _div_frac_width, \ _div_flags, _clk_num, \ _enb_refcnt, _regs, \ - _gate_flags), \ + _gate_flags, _table), \ .offset = _offset, \ .con_id = _con_id, \ .dev_id = _dev_id, \ + .flags = _flags \ } +#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, _mux_width, _mux_flags, _div_shift, \ + _div_width, _div_frac_width, _div_flags, _regs, \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, BIT(_mux_width) - 1, _mux_flags, \ + _div_shift, _div_width, _div_frac_width, _div_flags, \ + _regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\ + NULL, 0) + /** * struct clk_super_mux - super clock * @@ -499,4 +583,13 @@ void tegra30_clock_init(struct device_node *np); static inline void tegra30_clock_init(struct device_node *np) {} #endif /* CONFIG_ARCH_TEGRA_3x_SOC */ +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_clock_init(struct device_node *np); +#else +static inline void tegra114_clock_init(struct device_node *np) {} +#endif /* CONFIG_ARCH_TEGRA114_SOC */ + +typedef void (*tegra_clk_apply_init_table_func)(void); +extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; + #endif /* TEGRA_CLK_H */ diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile index bcc0c11a507..c6a806ed0e8 100644 --- a/drivers/clk/ux500/Makefile +++ b/drivers/clk/ux500/Makefile @@ -5,6 +5,7 @@ # Clock types obj-y += clk-prcc.o obj-y += clk-prcmu.o +obj-y += clk-sysctrl.o # Clock definitions obj-y += u8500_clk.o diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c index 9f7400d74fa..a0fca004abc 100644 --- a/drivers/clk/ux500/abx500-clk.c +++ b/drivers/clk/ux500/abx500-clk.c @@ -12,13 +12,78 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/mfd/abx500/ab8500.h> - -/* TODO: Add clock implementations here */ - +#include <linux/mfd/abx500/ab8500-sysctrl.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/mfd/dbx500-prcmu.h> +#include "clk.h" /* Clock definitions for ab8500 */ static int ab8500_reg_clks(struct device *dev) { + int ret; + struct clk *clk; + + const char *intclk_parents[] = {"ab8500_sysclk", "ulpclk"}; + u16 intclk_reg_sel[] = {0 , AB8500_SYSULPCLKCTRL1}; + u8 intclk_reg_mask[] = {0 , AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK}; + u8 intclk_reg_bits[] = { + 0 , + (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT) + }; + + dev_info(dev, "register clocks for ab850x\n"); + + /* Enable SWAT */ + ret = ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE); + if (ret) + return ret; + + /* ab8500_sysclk */ + clk = clk_reg_prcmu_gate("ab8500_sysclk", NULL, PRCMU_SYSCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, "sysclk", "ab8500-usb.0"); + clk_register_clkdev(clk, "sysclk", "ab-iddet.0"); + clk_register_clkdev(clk, "sysclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "sysclk", "shrm_bus"); + + /* ab8500_sysclk2 */ + clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk2", "ab8500_sysclk", + AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, + AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, 0, 0); + clk_register_clkdev(clk, "sysclk", "0-0070"); + + /* ab8500_sysclk3 */ + clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk3", "ab8500_sysclk", + AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, + AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, 0, 0); + clk_register_clkdev(clk, "sysclk", "cg1960_core.0"); + + /* ab8500_sysclk4 */ + clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk4", "ab8500_sysclk", + AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, + AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, 0, 0); + + /* ab_ulpclk */ + clk = clk_reg_sysctrl_gate_fixed_rate(dev, "ulpclk", NULL, + AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, + AB8500_SYSULPCLKCTRL1_ULPCLKREQ, + 38400000, 9000, CLK_IS_ROOT); + clk_register_clkdev(clk, "ulpclk", "ab85xx-codec.0"); + + /* ab8500_intclk */ + clk = clk_reg_sysctrl_set_parent(dev , "intclk", intclk_parents, 2, + intclk_reg_sel, intclk_reg_mask, intclk_reg_bits, 0); + clk_register_clkdev(clk, "intclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, NULL, "ab8500-pwm.1"); + + /* ab8500_audioclk */ + clk = clk_reg_sysctrl_gate(dev , "audioclk", "intclk", + AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, + AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, 0, 0); + clk_register_clkdev(clk, "audioclk", "ab85xx-codec.0"); + return 0; } diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c index 7eee7f76835..bd4769a8448 100644 --- a/drivers/clk/ux500/clk-prcc.c +++ b/drivers/clk/ux500/clk-prcc.c @@ -13,7 +13,6 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/types.h> -#include <mach/hardware.h> #include "clk.h" diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 74faa7e3cf5..293a2885441 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -20,15 +20,23 @@ struct clk_prcmu { struct clk_hw hw; u8 cg_sel; + int is_prepared; int is_enabled; + int opp_requested; }; /* PRCMU clock operations. */ static int clk_prcmu_prepare(struct clk_hw *hw) { + int ret; struct clk_prcmu *clk = to_clk_prcmu(hw); - return prcmu_request_clock(clk->cg_sel, true); + + ret = prcmu_request_clock(clk->cg_sel, true); + if (!ret) + clk->is_prepared = 1; + + return ret;; } static void clk_prcmu_unprepare(struct clk_hw *hw) @@ -36,7 +44,15 @@ static void clk_prcmu_unprepare(struct clk_hw *hw) struct clk_prcmu *clk = to_clk_prcmu(hw); if (prcmu_request_clock(clk->cg_sel, false)) pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + __clk_get_name(hw->clk)); + else + clk->is_prepared = 0; +} + +static int clk_prcmu_is_prepared(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return clk->is_prepared; } static int clk_prcmu_enable(struct clk_hw *hw) @@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, return prcmu_set_clock_rate(clk->cg_sel, rate); } -static int request_ape_opp100(bool enable) -{ - static int reqs; - int err = 0; - - if (enable) { - if (!reqs) - err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, - "clock", 100); - if (!err) - reqs++; - } else { - reqs--; - if (!reqs) - prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, - "clock"); - } - return err; -} - static int clk_prcmu_opp_prepare(struct clk_hw *hw) { int err; struct clk_prcmu *clk = to_clk_prcmu(hw); - err = request_ape_opp100(true); - if (err) { - pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", - __func__, hw->init->name); - return err; + if (!clk->opp_requested) { + err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk), + 100); + if (err) { + pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", + __func__, __clk_get_name(hw->clk)); + return err; + } + clk->opp_requested = 1; } err = prcmu_request_clock(clk->cg_sel, true); - if (err) - request_ape_opp100(false); + if (err) { + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk)); + clk->opp_requested = 0; + return err; + } - return err; + clk->is_prepared = 1; + return 0; } static void clk_prcmu_opp_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) - goto out_error; - if (request_ape_opp100(false)) - goto out_error; - return; - -out_error: - pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + if (prcmu_request_clock(clk->cg_sel, false)) { + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + __clk_get_name(hw->clk)); + return; + } + + if (clk->opp_requested) { + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk)); + clk->opp_requested = 0; + } + + clk->is_prepared = 0; } static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) @@ -138,38 +148,49 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) int err; struct clk_prcmu *clk = to_clk_prcmu(hw); - err = prcmu_request_ape_opp_100_voltage(true); - if (err) { - pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n", - __func__, hw->init->name); - return err; + if (!clk->opp_requested) { + err = prcmu_request_ape_opp_100_voltage(true); + if (err) { + pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", + __func__, __clk_get_name(hw->clk)); + return err; + } + clk->opp_requested = 1; } err = prcmu_request_clock(clk->cg_sel, true); - if (err) + if (err) { prcmu_request_ape_opp_100_voltage(false); + clk->opp_requested = 0; + return err; + } - return err; + clk->is_prepared = 1; + return 0; } static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) - goto out_error; - if (prcmu_request_ape_opp_100_voltage(false)) - goto out_error; - return; - -out_error: - pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + if (prcmu_request_clock(clk->cg_sel, false)) { + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + __clk_get_name(hw->clk)); + return; + } + + if (clk->opp_requested) { + prcmu_request_ape_opp_100_voltage(false); + clk->opp_requested = 0; + } + + clk->is_prepared = 0; } static struct clk_ops clk_prcmu_scalable_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = { static struct clk_ops clk_prcmu_gate_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = { static struct clk_ops clk_prcmu_opp_gate_ops = { .prepare = clk_prcmu_opp_prepare, .unprepare = clk_prcmu_opp_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = { static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { .prepare = clk_prcmu_opp_volt_prepare, .unprepare = clk_prcmu_opp_volt_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name, } clk->cg_sel = cg_sel; + clk->is_prepared = 1; clk->is_enabled = 1; + clk->opp_requested = 0; /* "rate" can be used for changing the initial frequency */ if (rate) prcmu_set_clock_rate(cg_sel, rate); diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c new file mode 100644 index 00000000000..e364c9d4aa6 --- /dev/null +++ b/drivers/clk/ux500/clk-sysctrl.c @@ -0,0 +1,227 @@ +/* + * Sysctrl clock implementation for ux500 platform. + * + * Copyright (C) 2013 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk-provider.h> +#include <linux/mfd/abx500/ab8500-sysctrl.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +#define SYSCTRL_MAX_NUM_PARENTS 4 + +#define to_clk_sysctrl(_hw) container_of(_hw, struct clk_sysctrl, hw) + +struct clk_sysctrl { + struct clk_hw hw; + struct device *dev; + u8 parent_index; + u16 reg_sel[SYSCTRL_MAX_NUM_PARENTS]; + u8 reg_mask[SYSCTRL_MAX_NUM_PARENTS]; + u8 reg_bits[SYSCTRL_MAX_NUM_PARENTS]; + unsigned long rate; + unsigned long enable_delay_us; +}; + +/* Sysctrl clock operations. */ + +static int clk_sysctrl_prepare(struct clk_hw *hw) +{ + int ret; + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + + ret = ab8500_sysctrl_write(clk->reg_sel[0], clk->reg_mask[0], + clk->reg_bits[0]); + + if (!ret && clk->enable_delay_us) + usleep_range(clk->enable_delay_us, clk->enable_delay_us); + + return ret; +} + +static void clk_sysctrl_unprepare(struct clk_hw *hw) +{ + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + if (ab8500_sysctrl_clear(clk->reg_sel[0], clk->reg_mask[0])) + dev_err(clk->dev, "clk_sysctrl: %s fail to clear %s.\n", + __func__, __clk_get_name(hw->clk)); +} + +static unsigned long clk_sysctrl_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + return clk->rate; +} + +static int clk_sysctrl_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + u8 old_index = clk->parent_index; + int ret = 0; + + if (clk->reg_sel[old_index]) { + ret = ab8500_sysctrl_clear(clk->reg_sel[old_index], + clk->reg_mask[old_index]); + if (ret) + return ret; + } + + if (clk->reg_sel[index]) { + ret = ab8500_sysctrl_write(clk->reg_sel[index], + clk->reg_mask[index], + clk->reg_bits[index]); + if (ret) { + if (clk->reg_sel[old_index]) + ab8500_sysctrl_write(clk->reg_sel[old_index], + clk->reg_mask[old_index], + clk->reg_bits[old_index]); + return ret; + } + } + clk->parent_index = index; + + return ret; +} + +static u8 clk_sysctrl_get_parent(struct clk_hw *hw) +{ + struct clk_sysctrl *clk = to_clk_sysctrl(hw); + return clk->parent_index; +} + +static struct clk_ops clk_sysctrl_gate_ops = { + .prepare = clk_sysctrl_prepare, + .unprepare = clk_sysctrl_unprepare, +}; + +static struct clk_ops clk_sysctrl_gate_fixed_rate_ops = { + .prepare = clk_sysctrl_prepare, + .unprepare = clk_sysctrl_unprepare, + .recalc_rate = clk_sysctrl_recalc_rate, +}; + +static struct clk_ops clk_sysctrl_set_parent_ops = { + .set_parent = clk_sysctrl_set_parent, + .get_parent = clk_sysctrl_get_parent, +}; + +static struct clk *clk_reg_sysctrl(struct device *dev, + const char *name, + const char **parent_names, + u8 num_parents, + u16 *reg_sel, + u8 *reg_mask, + u8 *reg_bits, + unsigned long rate, + unsigned long enable_delay_us, + unsigned long flags, + struct clk_ops *clk_sysctrl_ops) +{ + struct clk_sysctrl *clk; + struct clk_init_data clk_sysctrl_init; + struct clk *clk_reg; + int i; + + if (!dev) + return ERR_PTR(-EINVAL); + + if (!name || (num_parents > SYSCTRL_MAX_NUM_PARENTS)) { + dev_err(dev, "clk_sysctrl: invalid arguments passed\n"); + return ERR_PTR(-EINVAL); + } + + clk = devm_kzalloc(dev, sizeof(struct clk_sysctrl), GFP_KERNEL); + if (!clk) { + dev_err(dev, "clk_sysctrl: could not allocate clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* set main clock registers */ + clk->reg_sel[0] = reg_sel[0]; + clk->reg_bits[0] = reg_bits[0]; + clk->reg_mask[0] = reg_mask[0]; + + /* handle clocks with more than one parent */ + for (i = 1; i < num_parents; i++) { + clk->reg_sel[i] = reg_sel[i]; + clk->reg_bits[i] = reg_bits[i]; + clk->reg_mask[i] = reg_mask[i]; + } + + clk->parent_index = 0; + clk->rate = rate; + clk->enable_delay_us = enable_delay_us; + clk->dev = dev; + + clk_sysctrl_init.name = name; + clk_sysctrl_init.ops = clk_sysctrl_ops; + clk_sysctrl_init.flags = flags; + clk_sysctrl_init.parent_names = parent_names; + clk_sysctrl_init.num_parents = num_parents; + clk->hw.init = &clk_sysctrl_init; + + clk_reg = devm_clk_register(clk->dev, &clk->hw); + if (IS_ERR(clk_reg)) + dev_err(dev, "clk_sysctrl: clk_register failed\n"); + + return clk_reg; +} + +struct clk *clk_reg_sysctrl_gate(struct device *dev, + const char *name, + const char *parent_name, + u16 reg_sel, + u8 reg_mask, + u8 reg_bits, + unsigned long enable_delay_us, + unsigned long flags) +{ + const char **parent_names = (parent_name ? &parent_name : NULL); + u8 num_parents = (parent_name ? 1 : 0); + + return clk_reg_sysctrl(dev, name, parent_names, num_parents, + ®_sel, ®_mask, ®_bits, 0, enable_delay_us, + flags, &clk_sysctrl_gate_ops); +} + +struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev, + const char *name, + const char *parent_name, + u16 reg_sel, + u8 reg_mask, + u8 reg_bits, + unsigned long rate, + unsigned long enable_delay_us, + unsigned long flags) +{ + const char **parent_names = (parent_name ? &parent_name : NULL); + u8 num_parents = (parent_name ? 1 : 0); + + return clk_reg_sysctrl(dev, name, parent_names, num_parents, + ®_sel, ®_mask, ®_bits, + rate, enable_delay_us, flags, + &clk_sysctrl_gate_fixed_rate_ops); +} + +struct clk *clk_reg_sysctrl_set_parent(struct device *dev, + const char *name, + const char **parent_names, + u8 num_parents, + u16 *reg_sel, + u8 *reg_mask, + u8 *reg_bits, + unsigned long flags) +{ + return clk_reg_sysctrl(dev, name, parent_names, num_parents, + reg_sel, reg_mask, reg_bits, 0, 0, flags, + &clk_sysctrl_set_parent_ops); +} diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h index c3e449169a8..a2bb92d85ee 100644 --- a/drivers/clk/ux500/clk.h +++ b/drivers/clk/ux500/clk.h @@ -11,16 +11,18 @@ #define __UX500_CLK_H #include <linux/clk.h> +#include <linux/device.h> +#include <linux/types.h> struct clk *clk_reg_prcc_pclk(const char *name, const char *parent_name, - unsigned int phy_base, + resource_size_t phy_base, u32 cg_sel, unsigned long flags); struct clk *clk_reg_prcc_kclk(const char *name, const char *parent_name, - unsigned int phy_base, + resource_size_t phy_base, u32 cg_sel, unsigned long flags); @@ -57,4 +59,32 @@ struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name, unsigned long rate, unsigned long flags); +struct clk *clk_reg_sysctrl_gate(struct device *dev, + const char *name, + const char *parent_name, + u16 reg_sel, + u8 reg_mask, + u8 reg_bits, + unsigned long enable_delay_us, + unsigned long flags); + +struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev, + const char *name, + const char *parent_name, + u16 reg_sel, + u8 reg_mask, + u8 reg_bits, + unsigned long rate, + unsigned long enable_delay_us, + unsigned long flags); + +struct clk *clk_reg_sysctrl_set_parent(struct device *dev, + const char *name, + const char **parent_names, + u8 num_parents, + u16 *reg_sel, + u8 *reg_mask, + u8 *reg_bits, + unsigned long flags); + #endif /* __UX500_CLK_H */ diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c index 6b889a0e90b..80069c370a4 100644 --- a/drivers/clk/ux500/u8500_clk.c +++ b/drivers/clk/ux500/u8500_clk.c @@ -12,10 +12,10 @@ #include <linux/clk-provider.h> #include <linux/mfd/dbx500-prcmu.h> #include <linux/platform_data/clk-ux500.h> -#include <mach/db8500-regs.h> #include "clk.h" -void u8500_clk_init(void) +void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { struct prcmu_fw_version *fw_version; const char *sgaclk_parent = NULL; @@ -215,147 +215,148 @@ void u8500_clk_init(void) */ /* PRCC P-clocks */ - clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", clkrst1_base, BIT(0), 0); clk_register_clkdev(clk, "apb_pclk", "uart0"); - clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", clkrst1_base, BIT(1), 0); clk_register_clkdev(clk, "apb_pclk", "uart1"); - clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", clkrst1_base, BIT(2), 0); clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.1"); - clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", clkrst1_base, BIT(3), 0); clk_register_clkdev(clk, "apb_pclk", "msp0"); clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.0"); - clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", clkrst1_base, BIT(4), 0); clk_register_clkdev(clk, "apb_pclk", "msp1"); clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.1"); - clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", clkrst1_base, BIT(5), 0); clk_register_clkdev(clk, "apb_pclk", "sdi0"); - clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", clkrst1_base, BIT(6), 0); clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.2"); - clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", clkrst1_base, BIT(7), 0); clk_register_clkdev(clk, NULL, "spi3"); - clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", clkrst1_base, BIT(8), 0); clk_register_clkdev(clk, "apb_pclk", "slimbus0"); - clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", clkrst1_base, BIT(9), 0); clk_register_clkdev(clk, NULL, "gpio.0"); clk_register_clkdev(clk, NULL, "gpio.1"); clk_register_clkdev(clk, NULL, "gpioblock0"); - clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", clkrst1_base, BIT(10), 0); clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.4"); - clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", U8500_CLKRST1_BASE, + clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", clkrst1_base, BIT(11), 0); clk_register_clkdev(clk, "apb_pclk", "msp3"); clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.3"); - clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", clkrst2_base, BIT(0), 0); clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.3"); - clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", clkrst2_base, BIT(1), 0); clk_register_clkdev(clk, NULL, "spi2"); - clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", clkrst2_base, BIT(2), 0); clk_register_clkdev(clk, NULL, "spi1"); - clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", clkrst2_base, BIT(3), 0); clk_register_clkdev(clk, NULL, "pwl"); - clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", clkrst2_base, BIT(4), 0); clk_register_clkdev(clk, "apb_pclk", "sdi4"); - clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", clkrst2_base, BIT(5), 0); clk_register_clkdev(clk, "apb_pclk", "msp2"); clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.2"); - clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", clkrst2_base, BIT(6), 0); clk_register_clkdev(clk, "apb_pclk", "sdi1"); - clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", clkrst2_base, BIT(7), 0); clk_register_clkdev(clk, "apb_pclk", "sdi3"); - clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", clkrst2_base, BIT(8), 0); clk_register_clkdev(clk, NULL, "spi0"); - clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", clkrst2_base, BIT(9), 0); clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0"); - clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", clkrst2_base, BIT(10), 0); clk_register_clkdev(clk, "hsit_hclk", "ste_hsi.0"); - clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", clkrst2_base, BIT(11), 0); clk_register_clkdev(clk, NULL, "gpio.6"); clk_register_clkdev(clk, NULL, "gpio.7"); clk_register_clkdev(clk, NULL, "gpioblock1"); - clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", U8500_CLKRST2_BASE, + clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", clkrst2_base, BIT(12), 0); - clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", clkrst3_base, BIT(0), 0); - clk_register_clkdev(clk, NULL, "fsmc"); + clk_register_clkdev(clk, "fsmc", NULL); + clk_register_clkdev(clk, NULL, "smsc911x.0"); - clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", clkrst3_base, BIT(1), 0); clk_register_clkdev(clk, "apb_pclk", "ssp0"); - clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", clkrst3_base, BIT(2), 0); clk_register_clkdev(clk, "apb_pclk", "ssp1"); - clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", clkrst3_base, BIT(3), 0); clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.0"); - clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", clkrst3_base, BIT(4), 0); clk_register_clkdev(clk, "apb_pclk", "sdi2"); - clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", clkrst3_base, BIT(5), 0); clk_register_clkdev(clk, "apb_pclk", "ske"); clk_register_clkdev(clk, "apb_pclk", "nmk-ske-keypad"); - clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", clkrst3_base, BIT(6), 0); clk_register_clkdev(clk, "apb_pclk", "uart2"); - clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", clkrst3_base, BIT(7), 0); clk_register_clkdev(clk, "apb_pclk", "sdi5"); - clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", U8500_CLKRST3_BASE, + clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", clkrst3_base, BIT(8), 0); clk_register_clkdev(clk, NULL, "gpio.2"); clk_register_clkdev(clk, NULL, "gpio.3"); @@ -363,45 +364,45 @@ void u8500_clk_init(void) clk_register_clkdev(clk, NULL, "gpio.5"); clk_register_clkdev(clk, NULL, "gpioblock2"); - clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", U8500_CLKRST5_BASE, + clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", clkrst5_base, BIT(0), 0); clk_register_clkdev(clk, "usb", "musb-ux500.0"); - clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", U8500_CLKRST5_BASE, + clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", clkrst5_base, BIT(1), 0); clk_register_clkdev(clk, NULL, "gpio.8"); clk_register_clkdev(clk, NULL, "gpioblock3"); - clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", U8500_CLKRST6_BASE, + clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", clkrst6_base, BIT(0), 0); clk_register_clkdev(clk, "apb_pclk", "rng"); - clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", U8500_CLKRST6_BASE, + clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", clkrst6_base, BIT(1), 0); clk_register_clkdev(clk, NULL, "cryp0"); clk_register_clkdev(clk, NULL, "cryp1"); - clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", U8500_CLKRST6_BASE, + clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", clkrst6_base, BIT(2), 0); clk_register_clkdev(clk, NULL, "hash0"); - clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", U8500_CLKRST6_BASE, + clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", clkrst6_base, BIT(3), 0); clk_register_clkdev(clk, NULL, "pka"); - clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", U8500_CLKRST6_BASE, + clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", clkrst6_base, BIT(4), 0); clk_register_clkdev(clk, NULL, "hash1"); - clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", U8500_CLKRST6_BASE, + clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", clkrst6_base, BIT(5), 0); clk_register_clkdev(clk, NULL, "cfgreg"); - clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", U8500_CLKRST6_BASE, + clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", clkrst6_base, BIT(6), 0); clk_register_clkdev(clk, "apb_pclk", "mtu0"); - clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", U8500_CLKRST6_BASE, + clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", clkrst6_base, BIT(7), 0); clk_register_clkdev(clk, "apb_pclk", "mtu1"); @@ -415,110 +416,110 @@ void u8500_clk_init(void) /* Periph1 */ clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk", - U8500_CLKRST1_BASE, BIT(0), CLK_SET_RATE_GATE); + clkrst1_base, BIT(0), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "uart0"); clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk", - U8500_CLKRST1_BASE, BIT(1), CLK_SET_RATE_GATE); + clkrst1_base, BIT(1), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "uart1"); clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk", - U8500_CLKRST1_BASE, BIT(2), CLK_SET_RATE_GATE); + clkrst1_base, BIT(2), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "nmk-i2c.1"); clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk", - U8500_CLKRST1_BASE, BIT(3), CLK_SET_RATE_GATE); + clkrst1_base, BIT(3), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "msp0"); clk_register_clkdev(clk, NULL, "ux500-msp-i2s.0"); clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk", - U8500_CLKRST1_BASE, BIT(4), CLK_SET_RATE_GATE); + clkrst1_base, BIT(4), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "msp1"); clk_register_clkdev(clk, NULL, "ux500-msp-i2s.1"); clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmcclk", - U8500_CLKRST1_BASE, BIT(5), CLK_SET_RATE_GATE); + clkrst1_base, BIT(5), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "sdi0"); clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk", - U8500_CLKRST1_BASE, BIT(6), CLK_SET_RATE_GATE); + clkrst1_base, BIT(6), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "nmk-i2c.2"); clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk", - U8500_CLKRST1_BASE, BIT(8), CLK_SET_RATE_GATE); + clkrst1_base, BIT(8), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "slimbus0"); clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk", - U8500_CLKRST1_BASE, BIT(9), CLK_SET_RATE_GATE); + clkrst1_base, BIT(9), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "nmk-i2c.4"); clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk", - U8500_CLKRST1_BASE, BIT(10), CLK_SET_RATE_GATE); + clkrst1_base, BIT(10), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "msp3"); clk_register_clkdev(clk, NULL, "ux500-msp-i2s.3"); /* Periph2 */ clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk", - U8500_CLKRST2_BASE, BIT(0), CLK_SET_RATE_GATE); + clkrst2_base, BIT(0), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "nmk-i2c.3"); clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmcclk", - U8500_CLKRST2_BASE, BIT(2), CLK_SET_RATE_GATE); + clkrst2_base, BIT(2), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "sdi4"); clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk", - U8500_CLKRST2_BASE, BIT(3), CLK_SET_RATE_GATE); + clkrst2_base, BIT(3), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "msp2"); clk_register_clkdev(clk, NULL, "ux500-msp-i2s.2"); clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmcclk", - U8500_CLKRST2_BASE, BIT(4), CLK_SET_RATE_GATE); + clkrst2_base, BIT(4), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "sdi1"); clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk", - U8500_CLKRST2_BASE, BIT(5), CLK_SET_RATE_GATE); + clkrst2_base, BIT(5), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "sdi3"); /* Note that rate is received from parent. */ clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk", - U8500_CLKRST2_BASE, BIT(6), + clkrst2_base, BIT(6), CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk", - U8500_CLKRST2_BASE, BIT(7), + clkrst2_base, BIT(7), CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); /* Periph3 */ clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk", - U8500_CLKRST3_BASE, BIT(1), CLK_SET_RATE_GATE); + clkrst3_base, BIT(1), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "ssp0"); clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk", - U8500_CLKRST3_BASE, BIT(2), CLK_SET_RATE_GATE); + clkrst3_base, BIT(2), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "ssp1"); clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk", - U8500_CLKRST3_BASE, BIT(3), CLK_SET_RATE_GATE); + clkrst3_base, BIT(3), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "nmk-i2c.0"); clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmcclk", - U8500_CLKRST3_BASE, BIT(4), CLK_SET_RATE_GATE); + clkrst3_base, BIT(4), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "sdi2"); clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k", - U8500_CLKRST3_BASE, BIT(5), CLK_SET_RATE_GATE); + clkrst3_base, BIT(5), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "ske"); clk_register_clkdev(clk, NULL, "nmk-ske-keypad"); clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk", - U8500_CLKRST3_BASE, BIT(6), CLK_SET_RATE_GATE); + clkrst3_base, BIT(6), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "uart2"); clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk", - U8500_CLKRST3_BASE, BIT(7), CLK_SET_RATE_GATE); + clkrst3_base, BIT(7), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "sdi5"); /* Periph6 */ clk = clk_reg_prcc_kclk("p3_rng_kclk", "rngclk", - U8500_CLKRST6_BASE, BIT(0), CLK_SET_RATE_GATE); + clkrst6_base, BIT(0), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "rng"); } diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile index ec3b88fe3e6..c16ca787170 100644 --- a/drivers/clk/versatile/Makefile +++ b/drivers/clk/versatile/Makefile @@ -3,5 +3,5 @@ obj-$(CONFIG_ICST) += clk-icst.o obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o -obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o +obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o clk-sp810.o obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c new file mode 100644 index 00000000000..bf9b15a585e --- /dev/null +++ b/drivers/clk/versatile/clk-sp810.c @@ -0,0 +1,188 @@ +/* + * 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. + * + * 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. + * + * Copyright (C) 2013 ARM Limited + */ + +#include <linux/amba/sp810.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#define to_clk_sp810_timerclken(_hw) \ + container_of(_hw, struct clk_sp810_timerclken, hw) + +struct clk_sp810; + +struct clk_sp810_timerclken { + struct clk_hw hw; + struct clk *clk; + struct clk_sp810 *sp810; + int channel; +}; + +struct clk_sp810 { + struct device_node *node; + int refclk_index, timclk_index; + void __iomem *base; + spinlock_t lock; + struct clk_sp810_timerclken timerclken[4]; + struct clk *refclk; + struct clk *timclk; +}; + +static u8 clk_sp810_timerclken_get_parent(struct clk_hw *hw) +{ + struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw); + u32 val = readl(timerclken->sp810->base + SCCTRL); + + return !!(val & (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel))); +} + +static int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw); + struct clk_sp810 *sp810 = timerclken->sp810; + u32 val, shift = SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel); + unsigned long flags = 0; + + if (WARN_ON(index > 1)) + return -EINVAL; + + spin_lock_irqsave(&sp810->lock, flags); + + val = readl(sp810->base + SCCTRL); + val &= ~(1 << shift); + val |= index << shift; + writel(val, sp810->base + SCCTRL); + + spin_unlock_irqrestore(&sp810->lock, flags); + + return 0; +} + +/* + * FIXME - setting the parent every time .prepare is invoked is inefficient. + * This is better handled by a dedicated clock tree configuration mechanism at + * init-time. Revisit this later when such a mechanism exists + */ +static int clk_sp810_timerclken_prepare(struct clk_hw *hw) +{ + struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw); + struct clk_sp810 *sp810 = timerclken->sp810; + struct clk *old_parent = __clk_get_parent(hw->clk); + struct clk *new_parent; + + if (!sp810->refclk) + sp810->refclk = of_clk_get(sp810->node, sp810->refclk_index); + + if (!sp810->timclk) + sp810->timclk = of_clk_get(sp810->node, sp810->timclk_index); + + if (WARN_ON(IS_ERR(sp810->refclk) || IS_ERR(sp810->timclk))) + return -ENOENT; + + /* Select fastest parent */ + if (clk_get_rate(sp810->refclk) > clk_get_rate(sp810->timclk)) + new_parent = sp810->refclk; + else + new_parent = sp810->timclk; + + /* Switch the parent if necessary */ + if (old_parent != new_parent) { + clk_prepare(new_parent); + clk_set_parent(hw->clk, new_parent); + clk_unprepare(old_parent); + } + + return 0; +} + +static void clk_sp810_timerclken_unprepare(struct clk_hw *hw) +{ + struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw); + struct clk_sp810 *sp810 = timerclken->sp810; + + clk_put(sp810->timclk); + clk_put(sp810->refclk); +} + +static const struct clk_ops clk_sp810_timerclken_ops = { + .prepare = clk_sp810_timerclken_prepare, + .unprepare = clk_sp810_timerclken_unprepare, + .get_parent = clk_sp810_timerclken_get_parent, + .set_parent = clk_sp810_timerclken_set_parent, +}; + +struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec, + void *data) +{ + struct clk_sp810 *sp810 = data; + + if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] > + ARRAY_SIZE(sp810->timerclken))) + return NULL; + + return sp810->timerclken[clkspec->args[0]].clk; +} + +void __init clk_sp810_of_setup(struct device_node *node) +{ + struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL); + const char *parent_names[2]; + char name[12]; + struct clk_init_data init; + int i; + + if (!sp810) { + pr_err("Failed to allocate memory for SP810!\n"); + return; + } + + sp810->refclk_index = of_property_match_string(node, "clock-names", + "refclk"); + parent_names[0] = of_clk_get_parent_name(node, sp810->refclk_index); + + sp810->timclk_index = of_property_match_string(node, "clock-names", + "timclk"); + parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index); + + if (parent_names[0] <= 0 || parent_names[1] <= 0) { + pr_warn("Failed to obtain parent clocks for SP810!\n"); + return; + } + + sp810->node = node; + sp810->base = of_iomap(node, 0); + spin_lock_init(&sp810->lock); + + init.name = name; + init.ops = &clk_sp810_timerclken_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = ARRAY_SIZE(parent_names); + + for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) { + snprintf(name, ARRAY_SIZE(name), "timerclken%d", i); + + sp810->timerclken[i].sp810 = sp810; + sp810->timerclken[i].channel = i; + sp810->timerclken[i].hw.init = &init; + + sp810->timerclken[i].clk = clk_register(NULL, + &sp810->timerclken[i].hw); + WARN_ON(IS_ERR(sp810->timerclken[i].clk)); + } + + of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810); +} +CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup); diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c index 82b45aad8cc..a4a728d0509 100644 --- a/drivers/clk/versatile/clk-vexpress.c +++ b/drivers/clk/versatile/clk-vexpress.c @@ -15,8 +15,6 @@ #include <linux/clkdev.h> #include <linux/clk-provider.h> #include <linux/err.h> -#include <linux/of.h> -#include <linux/of_address.h> #include <linux/vexpress.h> static struct clk *vexpress_sp810_timerclken[4]; @@ -86,50 +84,3 @@ void __init vexpress_clk_init(void __iomem *sp810_base) WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1], "v2m-timer1", "sp804")); } - -#if defined(CONFIG_OF) - -struct clk *vexpress_sp810_of_get(struct of_phandle_args *clkspec, void *data) -{ - if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] > - ARRAY_SIZE(vexpress_sp810_timerclken))) - return NULL; - - return vexpress_sp810_timerclken[clkspec->args[0]]; -} - -void __init vexpress_clk_of_init(void) -{ - struct device_node *node; - struct clk *clk; - struct clk *refclk, *timclk; - - of_clk_init(NULL); - - node = of_find_compatible_node(NULL, NULL, "arm,sp810"); - vexpress_sp810_init(of_iomap(node, 0)); - of_clk_add_provider(node, vexpress_sp810_of_get, NULL); - - /* Select "better" (faster) parent for SP804 timers */ - refclk = of_clk_get_by_name(node, "refclk"); - timclk = of_clk_get_by_name(node, "timclk"); - if (!WARN_ON(IS_ERR(refclk) || IS_ERR(timclk))) { - int i = 0; - - if (clk_get_rate(refclk) > clk_get_rate(timclk)) - clk = refclk; - else - clk = timclk; - - for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++) - WARN_ON(clk_set_parent(vexpress_sp810_timerclken[i], - clk)); - } - - WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[0], - "v2m-timer0", "sp804")); - WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1], - "v2m-timer1", "sp804")); -} - -#endif diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile index f9ba4fab0dd..04781389d0f 100644 --- a/drivers/clk/x86/Makefile +++ b/drivers/clk/x86/Makefile @@ -1,2 +1,2 @@ -clk-x86-lpss-objs := clk-lpss.o clk-lpt.o +clk-x86-lpss-objs := clk-lpt.o obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o diff --git a/drivers/clk/x86/clk-lpss.c b/drivers/clk/x86/clk-lpss.c deleted file mode 100644 index b5e229f3c3d..00000000000 --- a/drivers/clk/x86/clk-lpss.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Intel Low Power Subsystem clocks. - * - * Copyright (C) 2013, Intel Corporation - * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> - * Heikki Krogerus <heikki.krogerus@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/acpi.h> -#include <linux/clk.h> -#include <linux/clk-provider.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/module.h> - -static int clk_lpss_is_mmio_resource(struct acpi_resource *res, void *data) -{ - struct resource r; - return !acpi_dev_resource_memory(res, &r); -} - -static acpi_status clk_lpss_find_mmio(acpi_handle handle, u32 level, - void *data, void **retval) -{ - struct resource_list_entry *rentry; - struct list_head resource_list; - struct acpi_device *adev; - const char *uid = data; - int ret; - - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - - if (uid) { - if (!adev->pnp.unique_id) - return AE_OK; - if (strcmp(uid, adev->pnp.unique_id)) - return AE_OK; - } - - INIT_LIST_HEAD(&resource_list); - ret = acpi_dev_get_resources(adev, &resource_list, - clk_lpss_is_mmio_resource, NULL); - if (ret < 0) - return AE_NO_MEMORY; - - list_for_each_entry(rentry, &resource_list, node) - if (resource_type(&rentry->res) == IORESOURCE_MEM) { - *(struct resource *)retval = rentry->res; - break; - } - - acpi_dev_free_resource_list(&resource_list); - return AE_OK; -} - -/** - * clk_register_lpss_gate - register LPSS clock gate - * @name: name of this clock gate - * @parent_name: parent clock name - * @hid: ACPI _HID of the device - * @uid: ACPI _UID of the device (optional) - * @offset: LPSS PRV_CLOCK_PARAMS offset - * - * Creates and registers LPSS clock gate. - */ -struct clk *clk_register_lpss_gate(const char *name, const char *parent_name, - const char *hid, const char *uid, - unsigned offset) -{ - struct resource res = { }; - void __iomem *mmio_base; - acpi_status status; - struct clk *clk; - - /* - * First try to look the device and its mmio resource from the - * ACPI namespace. - */ - status = acpi_get_devices(hid, clk_lpss_find_mmio, (void *)uid, - (void **)&res); - if (ACPI_FAILURE(status) || !res.start) - return ERR_PTR(-ENODEV); - - mmio_base = ioremap(res.start, resource_size(&res)); - if (!mmio_base) - return ERR_PTR(-ENOMEM); - - clk = clk_register_gate(NULL, name, parent_name, 0, mmio_base + offset, - 0, 0, NULL); - if (IS_ERR(clk)) - iounmap(mmio_base); - - return clk; -} diff --git a/drivers/clk/x86/clk-lpss.h b/drivers/clk/x86/clk-lpss.h deleted file mode 100644 index e9460f44229..00000000000 --- a/drivers/clk/x86/clk-lpss.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Intel Low Power Subsystem clock. - * - * Copyright (C) 2013, Intel Corporation - * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> - * Heikki Krogerus <heikki.krogerus@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __CLK_LPSS_H -#define __CLK_LPSS_H - -#include <linux/err.h> -#include <linux/errno.h> -#include <linux/clk.h> - -#ifdef CONFIG_ACPI -extern struct clk *clk_register_lpss_gate(const char *name, - const char *parent_name, - const char *hid, const char *uid, - unsigned offset); -#else -static inline struct clk *clk_register_lpss_gate(const char *name, - const char *parent_name, - const char *hid, - const char *uid, - unsigned offset) -{ - return ERR_PTR(-ENODEV); -} -#endif - -#endif /* __CLK_LPSS_H */ diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c index 81298aeef7e..4f45eee9e33 100644 --- a/drivers/clk/x86/clk-lpt.c +++ b/drivers/clk/x86/clk-lpt.c @@ -10,64 +10,34 @@ * published by the Free Software Foundation. */ -#include <linux/acpi.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> #include <linux/err.h> #include <linux/module.h> +#include <linux/platform_data/clk-lpss.h> #include <linux/platform_device.h> -#include "clk-lpss.h" - #define PRV_CLOCK_PARAMS 0x800 static int lpt_clk_probe(struct platform_device *pdev) { + struct lpss_clk_data *drvdata; struct clk *clk; + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + /* LPSS free running clock */ - clk = clk_register_fixed_rate(&pdev->dev, "lpss_clk", NULL, CLK_IS_ROOT, - 100000000); + drvdata->name = "lpss_clk"; + clk = clk_register_fixed_rate(&pdev->dev, drvdata->name, NULL, + CLK_IS_ROOT, 100000000); if (IS_ERR(clk)) return PTR_ERR(clk); - /* Shared DMA clock */ - clk_register_clkdev(clk, "hclk", "INTL9C60.0.auto"); - - /* SPI clocks */ - clk = clk_register_lpss_gate("spi0_clk", "lpss_clk", "INT33C0", NULL, - PRV_CLOCK_PARAMS); - if (!IS_ERR(clk)) - clk_register_clkdev(clk, NULL, "INT33C0:00"); - - clk = clk_register_lpss_gate("spi1_clk", "lpss_clk", "INT33C1", NULL, - PRV_CLOCK_PARAMS); - if (!IS_ERR(clk)) - clk_register_clkdev(clk, NULL, "INT33C1:00"); - - /* I2C clocks */ - clk = clk_register_lpss_gate("i2c0_clk", "lpss_clk", "INT33C2", NULL, - PRV_CLOCK_PARAMS); - if (!IS_ERR(clk)) - clk_register_clkdev(clk, NULL, "INT33C2:00"); - - clk = clk_register_lpss_gate("i2c1_clk", "lpss_clk", "INT33C3", NULL, - PRV_CLOCK_PARAMS); - if (!IS_ERR(clk)) - clk_register_clkdev(clk, NULL, "INT33C3:00"); - - /* UART clocks */ - clk = clk_register_lpss_gate("uart0_clk", "lpss_clk", "INT33C4", NULL, - PRV_CLOCK_PARAMS); - if (!IS_ERR(clk)) - clk_register_clkdev(clk, NULL, "INT33C4:00"); - - clk = clk_register_lpss_gate("uart1_clk", "lpss_clk", "INT33C5", NULL, - PRV_CLOCK_PARAMS); - if (!IS_ERR(clk)) - clk_register_clkdev(clk, NULL, "INT33C5:00"); - + drvdata->clk = clk; + platform_set_drvdata(pdev, drvdata); return 0; } @@ -79,8 +49,7 @@ static struct platform_driver lpt_clk_driver = { .probe = lpt_clk_probe, }; -static int __init lpt_clk_init(void) +int __init lpt_clk_init(void) { return platform_driver_register(&lpt_clk_driver); } -arch_initcall(lpt_clk_init); |