diff options
Diffstat (limited to 'drivers/clk')
74 files changed, 8826 insertions, 2775 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 0357ac44638b..279407a36391 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -27,7 +27,7 @@ config COMMON_CLK_DEBUG bool "DebugFS representation of clock tree" select DEBUG_FS ---help--- - Creates a directory hierchy in debugfs for visualizing the clk + Creates a directory hierarchy in debugfs for visualizing the clk tree structure. Each directory contains read-only members that export information specific to that clk node: clk_rate, clk_flags, clk_prepare_count, clk_enable_count & @@ -42,7 +42,7 @@ config COMMON_CLK_WM831X config COMMON_CLK_VERSATILE bool "Clock driver for ARM Reference designs" - depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS + depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64 ---help--- Supports clocking on ARM Reference designs: - Integrator/AP and Integrator/CP @@ -58,13 +58,18 @@ config COMMON_CLK_MAX77686 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 COMMON_CLK_S2MPS11 + tristate "Clock driver for S2MPS11 MFD" + depends on MFD_SEC_CORE + ---help--- + This driver supports S2MPS11 crystal oscillator clock. + config CLK_TWL6040 tristate "External McPDM functional clock from twl6040" depends on TWL6040_CORE @@ -81,6 +86,13 @@ config COMMON_CLK_AXI_CLKGEN Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx FPGAs. It is commonly used in Analog Devices' reference designs. +config CLK_PPC_CORENET + bool "Clock driver for PowerPC corenet platforms" + depends on PPC_E500MC && OF + ---help--- + This adds the clock driver support for Freescale PowerPC corenet + platforms using common clock framework. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 137d3e730f86..7b111062ccba 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,21 +13,23 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o +obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ -obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o +obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o obj-$(CONFIG_PLAT_ORION) += mvebu/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ 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_ZYNQ) += zynq/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ @@ -38,4 +40,6 @@ 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_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o +obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o diff --git a/drivers/clk/clk-bcm2835.c b/drivers/clk/clk-bcm2835.c index 792bc57a9db7..5fb4ff53d088 100644 --- a/drivers/clk/clk-bcm2835.c +++ b/drivers/clk/clk-bcm2835.c @@ -23,7 +23,7 @@ #include <linux/clk-provider.h> #include <linux/of.h> -static const __initconst struct of_device_id clk_match[] = { +static const struct of_device_id clk_match[] __initconst = { { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, { } }; diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 6d9674160430..8d3009e44fba 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -104,7 +104,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, struct clk_divider *divider = to_clk_divider(hw); unsigned int div, val; - val = readl(divider->reg) >> divider->shift; + val = clk_readl(divider->reg) >> divider->shift; val &= div_mask(divider); div = _get_div(divider, val); @@ -150,6 +150,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, struct clk_divider *divider = to_clk_divider(hw); int i, bestdiv = 0; unsigned long parent_rate, best = 0, now, maxdiv; + unsigned long parent_rate_saved = *best_parent_rate; if (!rate) rate = 1; @@ -173,6 +174,15 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, for (i = 1; i <= maxdiv; i++) { if (!_is_valid_div(divider, i)) continue; + if (rate * i == parent_rate_saved) { + /* + * It's the most ideal case if the requested rate can be + * divided from parent clock without needing to change + * parent rate, so return the divider immediately. + */ + *best_parent_rate = parent_rate_saved; + return i; + } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); now = parent_rate / i; @@ -217,10 +227,14 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, if (divider->lock) spin_lock_irqsave(divider->lock, flags); - val = readl(divider->reg); - val &= ~(div_mask(divider) << divider->shift); + if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { + val = div_mask(divider) << (divider->shift + 16); + } else { + val = clk_readl(divider->reg); + val &= ~(div_mask(divider) << divider->shift); + } val |= value << divider->shift; - writel(val, divider->reg); + clk_writel(val, divider->reg); if (divider->lock) spin_unlock_irqrestore(divider->lock, flags); @@ -245,6 +259,13 @@ static struct clk *_register_divider(struct device *dev, const char *name, struct clk *clk; struct clk_init_data init; + if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { + if (width + shift > 16) { + pr_warn("divider value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + /* allocate the divider */ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); if (!div) { @@ -296,6 +317,7 @@ struct clk *clk_register_divider(struct device *dev, const char *name, return _register_divider(dev, name, parent_name, flags, reg, shift, width, clk_divider_flags, NULL, lock); } +EXPORT_SYMBOL_GPL(clk_register_divider); /** * clk_register_divider_table - register a table based divider clock with @@ -320,3 +342,4 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, return _register_divider(dev, name, parent_name, flags, reg, shift, width, clk_divider_flags, table, lock); } +EXPORT_SYMBOL_GPL(clk_register_divider_table); diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 9ff7d510faa3..0e1d89b4321b 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -97,6 +97,8 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, return clk; } +EXPORT_SYMBOL_GPL(clk_register_fixed_factor); + #ifdef CONFIG_OF /** * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index dc58fbd8516f..1ed591ab8b1d 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -80,6 +80,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name, return clk; } +EXPORT_SYMBOL_GPL(clk_register_fixed_rate); #ifdef CONFIG_OF /** diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 15114febfd92..4a58c55255bd 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -53,14 +53,20 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable) if (gate->lock) spin_lock_irqsave(gate->lock, flags); - reg = readl(gate->reg); - - if (set) - reg |= BIT(gate->bit_idx); - else - reg &= ~BIT(gate->bit_idx); + if (gate->flags & CLK_GATE_HIWORD_MASK) { + reg = BIT(gate->bit_idx + 16); + if (set) + reg |= BIT(gate->bit_idx); + } else { + reg = clk_readl(gate->reg); + + if (set) + reg |= BIT(gate->bit_idx); + else + reg &= ~BIT(gate->bit_idx); + } - writel(reg, gate->reg); + clk_writel(reg, gate->reg); if (gate->lock) spin_unlock_irqrestore(gate->lock, flags); @@ -83,7 +89,7 @@ static int clk_gate_is_enabled(struct clk_hw *hw) u32 reg; struct clk_gate *gate = to_clk_gate(hw); - reg = readl(gate->reg); + reg = clk_readl(gate->reg); /* if a set bit disables this clk, flip it before masking */ if (gate->flags & CLK_GATE_SET_TO_DISABLE) @@ -121,6 +127,13 @@ struct clk *clk_register_gate(struct device *dev, const char *name, struct clk *clk; struct clk_init_data init; + if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { + if (bit_idx > 16) { + pr_err("gate bit exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + /* allocate the gate */ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); if (!gate) { @@ -148,3 +161,4 @@ struct clk *clk_register_gate(struct device *dev, const char *name, return clk; } +EXPORT_SYMBOL_GPL(clk_register_gate); diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 25b1734560d0..4f96ff3ba728 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -42,7 +42,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so * val = 0x4 really means "bit 2, index starts at bit 0" */ - val = readl(mux->reg) >> mux->shift; + val = clk_readl(mux->reg) >> mux->shift; val &= mux->mask; if (mux->table) { @@ -86,10 +86,14 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) if (mux->lock) spin_lock_irqsave(mux->lock, flags); - val = readl(mux->reg); - val &= ~(mux->mask << mux->shift); + if (mux->flags & CLK_MUX_HIWORD_MASK) { + val = mux->mask << (mux->shift + 16); + } else { + val = clk_readl(mux->reg); + val &= ~(mux->mask << mux->shift); + } val |= index << mux->shift; - writel(val, mux->reg); + clk_writel(val, mux->reg); if (mux->lock) spin_unlock_irqrestore(mux->lock, flags); @@ -100,9 +104,15 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) const struct clk_ops clk_mux_ops = { .get_parent = clk_mux_get_parent, .set_parent = clk_mux_set_parent, + .determine_rate = __clk_mux_determine_rate, }; EXPORT_SYMBOL_GPL(clk_mux_ops); +const struct clk_ops clk_mux_ro_ops = { + .get_parent = clk_mux_get_parent, +}; +EXPORT_SYMBOL_GPL(clk_mux_ro_ops); + 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, u32 mask, @@ -111,6 +121,15 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, struct clk_mux *mux; struct clk *clk; struct clk_init_data init; + u8 width = 0; + + if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { + width = fls(mask) - ffs(mask) + 1; + if (width + shift > 16) { + pr_err("mux value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } /* allocate the mux */ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); @@ -120,7 +139,10 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, } init.name = name; - init.ops = &clk_mux_ops; + if (clk_mux_flags & CLK_MUX_READ_ONLY) + init.ops = &clk_mux_ro_ops; + else + init.ops = &clk_mux_ops; init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; @@ -141,6 +163,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, return clk; } +EXPORT_SYMBOL_GPL(clk_register_mux_table); struct clk *clk_register_mux(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, @@ -153,3 +176,4 @@ struct clk *clk_register_mux(struct device *dev, const char *name, flags, reg, shift, mask, clk_mux_flags, NULL, lock); } +EXPORT_SYMBOL_GPL(clk_register_mux); diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c index 6b4c70f7d23d..51410c2ac2cb 100644 --- a/drivers/clk/clk-nomadik.c +++ b/drivers/clk/clk-nomadik.c @@ -1,48 +1,566 @@ +/* + * Nomadik clock implementation + * Copyright (C) 2013 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Author: Linus Walleij <linus.walleij@linaro.org> + */ + +#define pr_fmt(fmt) "Nomadik SRC clocks: " fmt + +#include <linux/bitops.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/err.h> #include <linux/io.h> #include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/spinlock.h> +#include <linux/reboot.h> /* * The Nomadik clock tree is described in the STN8815A12 DB V4.2 * reference manual for the chip, page 94 ff. + * Clock IDs are in the STn8815 Reference Manual table 3, page 27. */ -void __init nomadik_clk_init(void) +#define SRC_CR 0x00U +#define SRC_XTALCR 0x0CU +#define SRC_XTALCR_XTALTIMEN BIT(20) +#define SRC_XTALCR_SXTALDIS BIT(19) +#define SRC_XTALCR_MXTALSTAT BIT(2) +#define SRC_XTALCR_MXTALEN BIT(1) +#define SRC_XTALCR_MXTALOVER BIT(0) +#define SRC_PLLCR 0x10U +#define SRC_PLLCR_PLLTIMEN BIT(29) +#define SRC_PLLCR_PLL2EN BIT(28) +#define SRC_PLLCR_PLL1STAT BIT(2) +#define SRC_PLLCR_PLL1EN BIT(1) +#define SRC_PLLCR_PLL1OVER BIT(0) +#define SRC_PLLFR 0x14U +#define SRC_PCKEN0 0x24U +#define SRC_PCKDIS0 0x28U +#define SRC_PCKENSR0 0x2CU +#define SRC_PCKSR0 0x30U +#define SRC_PCKEN1 0x34U +#define SRC_PCKDIS1 0x38U +#define SRC_PCKENSR1 0x3CU +#define SRC_PCKSR1 0x40U + +/* Lock protecting the SRC_CR register */ +static DEFINE_SPINLOCK(src_lock); +/* Base address of the SRC */ +static void __iomem *src_base; + +/** + * struct clk_pll1 - Nomadik PLL1 clock + * @hw: corresponding clock hardware entry + * @id: PLL instance: 1 or 2 + */ +struct clk_pll { + struct clk_hw hw; + int id; +}; + +/** + * struct clk_src - Nomadik src clock + * @hw: corresponding clock hardware entry + * @id: the clock ID + * @group1: true if the clock is in group1, else it is in group0 + * @clkbit: bit 0...31 corresponding to the clock in each clock register + */ +struct clk_src { + struct clk_hw hw; + int id; + bool group1; + u32 clkbit; +}; + +#define to_pll(_hw) container_of(_hw, struct clk_pll, hw) +#define to_src(_hw) container_of(_hw, struct clk_src, hw) + +static int pll_clk_enable(struct clk_hw *hw) +{ + struct clk_pll *pll = to_pll(hw); + u32 val; + + spin_lock(&src_lock); + val = readl(src_base + SRC_PLLCR); + if (pll->id == 1) { + if (val & SRC_PLLCR_PLL1OVER) { + val |= SRC_PLLCR_PLL1EN; + writel(val, src_base + SRC_PLLCR); + } + } else if (pll->id == 2) { + val |= SRC_PLLCR_PLL2EN; + writel(val, src_base + SRC_PLLCR); + } + spin_unlock(&src_lock); + return 0; +} + +static void pll_clk_disable(struct clk_hw *hw) +{ + struct clk_pll *pll = to_pll(hw); + u32 val; + + spin_lock(&src_lock); + val = readl(src_base + SRC_PLLCR); + if (pll->id == 1) { + if (val & SRC_PLLCR_PLL1OVER) { + val &= ~SRC_PLLCR_PLL1EN; + writel(val, src_base + SRC_PLLCR); + } + } else if (pll->id == 2) { + val &= ~SRC_PLLCR_PLL2EN; + writel(val, src_base + SRC_PLLCR); + } + spin_unlock(&src_lock); +} + +static int pll_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_pll *pll = to_pll(hw); + u32 val; + + val = readl(src_base + SRC_PLLCR); + if (pll->id == 1) { + if (val & SRC_PLLCR_PLL1OVER) + return !!(val & SRC_PLLCR_PLL1EN); + } else if (pll->id == 2) { + return !!(val & SRC_PLLCR_PLL2EN); + } + return 1; +} + +static unsigned long pll_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_pll(hw); + u32 val; + + val = readl(src_base + SRC_PLLFR); + + if (pll->id == 1) { + u8 mul; + u8 div; + + mul = (val >> 8) & 0x3FU; + mul += 2; + div = val & 0x07U; + return (parent_rate * mul) >> div; + } + + if (pll->id == 2) { + u8 mul; + + mul = (val >> 24) & 0x3FU; + mul += 2; + return (parent_rate * mul); + } + + /* Unknown PLL */ + return 0; +} + + +static const struct clk_ops pll_clk_ops = { + .enable = pll_clk_enable, + .disable = pll_clk_disable, + .is_enabled = pll_clk_is_enabled, + .recalc_rate = pll_clk_recalc_rate, +}; + +static struct clk * __init +pll_clk_register(struct device *dev, const char *name, + const char *parent_name, u32 id) { struct clk *clk; + struct clk_pll *pll; + struct clk_init_data init; - clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); - clk_register_clkdev(clk, "apb_pclk", NULL); - clk_register_clkdev(clk, NULL, "gpio.0"); - clk_register_clkdev(clk, NULL, "gpio.1"); - clk_register_clkdev(clk, NULL, "gpio.2"); - clk_register_clkdev(clk, NULL, "gpio.3"); - clk_register_clkdev(clk, NULL, "rng"); - clk_register_clkdev(clk, NULL, "fsmc-nand"); + if (id != 1 && id != 2) { + pr_err("%s: the Nomadik has only PLL 1 & 2\n", __func__); + return ERR_PTR(-EINVAL); + } - /* - * The 2.4 MHz TIMCLK reference clock is active at boot time, this is - * actually the MXTALCLK @19.2 MHz divided by 8. This clock is used - * by the timers and watchdog. See page 105 ff. - */ - clk = clk_register_fixed_rate(NULL, "TIMCLK", NULL, CLK_IS_ROOT, - 2400000); - clk_register_clkdev(clk, NULL, "mtu0"); - clk_register_clkdev(clk, NULL, "mtu1"); + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate PLL clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &pll_clk_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + pll->hw.init = &init; + pll->id = id; + + pr_debug("register PLL1 clock \"%s\"\n", name); + + clk = clk_register(dev, &pll->hw); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +/* + * The Nomadik SRC clocks are gated, but not in the sense that + * you read-modify-write a register. Instead there are separate + * clock enable and clock disable registers. Writing a '1' bit in + * the enable register for a certain clock ungates that clock without + * affecting the other clocks. The disable register works the opposite + * way. + */ + +static int src_clk_enable(struct clk_hw *hw) +{ + struct clk_src *sclk = to_src(hw); + u32 enreg = sclk->group1 ? SRC_PCKEN1 : SRC_PCKEN0; + u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; + + writel(sclk->clkbit, src_base + enreg); + /* spin until enabled */ + while (!(readl(src_base + sreg) & sclk->clkbit)) + cpu_relax(); + return 0; +} + +static void src_clk_disable(struct clk_hw *hw) +{ + struct clk_src *sclk = to_src(hw); + u32 disreg = sclk->group1 ? SRC_PCKDIS1 : SRC_PCKDIS0; + u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; + + writel(sclk->clkbit, src_base + disreg); + /* spin until disabled */ + while (readl(src_base + sreg) & sclk->clkbit) + cpu_relax(); +} + +static int src_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_src *sclk = to_src(hw); + u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; + u32 val = readl(src_base + sreg); + return !!(val & sclk->clkbit); +} + +static unsigned long +src_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate; +} + +static const struct clk_ops src_clk_ops = { + .enable = src_clk_enable, + .disable = src_clk_disable, + .is_enabled = src_clk_is_enabled, + .recalc_rate = src_clk_recalc_rate, +}; + +static struct clk * __init +src_clk_register(struct device *dev, const char *name, + const char *parent_name, u8 id) +{ + struct clk *clk; + struct clk_src *sclk; + struct clk_init_data init; + + sclk = kzalloc(sizeof(*sclk), GFP_KERNEL); + if (!sclk) { + pr_err("could not allocate SRC clock %s\n", + name); + return ERR_PTR(-ENOMEM); + } + init.name = name; + init.ops = &src_clk_ops; + /* Do not force-disable the static SDRAM controller */ + if (id == 2) + init.flags = CLK_IGNORE_UNUSED; + else + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + sclk->hw.init = &init; + sclk->id = id; + sclk->group1 = (id > 31); + sclk->clkbit = BIT(id & 0x1f); + + pr_debug("register clock \"%s\" ID: %d group: %d bits: %08x\n", + name, id, sclk->group1, sclk->clkbit); + + clk = clk_register(dev, &sclk->hw); + if (IS_ERR(clk)) + kfree(sclk); + + return clk; +} + +#ifdef CONFIG_DEBUG_FS + +static u32 src_pcksr0_boot; +static u32 src_pcksr1_boot; + +static const char * const src_clk_names[] = { + "HCLKDMA0 ", + "HCLKSMC ", + "HCLKSDRAM ", + "HCLKDMA1 ", + "HCLKCLCD ", + "PCLKIRDA ", + "PCLKSSP ", + "PCLKUART0 ", + "PCLKSDI ", + "PCLKI2C0 ", + "PCLKI2C1 ", + "PCLKUART1 ", + "PCLMSP0 ", + "HCLKUSB ", + "HCLKDIF ", + "HCLKSAA ", + "HCLKSVA ", + "PCLKHSI ", + "PCLKXTI ", + "PCLKUART2 ", + "PCLKMSP1 ", + "PCLKMSP2 ", + "PCLKOWM ", + "HCLKHPI ", + "PCLKSKE ", + "PCLKHSEM ", + "HCLK3D ", + "HCLKHASH ", + "HCLKCRYP ", + "PCLKMSHC ", + "HCLKUSBM ", + "HCLKRNG ", + "RESERVED ", + "RESERVED ", + "RESERVED ", + "RESERVED ", + "CLDCLK ", + "IRDACLK ", + "SSPICLK ", + "UART0CLK ", + "SDICLK ", + "I2C0CLK ", + "I2C1CLK ", + "UART1CLK ", + "MSPCLK0 ", + "USBCLK ", + "DIFCLK ", + "IPI2CCLK ", + "IPBMCCLK ", + "HSICLKRX ", + "HSICLKTX ", + "UART2CLK ", + "MSPCLK1 ", + "MSPCLK2 ", + "OWMCLK ", + "RESERVED ", + "SKECLK ", + "RESERVED ", + "3DCLK ", + "PCLKMSP3 ", + "MSPCLK3 ", + "MSHCCLK ", + "USBMCLK ", + "RNGCCLK ", +}; + +static int nomadik_src_clk_show(struct seq_file *s, void *what) +{ + int i; + u32 src_pcksr0 = readl(src_base + SRC_PCKSR0); + u32 src_pcksr1 = readl(src_base + SRC_PCKSR1); + u32 src_pckensr0 = readl(src_base + SRC_PCKENSR0); + u32 src_pckensr1 = readl(src_base + SRC_PCKENSR1); + + seq_printf(s, "Clock: Boot: Now: Request: ASKED:\n"); + for (i = 0; i < ARRAY_SIZE(src_clk_names); i++) { + u32 pcksrb = (i < 0x20) ? src_pcksr0_boot : src_pcksr1_boot; + u32 pcksr = (i < 0x20) ? src_pcksr0 : src_pcksr1; + u32 pckreq = (i < 0x20) ? src_pckensr0 : src_pckensr1; + u32 mask = BIT(i & 0x1f); + + seq_printf(s, "%s %s %s %s\n", + src_clk_names[i], + (pcksrb & mask) ? "on " : "off", + (pcksr & mask) ? "on " : "off", + (pckreq & mask) ? "on " : "off"); + } + return 0; +} + +static int nomadik_src_clk_open(struct inode *inode, struct file *file) +{ + return single_open(file, nomadik_src_clk_show, NULL); +} + +static const struct file_operations nomadik_src_clk_debugfs_ops = { + .open = nomadik_src_clk_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init nomadik_src_clk_init_debugfs(void) +{ + src_pcksr0_boot = readl(src_base + SRC_PCKSR0); + src_pcksr1_boot = readl(src_base + SRC_PCKSR1); + debugfs_create_file("nomadik-src-clk", S_IFREG | S_IRUGO, + NULL, NULL, &nomadik_src_clk_debugfs_ops); + return 0; +} + +module_init(nomadik_src_clk_init_debugfs); + +#endif + +static void __init of_nomadik_pll_setup(struct device_node *np) +{ + struct clk *clk = ERR_PTR(-EINVAL); + const char *clk_name = np->name; + const char *parent_name; + u32 pll_id; + + if (of_property_read_u32(np, "pll-id", &pll_id)) { + pr_err("%s: PLL \"%s\" missing pll-id property\n", + __func__, clk_name); + return; + } + parent_name = of_clk_get_parent_name(np, 0); + clk = pll_clk_register(NULL, clk_name, parent_name, pll_id); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static void __init of_nomadik_hclk_setup(struct device_node *np) +{ + struct clk *clk = ERR_PTR(-EINVAL); + const char *clk_name = np->name; + const char *parent_name; + + parent_name = of_clk_get_parent_name(np, 0); /* - * At boot time, PLL2 is set to generate a set of fixed clocks, - * one of them is CLK48, the 48 MHz clock, routed to the UART, MMC/SD - * I2C, IrDA, USB and SSP blocks. + * The HCLK divides PLL1 with 1 (passthru), 2, 3 or 4. */ - clk = clk_register_fixed_rate(NULL, "CLK48", NULL, CLK_IS_ROOT, - 48000000); - clk_register_clkdev(clk, NULL, "uart0"); - clk_register_clkdev(clk, NULL, "uart1"); - clk_register_clkdev(clk, NULL, "mmci"); - clk_register_clkdev(clk, NULL, "ssp"); - clk_register_clkdev(clk, NULL, "nmk-i2c.0"); - clk_register_clkdev(clk, NULL, "nmk-i2c.1"); + clk = clk_register_divider(NULL, clk_name, parent_name, + 0, src_base + SRC_CR, + 13, 2, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + &src_lock); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static void __init of_nomadik_src_clk_setup(struct device_node *np) +{ + struct clk *clk = ERR_PTR(-EINVAL); + const char *clk_name = np->name; + const char *parent_name; + u32 clk_id; + + if (of_property_read_u32(np, "clock-id", &clk_id)) { + pr_err("%s: SRC clock \"%s\" missing clock-id property\n", + __func__, clk_name); + return; + } + parent_name = of_clk_get_parent_name(np, 0); + clk = src_clk_register(NULL, clk_name, parent_name, clk_id); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static const struct of_device_id nomadik_src_match[] __initconst = { + { .compatible = "stericsson,nomadik-src" }, + { /* sentinel */ } +}; + +static const struct of_device_id nomadik_src_clk_match[] __initconst = { + { + .compatible = "fixed-clock", + .data = of_fixed_clk_setup, + }, + { + .compatible = "fixed-factor-clock", + .data = of_fixed_factor_clk_setup, + }, + { + .compatible = "st,nomadik-pll-clock", + .data = of_nomadik_pll_setup, + }, + { + .compatible = "st,nomadik-hclk-clock", + .data = of_nomadik_hclk_setup, + }, + { + .compatible = "st,nomadik-src-clock", + .data = of_nomadik_src_clk_setup, + }, + { /* sentinel */ } +}; + +static int nomadik_clk_reboot_handler(struct notifier_block *this, + unsigned long code, + void *unused) +{ + u32 val; + + /* The main chrystal need to be enabled for reboot to work */ + val = readl(src_base + SRC_XTALCR); + val &= ~SRC_XTALCR_MXTALOVER; + val |= SRC_XTALCR_MXTALEN; + pr_crit("force-enabling MXTALO\n"); + writel(val, src_base + SRC_XTALCR); + return NOTIFY_OK; +} + +static struct notifier_block nomadik_clk_reboot_notifier = { + .notifier_call = nomadik_clk_reboot_handler, +}; + +void __init nomadik_clk_init(void) +{ + struct device_node *np; + u32 val; + + np = of_find_matching_node(NULL, nomadik_src_match); + if (!np) { + pr_crit("no matching node for SRC, aborting clock init\n"); + return; + } + src_base = of_iomap(np, 0); + if (!src_base) { + pr_err("%s: must have src parent node with REGS (%s)\n", + __func__, np->name); + return; + } + val = readl(src_base + SRC_XTALCR); + pr_info("SXTALO is %s\n", + (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled"); + pr_info("MXTAL is %s\n", + (val & SRC_XTALCR_MXTALSTAT) ? "enabled" : "disabled"); + if (of_property_read_bool(np, "disable-sxtalo")) { + /* The machine uses an external oscillator circuit */ + val |= SRC_XTALCR_SXTALDIS; + pr_info("disabling SXTALO\n"); + } + if (of_property_read_bool(np, "disable-mxtalo")) { + /* Disable this too: also run by external oscillator */ + val |= SRC_XTALCR_MXTALOVER; + val &= ~SRC_XTALCR_MXTALEN; + pr_info("disabling MXTALO\n"); + } + writel(val, src_base + SRC_XTALCR); + register_reboot_notifier(&nomadik_clk_reboot_notifier); + + of_clk_init(nomadik_src_clk_match); } diff --git a/drivers/clk/clk-nspire.c b/drivers/clk/clk-nspire.c new file mode 100644 index 000000000000..a378db7b2382 --- /dev/null +++ b/drivers/clk/clk-nspire.c @@ -0,0 +1,153 @@ +/* + * + * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> + * + * 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/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#define MHZ (1000 * 1000) + +#define BASE_CPU_SHIFT 1 +#define BASE_CPU_MASK 0x7F + +#define CPU_AHB_SHIFT 12 +#define CPU_AHB_MASK 0x07 + +#define FIXED_BASE_SHIFT 8 +#define FIXED_BASE_MASK 0x01 + +#define CLASSIC_BASE_SHIFT 16 +#define CLASSIC_BASE_MASK 0x1F + +#define CX_BASE_SHIFT 15 +#define CX_BASE_MASK 0x3F + +#define CX_UNKNOWN_SHIFT 21 +#define CX_UNKNOWN_MASK 0x03 + +struct nspire_clk_info { + u32 base_clock; + u16 base_cpu_ratio; + u16 base_ahb_ratio; +}; + + +#define EXTRACT(var, prop) (((var)>>prop##_SHIFT) & prop##_MASK) +static void nspire_clkinfo_cx(u32 val, struct nspire_clk_info *clk) +{ + if (EXTRACT(val, FIXED_BASE)) + clk->base_clock = 48 * MHZ; + else + clk->base_clock = 6 * EXTRACT(val, CX_BASE) * MHZ; + + clk->base_cpu_ratio = EXTRACT(val, BASE_CPU) * EXTRACT(val, CX_UNKNOWN); + clk->base_ahb_ratio = clk->base_cpu_ratio * (EXTRACT(val, CPU_AHB) + 1); +} + +static void nspire_clkinfo_classic(u32 val, struct nspire_clk_info *clk) +{ + if (EXTRACT(val, FIXED_BASE)) + clk->base_clock = 27 * MHZ; + else + clk->base_clock = (300 - 6 * EXTRACT(val, CLASSIC_BASE)) * MHZ; + + clk->base_cpu_ratio = EXTRACT(val, BASE_CPU) * 2; + clk->base_ahb_ratio = clk->base_cpu_ratio * (EXTRACT(val, CPU_AHB) + 1); +} + +static void __init nspire_ahbdiv_setup(struct device_node *node, + void (*get_clkinfo)(u32, struct nspire_clk_info *)) +{ + u32 val; + void __iomem *io; + struct clk *clk; + const char *clk_name = node->name; + const char *parent_name; + struct nspire_clk_info info; + + io = of_iomap(node, 0); + if (!io) + return; + val = readl(io); + iounmap(io); + + get_clkinfo(val, &info); + + 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, + 1, info.base_ahb_ratio); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + +static void __init nspire_ahbdiv_setup_cx(struct device_node *node) +{ + nspire_ahbdiv_setup(node, nspire_clkinfo_cx); +} + +static void __init nspire_ahbdiv_setup_classic(struct device_node *node) +{ + nspire_ahbdiv_setup(node, nspire_clkinfo_classic); +} + +CLK_OF_DECLARE(nspire_ahbdiv_cx, "lsi,nspire-cx-ahb-divider", + nspire_ahbdiv_setup_cx); +CLK_OF_DECLARE(nspire_ahbdiv_classic, "lsi,nspire-classic-ahb-divider", + nspire_ahbdiv_setup_classic); + +static void __init nspire_clk_setup(struct device_node *node, + void (*get_clkinfo)(u32, struct nspire_clk_info *)) +{ + u32 val; + void __iomem *io; + struct clk *clk; + const char *clk_name = node->name; + struct nspire_clk_info info; + + io = of_iomap(node, 0); + if (!io) + return; + val = readl(io); + iounmap(io); + + get_clkinfo(val, &info); + + of_property_read_string(node, "clock-output-names", &clk_name); + + clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, + info.base_clock); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + else + return; + + pr_info("TI-NSPIRE Base: %uMHz CPU: %uMHz AHB: %uMHz\n", + info.base_clock / MHZ, + info.base_clock / info.base_cpu_ratio / MHZ, + info.base_clock / info.base_ahb_ratio / MHZ); +} + +static void __init nspire_clk_setup_cx(struct device_node *node) +{ + nspire_clk_setup(node, nspire_clkinfo_cx); +} + +static void __init nspire_clk_setup_classic(struct device_node *node) +{ + nspire_clk_setup(node, nspire_clkinfo_classic); +} + +CLK_OF_DECLARE(nspire_clk_cx, "lsi,nspire-cx-clock", nspire_clk_setup_cx); +CLK_OF_DECLARE(nspire_clk_classic, "lsi,nspire-classic-clock", + nspire_clk_setup_classic); diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c new file mode 100644 index 000000000000..e9587073bd32 --- /dev/null +++ b/drivers/clk/clk-ppc-corenet.c @@ -0,0 +1,280 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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. + * + * clock driver for Freescale PowerPC corenet SoCs. + */ +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/of.h> +#include <linux/slab.h> + +struct cmux_clk { + struct clk_hw hw; + void __iomem *reg; + u32 flags; +}; + +#define PLL_KILL BIT(31) +#define CLKSEL_SHIFT 27 +#define CLKSEL_ADJUST BIT(0) +#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw) + +static void __iomem *base; +static unsigned int clocks_per_pll; + +static int cmux_set_parent(struct clk_hw *hw, u8 idx) +{ + struct cmux_clk *clk = to_cmux_clk(hw); + u32 clksel; + + clksel = ((idx / clocks_per_pll) << 2) + idx % clocks_per_pll; + if (clk->flags & CLKSEL_ADJUST) + clksel += 8; + clksel = (clksel & 0xf) << CLKSEL_SHIFT; + iowrite32be(clksel, clk->reg); + + return 0; +} + +static u8 cmux_get_parent(struct clk_hw *hw) +{ + struct cmux_clk *clk = to_cmux_clk(hw); + u32 clksel; + + clksel = ioread32be(clk->reg); + clksel = (clksel >> CLKSEL_SHIFT) & 0xf; + if (clk->flags & CLKSEL_ADJUST) + clksel -= 8; + clksel = (clksel >> 2) * clocks_per_pll + clksel % 4; + + return clksel; +} + +const struct clk_ops cmux_ops = { + .get_parent = cmux_get_parent, + .set_parent = cmux_set_parent, +}; + +static void __init core_mux_init(struct device_node *np) +{ + struct clk *clk; + struct clk_init_data init; + struct cmux_clk *cmux_clk; + struct device_node *node; + int rc, count, i; + u32 offset; + const char *clk_name; + const char **parent_names; + + rc = of_property_read_u32(np, "reg", &offset); + if (rc) { + pr_err("%s: could not get reg property\n", np->name); + return; + } + + /* get the input clock source count */ + count = of_property_count_strings(np, "clock-names"); + if (count < 0) { + pr_err("%s: get clock count error\n", np->name); + return; + } + parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL); + if (!parent_names) { + pr_err("%s: could not allocate parent_names\n", __func__); + return; + } + + for (i = 0; i < count; i++) + parent_names[i] = of_clk_get_parent_name(np, i); + + cmux_clk = kzalloc(sizeof(struct cmux_clk), GFP_KERNEL); + if (!cmux_clk) { + pr_err("%s: could not allocate cmux_clk\n", __func__); + goto err_name; + } + cmux_clk->reg = base + offset; + + node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen"); + if (node && (offset >= 0x80)) + cmux_clk->flags = CLKSEL_ADJUST; + + rc = of_property_read_string_index(np, "clock-output-names", + 0, &clk_name); + if (rc) { + pr_err("%s: read clock names error\n", np->name); + goto err_clk; + } + + init.name = clk_name; + init.ops = &cmux_ops; + init.parent_names = parent_names; + init.num_parents = count; + init.flags = 0; + cmux_clk->hw.init = &init; + + clk = clk_register(NULL, &cmux_clk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clock\n", clk_name); + goto err_clk; + } + + rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (rc) { + pr_err("Could not register clock provider for node:%s\n", + np->name); + goto err_clk; + } + goto err_name; + +err_clk: + kfree(cmux_clk); +err_name: + /* free *_names because they are reallocated when registered */ + kfree(parent_names); +} + +static void __init core_pll_init(struct device_node *np) +{ + u32 offset, mult; + int i, rc, count; + const char *clk_name, *parent_name; + struct clk_onecell_data *onecell_data; + struct clk **subclks; + + rc = of_property_read_u32(np, "reg", &offset); + if (rc) { + pr_err("%s: could not get reg property\n", np->name); + return; + } + + /* get the multiple of PLL */ + mult = ioread32be(base + offset); + + /* check if this PLL is disabled */ + if (mult & PLL_KILL) { + pr_debug("PLL:%s is disabled\n", np->name); + return; + } + mult = (mult >> 1) & 0x3f; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) { + pr_err("PLL: %s must have a parent\n", np->name); + return; + } + + count = of_property_count_strings(np, "clock-output-names"); + if (count < 0 || count > 4) { + pr_err("%s: clock is not supported\n", np->name); + return; + } + + /* output clock number per PLL */ + clocks_per_pll = count; + + subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL); + if (!subclks) { + pr_err("%s: could not allocate subclks\n", __func__); + return; + } + + onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!onecell_data) { + pr_err("%s: could not allocate onecell_data\n", __func__); + goto err_clks; + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(np, "clock-output-names", + i, &clk_name); + if (rc) { + pr_err("%s: could not get clock names\n", np->name); + goto err_cell; + } + + /* + * when count == 4, there are 4 output clocks: + * /1, /2, /3, /4 respectively + * when count < 4, there are at least 2 output clocks: + * /1, /2, (/4, if count == 3) respectively. + */ + if (count == 4) + subclks[i] = clk_register_fixed_factor(NULL, clk_name, + parent_name, 0, mult, 1 + i); + else + + subclks[i] = clk_register_fixed_factor(NULL, clk_name, + parent_name, 0, mult, 1 << i); + + if (IS_ERR(subclks[i])) { + pr_err("%s: could not register clock\n", clk_name); + goto err_cell; + } + } + + onecell_data->clks = subclks; + onecell_data->clk_num = count; + + rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data); + if (rc) { + pr_err("Could not register clk provider for node:%s\n", + np->name); + goto err_cell; + } + + return; +err_cell: + kfree(onecell_data); +err_clks: + kfree(subclks); +} + +static const struct of_device_id clk_match[] __initconst = { + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { .compatible = "fsl,core-pll-clock", .data = core_pll_init, }, + { .compatible = "fsl,core-mux-clock", .data = core_mux_init, }, + {} +}; + +static int __init ppc_corenet_clk_probe(struct platform_device *pdev) +{ + struct device_node *np; + + np = pdev->dev.of_node; + base = of_iomap(np, 0); + if (!base) { + dev_err(&pdev->dev, "iomap error\n"); + return -ENOMEM; + } + of_clk_init(clk_match); + + return 0; +} + +static const struct of_device_id ppc_clk_ids[] __initconst = { + { .compatible = "fsl,qoriq-clockgen-1.0", }, + { .compatible = "fsl,qoriq-clockgen-2.0", }, + {} +}; + +static struct platform_driver ppc_corenet_clk_driver = { + .driver = { + .name = "ppc_corenet_clock", + .owner = THIS_MODULE, + .of_match_table = ppc_clk_ids, + }, + .probe = ppc_corenet_clk_probe, +}; + +static int __init ppc_corenet_clk_init(void) +{ + return platform_driver_register(&ppc_corenet_clk_driver); +} +subsys_initcall(ppc_corenet_clk_init); diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index 643ca653fef0..5ab95f1ad579 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1034,7 +1034,7 @@ enum prima2_clk_index { usb0, usb1, maxclk, }; -static __initdata struct clk_hw* prima2_clk_hw_array[maxclk] = { +static struct clk_hw *prima2_clk_hw_array[maxclk] __initdata = { NULL, /* dummy */ NULL, &clk_pll1.hw, diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c new file mode 100644 index 000000000000..7be41e676a64 --- /dev/null +++ b/drivers/clk/clk-s2mps11.c @@ -0,0 +1,273 @@ +/* + * clk-s2mps11.c - Clock driver for S2MPS11. + * + * Copyright (C) 2013 Samsung Electornics + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/clkdev.h> +#include <linux/regmap.h> +#include <linux/clk-provider.h> +#include <linux/platform_device.h> +#include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/core.h> + +#define s2mps11_name(a) (a->hw.init->name) + +static struct clk **clk_table; +static struct clk_onecell_data clk_data; + +enum { + S2MPS11_CLK_AP = 0, + S2MPS11_CLK_CP, + S2MPS11_CLK_BT, + S2MPS11_CLKS_NUM, +}; + +struct s2mps11_clk { + struct sec_pmic_dev *iodev; + struct clk_hw hw; + struct clk *clk; + struct clk_lookup *lookup; + u32 mask; + bool enabled; +}; + +static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw) +{ + return container_of(hw, struct s2mps11_clk, hw); +} + +static int s2mps11_clk_prepare(struct clk_hw *hw) +{ + struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); + int ret; + + ret = regmap_update_bits(s2mps11->iodev->regmap, + S2MPS11_REG_RTC_CTRL, + s2mps11->mask, s2mps11->mask); + if (!ret) + s2mps11->enabled = true; + + return ret; +} + +static void s2mps11_clk_unprepare(struct clk_hw *hw) +{ + struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); + int ret; + + ret = regmap_update_bits(s2mps11->iodev->regmap, S2MPS11_REG_RTC_CTRL, + s2mps11->mask, ~s2mps11->mask); + + if (!ret) + s2mps11->enabled = false; +} + +static int s2mps11_clk_is_enabled(struct clk_hw *hw) +{ + struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); + + return s2mps11->enabled; +} + +static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); + if (s2mps11->enabled) + return 32768; + else + return 0; +} + +static struct clk_ops s2mps11_clk_ops = { + .prepare = s2mps11_clk_prepare, + .unprepare = s2mps11_clk_unprepare, + .is_enabled = s2mps11_clk_is_enabled, + .recalc_rate = s2mps11_clk_recalc_rate, +}; + +static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = { + [S2MPS11_CLK_AP] = { + .name = "s2mps11_ap", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, + [S2MPS11_CLK_CP] = { + .name = "s2mps11_cp", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, + [S2MPS11_CLK_BT] = { + .name = "s2mps11_bt", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, +}; + +static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev) +{ + struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct device_node *clk_np; + int i; + + if (!iodev->dev->of_node) + return NULL; + + clk_np = of_find_node_by_name(iodev->dev->of_node, "clocks"); + if (!clk_np) { + dev_err(&pdev->dev, "could not find clock sub-node\n"); + return ERR_PTR(-EINVAL); + } + + clk_table = devm_kzalloc(&pdev->dev, sizeof(struct clk *) * + S2MPS11_CLKS_NUM, GFP_KERNEL); + if (!clk_table) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < S2MPS11_CLKS_NUM; i++) + of_property_read_string_index(clk_np, "clock-output-names", i, + &s2mps11_clks_init[i].name); + + return clk_np; +} + +static int s2mps11_clk_probe(struct platform_device *pdev) +{ + struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct s2mps11_clk *s2mps11_clks, *s2mps11_clk; + struct device_node *clk_np = NULL; + int i, ret = 0; + u32 val; + + s2mps11_clks = devm_kzalloc(&pdev->dev, sizeof(*s2mps11_clk) * + S2MPS11_CLKS_NUM, GFP_KERNEL); + if (!s2mps11_clks) + return -ENOMEM; + + s2mps11_clk = s2mps11_clks; + + clk_np = s2mps11_clk_parse_dt(pdev); + if (IS_ERR(clk_np)) + return PTR_ERR(clk_np); + + for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) { + s2mps11_clk->iodev = iodev; + s2mps11_clk->hw.init = &s2mps11_clks_init[i]; + s2mps11_clk->mask = 1 << i; + + ret = regmap_read(s2mps11_clk->iodev->regmap, + S2MPS11_REG_RTC_CTRL, &val); + if (ret < 0) + goto err_reg; + + s2mps11_clk->enabled = val & s2mps11_clk->mask; + + s2mps11_clk->clk = devm_clk_register(&pdev->dev, + &s2mps11_clk->hw); + if (IS_ERR(s2mps11_clk->clk)) { + dev_err(&pdev->dev, "Fail to register : %s\n", + s2mps11_name(s2mps11_clk)); + ret = PTR_ERR(s2mps11_clk->clk); + goto err_reg; + } + + s2mps11_clk->lookup = devm_kzalloc(&pdev->dev, + sizeof(struct clk_lookup), GFP_KERNEL); + if (!s2mps11_clk->lookup) { + ret = -ENOMEM; + goto err_lup; + } + + s2mps11_clk->lookup->con_id = s2mps11_name(s2mps11_clk); + s2mps11_clk->lookup->clk = s2mps11_clk->clk; + + clkdev_add(s2mps11_clk->lookup); + } + + if (clk_table) { + for (i = 0; i < S2MPS11_CLKS_NUM; i++) + clk_table[i] = s2mps11_clks[i].clk; + + clk_data.clks = clk_table; + clk_data.clk_num = S2MPS11_CLKS_NUM; + of_clk_add_provider(clk_np, of_clk_src_onecell_get, &clk_data); + } + + platform_set_drvdata(pdev, s2mps11_clks); + + return ret; +err_lup: + devm_clk_unregister(&pdev->dev, s2mps11_clk->clk); +err_reg: + while (s2mps11_clk > s2mps11_clks) { + if (s2mps11_clk->lookup) { + clkdev_drop(s2mps11_clk->lookup); + devm_clk_unregister(&pdev->dev, s2mps11_clk->clk); + } + s2mps11_clk--; + } + + return ret; +} + +static int s2mps11_clk_remove(struct platform_device *pdev) +{ + struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < S2MPS11_CLKS_NUM; i++) + clkdev_drop(s2mps11_clks[i].lookup); + + return 0; +} + +static const struct platform_device_id s2mps11_clk_id[] = { + { "s2mps11-clk", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); + +static struct platform_driver s2mps11_clk_driver = { + .driver = { + .name = "s2mps11-clk", + .owner = THIS_MODULE, + }, + .probe = s2mps11_clk_probe, + .remove = s2mps11_clk_remove, + .id_table = s2mps11_clk_id, +}; + +static int __init s2mps11_clk_init(void) +{ + return platform_driver_register(&s2mps11_clk_driver); +} +subsys_initcall(s2mps11_clk_init); + +static void __init s2mps11_clk_cleanup(void) +{ + platform_driver_unregister(&s2mps11_clk_driver); +} +module_exit(s2mps11_clk_cleanup); + +MODULE_DESCRIPTION("S2MPS11 Clock Driver"); +MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 24f553673b72..c50e83744b0a 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -851,6 +851,41 @@ static int _si5351_clkout_set_drive_strength( return 0; } +static int _si5351_clkout_set_disable_state( + struct si5351_driver_data *drvdata, int num, + enum si5351_disable_state state) +{ + u8 reg = (num < 4) ? SI5351_CLK3_0_DISABLE_STATE : + SI5351_CLK7_4_DISABLE_STATE; + u8 shift = (num < 4) ? (2 * num) : (2 * (num-4)); + u8 mask = SI5351_CLK_DISABLE_STATE_MASK << shift; + u8 val; + + if (num > 8) + return -EINVAL; + + switch (state) { + case SI5351_DISABLE_LOW: + val = SI5351_CLK_DISABLE_STATE_LOW; + break; + case SI5351_DISABLE_HIGH: + val = SI5351_CLK_DISABLE_STATE_HIGH; + break; + case SI5351_DISABLE_FLOATING: + val = SI5351_CLK_DISABLE_STATE_FLOAT; + break; + case SI5351_DISABLE_NEVER: + val = SI5351_CLK_DISABLE_STATE_NEVER; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, reg, mask, val << shift); + + return 0; +} + static int si5351_clkout_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = @@ -1225,6 +1260,33 @@ static int si5351_dt_parse(struct i2c_client *client) } } + if (!of_property_read_u32(child, "silabs,disable-state", + &val)) { + switch (val) { + case 0: + pdata->clkout[num].disable_state = + SI5351_DISABLE_LOW; + break; + case 1: + pdata->clkout[num].disable_state = + SI5351_DISABLE_HIGH; + break; + case 2: + pdata->clkout[num].disable_state = + SI5351_DISABLE_FLOATING; + break; + case 3: + pdata->clkout[num].disable_state = + SI5351_DISABLE_NEVER; + break; + default: + dev_err(&client->dev, + "invalid disable state %d for clkout %d\n", + val, num); + return -EINVAL; + } + } + if (!of_property_read_u32(child, "clock-frequency", &val)) pdata->clkout[num].rate = val; @@ -1281,9 +1343,6 @@ static int si5351_i2c_probe(struct i2c_client *client, /* 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, @@ -1327,6 +1386,15 @@ static int si5351_i2c_probe(struct i2c_client *client, n, pdata->clkout[n].drive); return ret; } + + ret = _si5351_clkout_set_disable_state(drvdata, n, + pdata->clkout[n].disable_state); + if (ret) { + dev_err(&client->dev, + "failed set disable state of clkout%d to %d\n", + n, pdata->clkout[n].disable_state); + return ret; + } } /* register xtal input clock gate */ @@ -1500,7 +1568,10 @@ static int si5351_i2c_probe(struct i2c_client *client, } static const struct i2c_device_id si5351_i2c_ids[] = { - { "silabs,si5351", 0 }, + { "si5351a", 0 }, + { "si5351a-msop", 0 }, + { "si5351b", 0 }, + { "si5351c", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids); diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h index af41b5080f43..c0dbf2676872 100644 --- a/drivers/clk/clk-si5351.h +++ b/drivers/clk/clk-si5351.h @@ -81,6 +81,7 @@ #define SI5351_CLK3_0_DISABLE_STATE 24 #define SI5351_CLK7_4_DISABLE_STATE 25 +#define SI5351_CLK_DISABLE_STATE_MASK 3 #define SI5351_CLK_DISABLE_STATE_LOW 0 #define SI5351_CLK_DISABLE_STATE_HIGH 1 #define SI5351_CLK_DISABLE_STATE_FLOAT 2 diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index 3af729b1b89d..1ada79a28052 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -95,14 +95,14 @@ static int twl6040_clk_probe(struct platform_device *pdev) if (IS_ERR(clkdata->clk)) return PTR_ERR(clkdata->clk); - dev_set_drvdata(&pdev->dev, clkdata); + platform_set_drvdata(pdev, clkdata); return 0; } static int twl6040_clk_remove(struct platform_device *pdev) { - struct twl6040_clk *clkdata = dev_get_drvdata(&pdev->dev); + struct twl6040_clk *clkdata = platform_get_drvdata(pdev); clk_unregister(clkdata->clk); diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c index a15f7928fb11..3efbdd078d14 100644 --- a/drivers/clk/clk-u300.c +++ b/drivers/clk/clk-u300.c @@ -11,7 +11,349 @@ #include <linux/io.h> #include <linux/clk-provider.h> #include <linux/spinlock.h> -#include <mach/syscon.h> +#include <linux/of.h> + +/* APP side SYSCON registers */ +/* CLK Control Register 16bit (R/W) */ +#define U300_SYSCON_CCR (0x0000) +#define U300_SYSCON_CCR_I2S1_USE_VCXO (0x0040) +#define U300_SYSCON_CCR_I2S0_USE_VCXO (0x0020) +#define U300_SYSCON_CCR_TURN_VCXO_ON (0x0008) +#define U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK (0x0007) +#define U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER (0x04) +#define U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW (0x03) +#define U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE (0x02) +#define U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH (0x01) +#define U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST (0x00) +/* CLK Status Register 16bit (R/W) */ +#define U300_SYSCON_CSR (0x0004) +#define U300_SYSCON_CSR_PLL208_LOCK_IND (0x0002) +#define U300_SYSCON_CSR_PLL13_LOCK_IND (0x0001) +/* Reset lines for SLOW devices 16bit (R/W) */ +#define U300_SYSCON_RSR (0x0014) +#define U300_SYSCON_RSR_PPM_RESET_EN (0x0200) +#define U300_SYSCON_RSR_ACC_TMR_RESET_EN (0x0100) +#define U300_SYSCON_RSR_APP_TMR_RESET_EN (0x0080) +#define U300_SYSCON_RSR_RTC_RESET_EN (0x0040) +#define U300_SYSCON_RSR_KEYPAD_RESET_EN (0x0020) +#define U300_SYSCON_RSR_GPIO_RESET_EN (0x0010) +#define U300_SYSCON_RSR_EH_RESET_EN (0x0008) +#define U300_SYSCON_RSR_BTR_RESET_EN (0x0004) +#define U300_SYSCON_RSR_UART_RESET_EN (0x0002) +#define U300_SYSCON_RSR_SLOW_BRIDGE_RESET_EN (0x0001) +/* Reset lines for FAST devices 16bit (R/W) */ +#define U300_SYSCON_RFR (0x0018) +#define U300_SYSCON_RFR_UART1_RESET_ENABLE (0x0080) +#define U300_SYSCON_RFR_SPI_RESET_ENABLE (0x0040) +#define U300_SYSCON_RFR_MMC_RESET_ENABLE (0x0020) +#define U300_SYSCON_RFR_PCM_I2S1_RESET_ENABLE (0x0010) +#define U300_SYSCON_RFR_PCM_I2S0_RESET_ENABLE (0x0008) +#define U300_SYSCON_RFR_I2C1_RESET_ENABLE (0x0004) +#define U300_SYSCON_RFR_I2C0_RESET_ENABLE (0x0002) +#define U300_SYSCON_RFR_FAST_BRIDGE_RESET_ENABLE (0x0001) +/* Reset lines for the rest of the peripherals 16bit (R/W) */ +#define U300_SYSCON_RRR (0x001c) +#define U300_SYSCON_RRR_CDS_RESET_EN (0x4000) +#define U300_SYSCON_RRR_ISP_RESET_EN (0x2000) +#define U300_SYSCON_RRR_INTCON_RESET_EN (0x1000) +#define U300_SYSCON_RRR_MSPRO_RESET_EN (0x0800) +#define U300_SYSCON_RRR_XGAM_RESET_EN (0x0100) +#define U300_SYSCON_RRR_XGAM_VC_SYNC_RESET_EN (0x0080) +#define U300_SYSCON_RRR_NANDIF_RESET_EN (0x0040) +#define U300_SYSCON_RRR_EMIF_RESET_EN (0x0020) +#define U300_SYSCON_RRR_DMAC_RESET_EN (0x0010) +#define U300_SYSCON_RRR_CPU_RESET_EN (0x0008) +#define U300_SYSCON_RRR_APEX_RESET_EN (0x0004) +#define U300_SYSCON_RRR_AHB_RESET_EN (0x0002) +#define U300_SYSCON_RRR_AAIF_RESET_EN (0x0001) +/* Clock enable for SLOW peripherals 16bit (R/W) */ +#define U300_SYSCON_CESR (0x0020) +#define U300_SYSCON_CESR_PPM_CLK_EN (0x0200) +#define U300_SYSCON_CESR_ACC_TMR_CLK_EN (0x0100) +#define U300_SYSCON_CESR_APP_TMR_CLK_EN (0x0080) +#define U300_SYSCON_CESR_KEYPAD_CLK_EN (0x0040) +#define U300_SYSCON_CESR_GPIO_CLK_EN (0x0010) +#define U300_SYSCON_CESR_EH_CLK_EN (0x0008) +#define U300_SYSCON_CESR_BTR_CLK_EN (0x0004) +#define U300_SYSCON_CESR_UART_CLK_EN (0x0002) +#define U300_SYSCON_CESR_SLOW_BRIDGE_CLK_EN (0x0001) +/* Clock enable for FAST peripherals 16bit (R/W) */ +#define U300_SYSCON_CEFR (0x0024) +#define U300_SYSCON_CEFR_UART1_CLK_EN (0x0200) +#define U300_SYSCON_CEFR_I2S1_CORE_CLK_EN (0x0100) +#define U300_SYSCON_CEFR_I2S0_CORE_CLK_EN (0x0080) +#define U300_SYSCON_CEFR_SPI_CLK_EN (0x0040) +#define U300_SYSCON_CEFR_MMC_CLK_EN (0x0020) +#define U300_SYSCON_CEFR_I2S1_CLK_EN (0x0010) +#define U300_SYSCON_CEFR_I2S0_CLK_EN (0x0008) +#define U300_SYSCON_CEFR_I2C1_CLK_EN (0x0004) +#define U300_SYSCON_CEFR_I2C0_CLK_EN (0x0002) +#define U300_SYSCON_CEFR_FAST_BRIDGE_CLK_EN (0x0001) +/* Clock enable for the rest of the peripherals 16bit (R/W) */ +#define U300_SYSCON_CERR (0x0028) +#define U300_SYSCON_CERR_CDS_CLK_EN (0x2000) +#define U300_SYSCON_CERR_ISP_CLK_EN (0x1000) +#define U300_SYSCON_CERR_MSPRO_CLK_EN (0x0800) +#define U300_SYSCON_CERR_AHB_SUBSYS_BRIDGE_CLK_EN (0x0400) +#define U300_SYSCON_CERR_SEMI_CLK_EN (0x0200) +#define U300_SYSCON_CERR_XGAM_CLK_EN (0x0100) +#define U300_SYSCON_CERR_VIDEO_ENC_CLK_EN (0x0080) +#define U300_SYSCON_CERR_NANDIF_CLK_EN (0x0040) +#define U300_SYSCON_CERR_EMIF_CLK_EN (0x0020) +#define U300_SYSCON_CERR_DMAC_CLK_EN (0x0010) +#define U300_SYSCON_CERR_CPU_CLK_EN (0x0008) +#define U300_SYSCON_CERR_APEX_CLK_EN (0x0004) +#define U300_SYSCON_CERR_AHB_CLK_EN (0x0002) +#define U300_SYSCON_CERR_AAIF_CLK_EN (0x0001) +/* Single block clock enable 16bit (-/W) */ +#define U300_SYSCON_SBCER (0x002c) +#define U300_SYSCON_SBCER_PPM_CLK_EN (0x0009) +#define U300_SYSCON_SBCER_ACC_TMR_CLK_EN (0x0008) +#define U300_SYSCON_SBCER_APP_TMR_CLK_EN (0x0007) +#define U300_SYSCON_SBCER_KEYPAD_CLK_EN (0x0006) +#define U300_SYSCON_SBCER_GPIO_CLK_EN (0x0004) +#define U300_SYSCON_SBCER_EH_CLK_EN (0x0003) +#define U300_SYSCON_SBCER_BTR_CLK_EN (0x0002) +#define U300_SYSCON_SBCER_UART_CLK_EN (0x0001) +#define U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN (0x0000) +#define U300_SYSCON_SBCER_UART1_CLK_EN (0x0019) +#define U300_SYSCON_SBCER_I2S1_CORE_CLK_EN (0x0018) +#define U300_SYSCON_SBCER_I2S0_CORE_CLK_EN (0x0017) +#define U300_SYSCON_SBCER_SPI_CLK_EN (0x0016) +#define U300_SYSCON_SBCER_MMC_CLK_EN (0x0015) +#define U300_SYSCON_SBCER_I2S1_CLK_EN (0x0014) +#define U300_SYSCON_SBCER_I2S0_CLK_EN (0x0013) +#define U300_SYSCON_SBCER_I2C1_CLK_EN (0x0012) +#define U300_SYSCON_SBCER_I2C0_CLK_EN (0x0011) +#define U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN (0x0010) +#define U300_SYSCON_SBCER_CDS_CLK_EN (0x002D) +#define U300_SYSCON_SBCER_ISP_CLK_EN (0x002C) +#define U300_SYSCON_SBCER_MSPRO_CLK_EN (0x002B) +#define U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN (0x002A) +#define U300_SYSCON_SBCER_SEMI_CLK_EN (0x0029) +#define U300_SYSCON_SBCER_XGAM_CLK_EN (0x0028) +#define U300_SYSCON_SBCER_VIDEO_ENC_CLK_EN (0x0027) +#define U300_SYSCON_SBCER_NANDIF_CLK_EN (0x0026) +#define U300_SYSCON_SBCER_EMIF_CLK_EN (0x0025) +#define U300_SYSCON_SBCER_DMAC_CLK_EN (0x0024) +#define U300_SYSCON_SBCER_CPU_CLK_EN (0x0023) +#define U300_SYSCON_SBCER_APEX_CLK_EN (0x0022) +#define U300_SYSCON_SBCER_AHB_CLK_EN (0x0021) +#define U300_SYSCON_SBCER_AAIF_CLK_EN (0x0020) +/* Single block clock disable 16bit (-/W) */ +#define U300_SYSCON_SBCDR (0x0030) +/* Same values as above for SBCER */ +/* Clock force SLOW peripherals 16bit (R/W) */ +#define U300_SYSCON_CFSR (0x003c) +#define U300_SYSCON_CFSR_PPM_CLK_FORCE_EN (0x0200) +#define U300_SYSCON_CFSR_ACC_TMR_CLK_FORCE_EN (0x0100) +#define U300_SYSCON_CFSR_APP_TMR_CLK_FORCE_EN (0x0080) +#define U300_SYSCON_CFSR_KEYPAD_CLK_FORCE_EN (0x0020) +#define U300_SYSCON_CFSR_GPIO_CLK_FORCE_EN (0x0010) +#define U300_SYSCON_CFSR_EH_CLK_FORCE_EN (0x0008) +#define U300_SYSCON_CFSR_BTR_CLK_FORCE_EN (0x0004) +#define U300_SYSCON_CFSR_UART_CLK_FORCE_EN (0x0002) +#define U300_SYSCON_CFSR_SLOW_BRIDGE_CLK_FORCE_EN (0x0001) +/* Clock force FAST peripherals 16bit (R/W) */ +#define U300_SYSCON_CFFR (0x40) +/* Values not defined. Define if you want to use them. */ +/* Clock force the rest of the peripherals 16bit (R/W) */ +#define U300_SYSCON_CFRR (0x44) +#define U300_SYSCON_CFRR_CDS_CLK_FORCE_EN (0x2000) +#define U300_SYSCON_CFRR_ISP_CLK_FORCE_EN (0x1000) +#define U300_SYSCON_CFRR_MSPRO_CLK_FORCE_EN (0x0800) +#define U300_SYSCON_CFRR_AHB_SUBSYS_BRIDGE_CLK_FORCE_EN (0x0400) +#define U300_SYSCON_CFRR_SEMI_CLK_FORCE_EN (0x0200) +#define U300_SYSCON_CFRR_XGAM_CLK_FORCE_EN (0x0100) +#define U300_SYSCON_CFRR_VIDEO_ENC_CLK_FORCE_EN (0x0080) +#define U300_SYSCON_CFRR_NANDIF_CLK_FORCE_EN (0x0040) +#define U300_SYSCON_CFRR_EMIF_CLK_FORCE_EN (0x0020) +#define U300_SYSCON_CFRR_DMAC_CLK_FORCE_EN (0x0010) +#define U300_SYSCON_CFRR_CPU_CLK_FORCE_EN (0x0008) +#define U300_SYSCON_CFRR_APEX_CLK_FORCE_EN (0x0004) +#define U300_SYSCON_CFRR_AHB_CLK_FORCE_EN (0x0002) +#define U300_SYSCON_CFRR_AAIF_CLK_FORCE_EN (0x0001) +/* PLL208 Frequency Control 16bit (R/W) */ +#define U300_SYSCON_PFCR (0x48) +#define U300_SYSCON_PFCR_DPLL_MULT_NUM (0x000F) +/* Power Management Control 16bit (R/W) */ +#define U300_SYSCON_PMCR (0x50) +#define U300_SYSCON_PMCR_DCON_ENABLE (0x0002) +#define U300_SYSCON_PMCR_PWR_MGNT_ENABLE (0x0001) +/* Reset Out 16bit (R/W) */ +#define U300_SYSCON_RCR (0x6c) +#define U300_SYSCON_RCR_RESOUT0_RST_N_DISABLE (0x0001) +/* EMIF Slew Rate Control 16bit (R/W) */ +#define U300_SYSCON_SRCLR (0x70) +#define U300_SYSCON_SRCLR_MASK (0x03FF) +#define U300_SYSCON_SRCLR_VALUE (0x03FF) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_5_B (0x0200) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_5_A (0x0100) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_4_B (0x0080) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_4_A (0x0040) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_3_B (0x0020) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_3_A (0x0010) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_2_B (0x0008) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_2_A (0x0004) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_1_B (0x0002) +#define U300_SYSCON_SRCLR_EMIF_1_SLRC_1_A (0x0001) +/* EMIF Clock Control Register 16bit (R/W) */ +#define U300_SYSCON_ECCR (0x0078) +#define U300_SYSCON_ECCR_MASK (0x000F) +#define U300_SYSCON_ECCR_EMIF_1_STATIC_CLK_EN_N_DISABLE (0x0008) +#define U300_SYSCON_ECCR_EMIF_1_RET_OUT_CLK_EN_N_DISABLE (0x0004) +#define U300_SYSCON_ECCR_EMIF_MEMCLK_RET_EN_N_DISABLE (0x0002) +#define U300_SYSCON_ECCR_EMIF_SDRCLK_RET_EN_N_DISABLE (0x0001) +/* MMC/MSPRO frequency divider register 0 16bit (R/W) */ +#define U300_SYSCON_MMF0R (0x90) +#define U300_SYSCON_MMF0R_MASK (0x00FF) +#define U300_SYSCON_MMF0R_FREQ_0_HIGH_MASK (0x00F0) +#define U300_SYSCON_MMF0R_FREQ_0_LOW_MASK (0x000F) +/* MMC/MSPRO frequency divider register 1 16bit (R/W) */ +#define U300_SYSCON_MMF1R (0x94) +#define U300_SYSCON_MMF1R_MASK (0x00FF) +#define U300_SYSCON_MMF1R_FREQ_1_HIGH_MASK (0x00F0) +#define U300_SYSCON_MMF1R_FREQ_1_LOW_MASK (0x000F) +/* Clock control for the MMC and MSPRO blocks 16bit (R/W) */ +#define U300_SYSCON_MMCR (0x9C) +#define U300_SYSCON_MMCR_MASK (0x0003) +#define U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE (0x0002) +#define U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE (0x0001) +/* SYS_0_CLK_CONTROL first clock control 16bit (R/W) */ +#define U300_SYSCON_S0CCR (0x120) +#define U300_SYSCON_S0CCR_FIELD_MASK (0x43FF) +#define U300_SYSCON_S0CCR_CLOCK_REQ (0x4000) +#define U300_SYSCON_S0CCR_CLOCK_REQ_MONITOR (0x2000) +#define U300_SYSCON_S0CCR_CLOCK_INV (0x0200) +#define U300_SYSCON_S0CCR_CLOCK_FREQ_MASK (0x01E0) +#define U300_SYSCON_S0CCR_CLOCK_SELECT_MASK (0x001E) +#define U300_SYSCON_S0CCR_CLOCK_ENABLE (0x0001) +#define U300_SYSCON_S0CCR_SEL_MCLK (0x8<<1) +#define U300_SYSCON_S0CCR_SEL_ACC_FSM_CLK (0xA<<1) +#define U300_SYSCON_S0CCR_SEL_PLL60_48_CLK (0xC<<1) +#define U300_SYSCON_S0CCR_SEL_PLL60_60_CLK (0xD<<1) +#define U300_SYSCON_S0CCR_SEL_ACC_PLL208_CLK (0xE<<1) +#define U300_SYSCON_S0CCR_SEL_APP_PLL13_CLK (0x0<<1) +#define U300_SYSCON_S0CCR_SEL_APP_FSM_CLK (0x2<<1) +#define U300_SYSCON_S0CCR_SEL_RTC_CLK (0x4<<1) +#define U300_SYSCON_S0CCR_SEL_APP_PLL208_CLK (0x6<<1) +/* SYS_1_CLK_CONTROL second clock control 16 bit (R/W) */ +#define U300_SYSCON_S1CCR (0x124) +#define U300_SYSCON_S1CCR_FIELD_MASK (0x43FF) +#define U300_SYSCON_S1CCR_CLOCK_REQ (0x4000) +#define U300_SYSCON_S1CCR_CLOCK_REQ_MONITOR (0x2000) +#define U300_SYSCON_S1CCR_CLOCK_INV (0x0200) +#define U300_SYSCON_S1CCR_CLOCK_FREQ_MASK (0x01E0) +#define U300_SYSCON_S1CCR_CLOCK_SELECT_MASK (0x001E) +#define U300_SYSCON_S1CCR_CLOCK_ENABLE (0x0001) +#define U300_SYSCON_S1CCR_SEL_MCLK (0x8<<1) +#define U300_SYSCON_S1CCR_SEL_ACC_FSM_CLK (0xA<<1) +#define U300_SYSCON_S1CCR_SEL_PLL60_48_CLK (0xC<<1) +#define U300_SYSCON_S1CCR_SEL_PLL60_60_CLK (0xD<<1) +#define U300_SYSCON_S1CCR_SEL_ACC_PLL208_CLK (0xE<<1) +#define U300_SYSCON_S1CCR_SEL_ACC_PLL13_CLK (0x0<<1) +#define U300_SYSCON_S1CCR_SEL_APP_FSM_CLK (0x2<<1) +#define U300_SYSCON_S1CCR_SEL_RTC_CLK (0x4<<1) +#define U300_SYSCON_S1CCR_SEL_APP_PLL208_CLK (0x6<<1) +/* SYS_2_CLK_CONTROL third clock contol 16 bit (R/W) */ +#define U300_SYSCON_S2CCR (0x128) +#define U300_SYSCON_S2CCR_FIELD_MASK (0xC3FF) +#define U300_SYSCON_S2CCR_CLK_STEAL (0x8000) +#define U300_SYSCON_S2CCR_CLOCK_REQ (0x4000) +#define U300_SYSCON_S2CCR_CLOCK_REQ_MONITOR (0x2000) +#define U300_SYSCON_S2CCR_CLOCK_INV (0x0200) +#define U300_SYSCON_S2CCR_CLOCK_FREQ_MASK (0x01E0) +#define U300_SYSCON_S2CCR_CLOCK_SELECT_MASK (0x001E) +#define U300_SYSCON_S2CCR_CLOCK_ENABLE (0x0001) +#define U300_SYSCON_S2CCR_SEL_MCLK (0x8<<1) +#define U300_SYSCON_S2CCR_SEL_ACC_FSM_CLK (0xA<<1) +#define U300_SYSCON_S2CCR_SEL_PLL60_48_CLK (0xC<<1) +#define U300_SYSCON_S2CCR_SEL_PLL60_60_CLK (0xD<<1) +#define U300_SYSCON_S2CCR_SEL_ACC_PLL208_CLK (0xE<<1) +#define U300_SYSCON_S2CCR_SEL_ACC_PLL13_CLK (0x0<<1) +#define U300_SYSCON_S2CCR_SEL_APP_FSM_CLK (0x2<<1) +#define U300_SYSCON_S2CCR_SEL_RTC_CLK (0x4<<1) +#define U300_SYSCON_S2CCR_SEL_APP_PLL208_CLK (0x6<<1) +/* SC_PLL_IRQ_CONTROL 16bit (R/W) */ +#define U300_SYSCON_PICR (0x0130) +#define U300_SYSCON_PICR_MASK (0x00FF) +#define U300_SYSCON_PICR_FORCE_PLL208_LOCK_LOW_ENABLE (0x0080) +#define U300_SYSCON_PICR_FORCE_PLL208_LOCK_HIGH_ENABLE (0x0040) +#define U300_SYSCON_PICR_FORCE_PLL13_LOCK_LOW_ENABLE (0x0020) +#define U300_SYSCON_PICR_FORCE_PLL13_LOCK_HIGH_ENABLE (0x0010) +#define U300_SYSCON_PICR_IRQMASK_PLL13_UNLOCK_ENABLE (0x0008) +#define U300_SYSCON_PICR_IRQMASK_PLL13_LOCK_ENABLE (0x0004) +#define U300_SYSCON_PICR_IRQMASK_PLL208_UNLOCK_ENABLE (0x0002) +#define U300_SYSCON_PICR_IRQMASK_PLL208_LOCK_ENABLE (0x0001) +/* SC_PLL_IRQ_STATUS 16 bit (R/-) */ +#define U300_SYSCON_PISR (0x0134) +#define U300_SYSCON_PISR_MASK (0x000F) +#define U300_SYSCON_PISR_PLL13_UNLOCK_IND (0x0008) +#define U300_SYSCON_PISR_PLL13_LOCK_IND (0x0004) +#define U300_SYSCON_PISR_PLL208_UNLOCK_IND (0x0002) +#define U300_SYSCON_PISR_PLL208_LOCK_IND (0x0001) +/* SC_PLL_IRQ_CLEAR 16 bit (-/W) */ +#define U300_SYSCON_PICLR (0x0138) +#define U300_SYSCON_PICLR_MASK (0x000F) +#define U300_SYSCON_PICLR_RWMASK (0x0000) +#define U300_SYSCON_PICLR_PLL13_UNLOCK_SC (0x0008) +#define U300_SYSCON_PICLR_PLL13_LOCK_SC (0x0004) +#define U300_SYSCON_PICLR_PLL208_UNLOCK_SC (0x0002) +#define U300_SYSCON_PICLR_PLL208_LOCK_SC (0x0001) +/* Clock activity observability register 0 */ +#define U300_SYSCON_C0OAR (0x140) +#define U300_SYSCON_C0OAR_MASK (0xFFFF) +#define U300_SYSCON_C0OAR_VALUE (0xFFFF) +#define U300_SYSCON_C0OAR_BT_H_CLK (0x8000) +#define U300_SYSCON_C0OAR_ASPB_P_CLK (0x4000) +#define U300_SYSCON_C0OAR_APP_SEMI_H_CLK (0x2000) +#define U300_SYSCON_C0OAR_APP_SEMI_CLK (0x1000) +#define U300_SYSCON_C0OAR_APP_MMC_MSPRO_CLK (0x0800) +#define U300_SYSCON_C0OAR_APP_I2S1_CLK (0x0400) +#define U300_SYSCON_C0OAR_APP_I2S0_CLK (0x0200) +#define U300_SYSCON_C0OAR_APP_CPU_CLK (0x0100) +#define U300_SYSCON_C0OAR_APP_52_CLK (0x0080) +#define U300_SYSCON_C0OAR_APP_208_CLK (0x0040) +#define U300_SYSCON_C0OAR_APP_104_CLK (0x0020) +#define U300_SYSCON_C0OAR_APEX_CLK (0x0010) +#define U300_SYSCON_C0OAR_AHPB_M_H_CLK (0x0008) +#define U300_SYSCON_C0OAR_AHB_CLK (0x0004) +#define U300_SYSCON_C0OAR_AFPB_P_CLK (0x0002) +#define U300_SYSCON_C0OAR_AAIF_CLK (0x0001) +/* Clock activity observability register 1 */ +#define U300_SYSCON_C1OAR (0x144) +#define U300_SYSCON_C1OAR_MASK (0x3FFE) +#define U300_SYSCON_C1OAR_VALUE (0x3FFE) +#define U300_SYSCON_C1OAR_NFIF_F_CLK (0x2000) +#define U300_SYSCON_C1OAR_MSPRO_CLK (0x1000) +#define U300_SYSCON_C1OAR_MMC_P_CLK (0x0800) +#define U300_SYSCON_C1OAR_MMC_CLK (0x0400) +#define U300_SYSCON_C1OAR_KP_P_CLK (0x0200) +#define U300_SYSCON_C1OAR_I2C1_P_CLK (0x0100) +#define U300_SYSCON_C1OAR_I2C0_P_CLK (0x0080) +#define U300_SYSCON_C1OAR_GPIO_CLK (0x0040) +#define U300_SYSCON_C1OAR_EMIF_MPMC_CLK (0x0020) +#define U300_SYSCON_C1OAR_EMIF_H_CLK (0x0010) +#define U300_SYSCON_C1OAR_EVHIST_CLK (0x0008) +#define U300_SYSCON_C1OAR_PPM_CLK (0x0004) +#define U300_SYSCON_C1OAR_DMA_CLK (0x0002) +/* Clock activity observability register 2 */ +#define U300_SYSCON_C2OAR (0x148) +#define U300_SYSCON_C2OAR_MASK (0x0FFF) +#define U300_SYSCON_C2OAR_VALUE (0x0FFF) +#define U300_SYSCON_C2OAR_XGAM_CDI_CLK (0x0800) +#define U300_SYSCON_C2OAR_XGAM_CLK (0x0400) +#define U300_SYSCON_C2OAR_VC_H_CLK (0x0200) +#define U300_SYSCON_C2OAR_VC_CLK (0x0100) +#define U300_SYSCON_C2OAR_UA_P_CLK (0x0080) +#define U300_SYSCON_C2OAR_TMR1_CLK (0x0040) +#define U300_SYSCON_C2OAR_TMR0_CLK (0x0020) +#define U300_SYSCON_C2OAR_SPI_P_CLK (0x0010) +#define U300_SYSCON_C2OAR_PCM_I2S1_CORE_CLK (0x0008) +#define U300_SYSCON_C2OAR_PCM_I2S1_CLK (0x0004) +#define U300_SYSCON_C2OAR_PCM_I2S0_CORE_CLK (0x0002) +#define U300_SYSCON_C2OAR_PCM_I2S0_CLK (0x0001) + /* * The clocking hierarchy currently looks like this. @@ -386,6 +728,213 @@ syscon_clk_register(struct device *dev, const char *name, return clk; } +#define U300_CLK_TYPE_SLOW 0 +#define U300_CLK_TYPE_FAST 1 +#define U300_CLK_TYPE_REST 2 + +/** + * struct u300_clock - defines the bits and pieces for a certain clock + * @type: the clock type, slow fast or rest + * @id: the bit in the slow/fast/rest register for this clock + * @hw_ctrld: whether the clock is hardware controlled + * @clk_val: a value to poke in the one-write enable/disable registers + */ +struct u300_clock { + u8 type; + u8 id; + bool hw_ctrld; + u16 clk_val; +}; + +static struct u300_clock const u300_clk_lookup[] __initconst = { + { + .type = U300_CLK_TYPE_REST, + .id = 3, + .hw_ctrld = true, + .clk_val = U300_SYSCON_SBCER_CPU_CLK_EN, + }, + { + .type = U300_CLK_TYPE_REST, + .id = 4, + .hw_ctrld = true, + .clk_val = U300_SYSCON_SBCER_DMAC_CLK_EN, + }, + { + .type = U300_CLK_TYPE_REST, + .id = 5, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_EMIF_CLK_EN, + }, + { + .type = U300_CLK_TYPE_REST, + .id = 6, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_NANDIF_CLK_EN, + }, + { + .type = U300_CLK_TYPE_REST, + .id = 8, + .hw_ctrld = true, + .clk_val = U300_SYSCON_SBCER_XGAM_CLK_EN, + }, + { + .type = U300_CLK_TYPE_REST, + .id = 9, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_SEMI_CLK_EN, + }, + { + .type = U300_CLK_TYPE_REST, + .id = 10, + .hw_ctrld = true, + .clk_val = U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN, + }, + { + .type = U300_CLK_TYPE_REST, + .id = 12, + .hw_ctrld = false, + /* INTCON: cannot be enabled, just taken out of reset */ + .clk_val = 0xFFFFU, + }, + { + .type = U300_CLK_TYPE_FAST, + .id = 0, + .hw_ctrld = true, + .clk_val = U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN, + }, + { + .type = U300_CLK_TYPE_FAST, + .id = 1, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_I2C0_CLK_EN, + }, + { + .type = U300_CLK_TYPE_FAST, + .id = 2, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_I2C1_CLK_EN, + }, + { + .type = U300_CLK_TYPE_FAST, + .id = 5, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_MMC_CLK_EN, + }, + { + .type = U300_CLK_TYPE_FAST, + .id = 6, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_SPI_CLK_EN, + }, + { + .type = U300_CLK_TYPE_SLOW, + .id = 0, + .hw_ctrld = true, + .clk_val = U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN, + }, + { + .type = U300_CLK_TYPE_SLOW, + .id = 1, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_UART_CLK_EN, + }, + { + .type = U300_CLK_TYPE_SLOW, + .id = 4, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_GPIO_CLK_EN, + }, + { + .type = U300_CLK_TYPE_SLOW, + .id = 6, + .hw_ctrld = true, + /* No clock enable register bit */ + .clk_val = 0xFFFFU, + }, + { + .type = U300_CLK_TYPE_SLOW, + .id = 7, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_APP_TMR_CLK_EN, + }, + { + .type = U300_CLK_TYPE_SLOW, + .id = 8, + .hw_ctrld = false, + .clk_val = U300_SYSCON_SBCER_ACC_TMR_CLK_EN, + }, +}; + +static void __init of_u300_syscon_clk_init(struct device_node *np) +{ + struct clk *clk = ERR_PTR(-EINVAL); + const char *clk_name = np->name; + const char *parent_name; + void __iomem *res_reg; + void __iomem *en_reg; + u32 clk_type; + u32 clk_id; + int i; + + if (of_property_read_u32(np, "clock-type", &clk_type)) { + pr_err("%s: syscon clock \"%s\" missing clock-type property\n", + __func__, clk_name); + return; + } + if (of_property_read_u32(np, "clock-id", &clk_id)) { + pr_err("%s: syscon clock \"%s\" missing clock-id property\n", + __func__, clk_name); + return; + } + parent_name = of_clk_get_parent_name(np, 0); + + switch (clk_type) { + case U300_CLK_TYPE_SLOW: + res_reg = syscon_vbase + U300_SYSCON_RSR; + en_reg = syscon_vbase + U300_SYSCON_CESR; + break; + case U300_CLK_TYPE_FAST: + res_reg = syscon_vbase + U300_SYSCON_RFR; + en_reg = syscon_vbase + U300_SYSCON_CEFR; + break; + case U300_CLK_TYPE_REST: + res_reg = syscon_vbase + U300_SYSCON_RRR; + en_reg = syscon_vbase + U300_SYSCON_CERR; + break; + default: + pr_err("unknown clock type %x specified\n", clk_type); + return; + } + + for (i = 0; i < ARRAY_SIZE(u300_clk_lookup); i++) { + const struct u300_clock *u3clk = &u300_clk_lookup[i]; + + if (u3clk->type == clk_type && u3clk->id == clk_id) + clk = syscon_clk_register(NULL, + clk_name, parent_name, + 0, u3clk->hw_ctrld, + res_reg, u3clk->id, + en_reg, u3clk->id, + u3clk->clk_val); + } + + if (!IS_ERR(clk)) { + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + /* + * Some few system clocks - device tree does not + * represent clocks without a corresponding device node. + * for now we add these three clocks here. + */ + if (clk_type == U300_CLK_TYPE_REST && clk_id == 5) + clk_register_clkdev(clk, NULL, "pl172"); + if (clk_type == U300_CLK_TYPE_REST && clk_id == 9) + clk_register_clkdev(clk, NULL, "semi"); + if (clk_type == U300_CLK_TYPE_REST && clk_id == 12) + clk_register_clkdev(clk, NULL, "intcon"); + } +} + /** * struct clk_mclk - U300 MCLK clock (MMC/SD clock) * @hw: corresponding clock hardware entry @@ -590,10 +1139,41 @@ mclk_clk_register(struct device *dev, const char *name, return clk; } +static void __init of_u300_syscon_mclk_init(struct device_node *np) +{ + struct clk *clk = ERR_PTR(-EINVAL); + const char *clk_name = np->name; + const char *parent_name; + + parent_name = of_clk_get_parent_name(np, 0); + clk = mclk_clk_register(NULL, clk_name, parent_name, false); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static const struct of_device_id u300_clk_match[] __initconst = { + { + .compatible = "fixed-clock", + .data = of_fixed_clk_setup, + }, + { + .compatible = "fixed-factor-clock", + .data = of_fixed_factor_clk_setup, + }, + { + .compatible = "stericsson,u300-syscon-clk", + .data = of_u300_syscon_clk_init, + }, + { + .compatible = "stericsson,u300-syscon-mclk", + .data = of_u300_syscon_mclk_init, + }, +}; + + void __init u300_clk_init(void __iomem *base) { u16 val; - struct clk *clk; syscon_vbase = base; @@ -610,137 +1190,5 @@ void __init u300_clk_init(void __iomem *base) val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE; writew(val, syscon_vbase + U300_SYSCON_PMCR); - /* These are always available (RTC and PLL13) */ - clk = clk_register_fixed_rate(NULL, "app_32_clk", NULL, - CLK_IS_ROOT, 32768); - /* The watchdog sits directly on the 32 kHz clock */ - clk_register_clkdev(clk, NULL, "coh901327_wdog"); - clk = clk_register_fixed_rate(NULL, "pll13", NULL, - CLK_IS_ROOT, 13000000); - - /* These derive from PLL208 */ - clk = clk_register_fixed_rate(NULL, "pll208", NULL, - CLK_IS_ROOT, 208000000); - clk = clk_register_fixed_factor(NULL, "app_208_clk", "pll208", - 0, 1, 1); - clk = clk_register_fixed_factor(NULL, "app_104_clk", "pll208", - 0, 1, 2); - clk = clk_register_fixed_factor(NULL, "app_52_clk", "pll208", - 0, 1, 4); - /* The 52 MHz is divided down to 26 MHz */ - clk = clk_register_fixed_factor(NULL, "app_26_clk", "app_52_clk", - 0, 1, 2); - - /* Directly on the AMBA interconnect */ - clk = syscon_clk_register(NULL, "cpu_clk", "app_208_clk", 0, true, - syscon_vbase + U300_SYSCON_RRR, 3, - syscon_vbase + U300_SYSCON_CERR, 3, - U300_SYSCON_SBCER_CPU_CLK_EN); - clk = syscon_clk_register(NULL, "dmac_clk", "app_52_clk", 0, true, - syscon_vbase + U300_SYSCON_RRR, 4, - syscon_vbase + U300_SYSCON_CERR, 4, - U300_SYSCON_SBCER_DMAC_CLK_EN); - clk_register_clkdev(clk, NULL, "dma"); - clk = syscon_clk_register(NULL, "fsmc_clk", "app_52_clk", 0, false, - syscon_vbase + U300_SYSCON_RRR, 6, - syscon_vbase + U300_SYSCON_CERR, 6, - U300_SYSCON_SBCER_NANDIF_CLK_EN); - clk_register_clkdev(clk, NULL, "fsmc-nand"); - clk = syscon_clk_register(NULL, "xgam_clk", "app_52_clk", 0, true, - syscon_vbase + U300_SYSCON_RRR, 8, - syscon_vbase + U300_SYSCON_CERR, 8, - U300_SYSCON_SBCER_XGAM_CLK_EN); - clk_register_clkdev(clk, NULL, "xgam"); - clk = syscon_clk_register(NULL, "semi_clk", "app_104_clk", 0, false, - syscon_vbase + U300_SYSCON_RRR, 9, - syscon_vbase + U300_SYSCON_CERR, 9, - U300_SYSCON_SBCER_SEMI_CLK_EN); - clk_register_clkdev(clk, NULL, "semi"); - - /* AHB bridge clocks */ - clk = syscon_clk_register(NULL, "ahb_subsys_clk", "app_52_clk", 0, true, - syscon_vbase + U300_SYSCON_RRR, 10, - syscon_vbase + U300_SYSCON_CERR, 10, - U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN); - clk = syscon_clk_register(NULL, "intcon_clk", "ahb_subsys_clk", 0, false, - syscon_vbase + U300_SYSCON_RRR, 12, - syscon_vbase + U300_SYSCON_CERR, 12, - /* Cannot be enabled, just taken out of reset */ - 0xFFFFU); - clk_register_clkdev(clk, NULL, "intcon"); - clk = syscon_clk_register(NULL, "emif_clk", "ahb_subsys_clk", 0, false, - syscon_vbase + U300_SYSCON_RRR, 5, - syscon_vbase + U300_SYSCON_CERR, 5, - U300_SYSCON_SBCER_EMIF_CLK_EN); - clk_register_clkdev(clk, NULL, "pl172"); - - /* FAST bridge clocks */ - clk = syscon_clk_register(NULL, "fast_clk", "app_26_clk", 0, true, - syscon_vbase + U300_SYSCON_RFR, 0, - syscon_vbase + U300_SYSCON_CEFR, 0, - U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN); - clk = syscon_clk_register(NULL, "i2c0_p_clk", "fast_clk", 0, false, - syscon_vbase + U300_SYSCON_RFR, 1, - syscon_vbase + U300_SYSCON_CEFR, 1, - U300_SYSCON_SBCER_I2C0_CLK_EN); - clk_register_clkdev(clk, NULL, "stu300.0"); - clk = syscon_clk_register(NULL, "i2c1_p_clk", "fast_clk", 0, false, - syscon_vbase + U300_SYSCON_RFR, 2, - syscon_vbase + U300_SYSCON_CEFR, 2, - U300_SYSCON_SBCER_I2C1_CLK_EN); - clk_register_clkdev(clk, NULL, "stu300.1"); - clk = syscon_clk_register(NULL, "mmc_p_clk", "fast_clk", 0, false, - syscon_vbase + U300_SYSCON_RFR, 5, - syscon_vbase + U300_SYSCON_CEFR, 5, - U300_SYSCON_SBCER_MMC_CLK_EN); - clk_register_clkdev(clk, "apb_pclk", "mmci"); - clk = syscon_clk_register(NULL, "spi_p_clk", "fast_clk", 0, false, - syscon_vbase + U300_SYSCON_RFR, 6, - syscon_vbase + U300_SYSCON_CEFR, 6, - U300_SYSCON_SBCER_SPI_CLK_EN); - /* The SPI has no external clock for the outward bus, uses the pclk */ - clk_register_clkdev(clk, NULL, "pl022"); - clk_register_clkdev(clk, "apb_pclk", "pl022"); - - /* SLOW bridge clocks */ - clk = syscon_clk_register(NULL, "slow_clk", "pll13", 0, true, - syscon_vbase + U300_SYSCON_RSR, 0, - syscon_vbase + U300_SYSCON_CESR, 0, - U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN); - clk = syscon_clk_register(NULL, "uart0_clk", "slow_clk", 0, false, - syscon_vbase + U300_SYSCON_RSR, 1, - syscon_vbase + U300_SYSCON_CESR, 1, - U300_SYSCON_SBCER_UART_CLK_EN); - /* Same clock is used for APB and outward bus */ - clk_register_clkdev(clk, NULL, "uart0"); - clk_register_clkdev(clk, "apb_pclk", "uart0"); - clk = syscon_clk_register(NULL, "gpio_clk", "slow_clk", 0, false, - syscon_vbase + U300_SYSCON_RSR, 4, - syscon_vbase + U300_SYSCON_CESR, 4, - U300_SYSCON_SBCER_GPIO_CLK_EN); - clk_register_clkdev(clk, NULL, "u300-gpio"); - clk = syscon_clk_register(NULL, "keypad_clk", "slow_clk", 0, false, - syscon_vbase + U300_SYSCON_RSR, 5, - syscon_vbase + U300_SYSCON_CESR, 6, - U300_SYSCON_SBCER_KEYPAD_CLK_EN); - clk_register_clkdev(clk, NULL, "coh901461-keypad"); - clk = syscon_clk_register(NULL, "rtc_clk", "slow_clk", 0, true, - syscon_vbase + U300_SYSCON_RSR, 6, - /* No clock enable register bit */ - NULL, 0, 0xFFFFU); - clk_register_clkdev(clk, NULL, "rtc-coh901331"); - clk = syscon_clk_register(NULL, "app_tmr_clk", "slow_clk", 0, false, - syscon_vbase + U300_SYSCON_RSR, 7, - syscon_vbase + U300_SYSCON_CESR, 7, - U300_SYSCON_SBCER_APP_TMR_CLK_EN); - clk_register_clkdev(clk, NULL, "apptimer"); - clk = syscon_clk_register(NULL, "acc_tmr_clk", "slow_clk", 0, false, - syscon_vbase + U300_SYSCON_RSR, 8, - syscon_vbase + U300_SYSCON_CESR, 8, - U300_SYSCON_SBCER_ACC_TMR_CLK_EN); - clk_register_clkdev(clk, NULL, "timer"); - - /* Then this special MMC/SD clock */ - clk = mclk_clk_register(NULL, "mmc_clk", "mmc_p_clk", false); - clk_register_clkdev(clk, NULL, "mmci"); + of_clk_init(u300_clk_match); } diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index 553ac35bcc91..82306f5fb9c2 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -42,6 +42,7 @@ struct clk_device { #define PLL_TYPE_VT8500 0 #define PLL_TYPE_WM8650 1 #define PLL_TYPE_WM8750 2 +#define PLL_TYPE_WM8850 3 struct clk_pll { struct clk_hw hw; @@ -156,10 +157,6 @@ 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 < parent_rate) - divisor++; - if (divisor == cdev->div_mask + 1) divisor = 0; @@ -327,6 +324,15 @@ CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init); #define WM8750_BITS_TO_VAL(f, m, d1, d2) \ ((f << 24) | ((m - 1) << 16) | ((d1 - 1) << 8) | d2) +/* Helper macros for PLL_WM8850 */ +#define WM8850_PLL_MUL(x) ((((x >> 16) & 0x7F) + 1) * 2) +#define WM8850_PLL_DIV(x) ((((x >> 8) & 1) + 1) * (1 << (x & 3))) + +#define WM8850_BITS_TO_FREQ(r, m, d1, d2) \ + (r * ((m + 1) * 2) / ((d1+1) * (1 << d2))) + +#define WM8850_BITS_TO_VAL(m, d1, d2) \ + ((((m / 2) - 1) << 16) | ((d1 - 1) << 8) | d2) static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate, u32 *multiplier, u32 *prediv) @@ -466,6 +472,49 @@ static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate, *divisor2 = best_div2; } +static void wm8850_find_pll_bits(unsigned long rate, unsigned long parent_rate, + u32 *multiplier, u32 *divisor1, u32 *divisor2) +{ + u32 mul, div1, div2; + u32 best_mul, best_div1, best_div2; + unsigned long tclk, rate_err, best_err; + + best_err = (unsigned long)-1; + + /* Find the closest match (lower or equal to requested) */ + for (div1 = 1; div1 >= 0; div1--) + for (div2 = 3; div2 >= 0; div2--) + for (mul = 0; mul <= 127; mul++) { + tclk = parent_rate * ((mul + 1) * 2) / + ((div1 + 1) * (1 << div2)); + if (tclk > rate) + continue; + /* error will always be +ve */ + rate_err = rate - tclk; + if (rate_err == 0) { + *multiplier = mul; + *divisor1 = div1; + *divisor2 = div2; + return; + } + + if (rate_err < best_err) { + best_err = rate_err; + best_mul = mul; + best_div1 = div1; + best_div2 = div2; + } + } + + /* if we got here, it wasn't an exact match */ + pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, + rate - best_err); + + *multiplier = best_mul; + *divisor1 = best_div1; + *divisor2 = best_div2; +} + static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -489,6 +538,10 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2); pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2); break; + case PLL_TYPE_WM8850: + wm8850_find_pll_bits(rate, parent_rate, &mul, &div1, &div2); + pll_val = WM8850_BITS_TO_VAL(mul, div1, div2); + break; default: pr_err("%s: invalid pll type\n", __func__); return 0; @@ -525,6 +578,10 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate, wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2); round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2); break; + case PLL_TYPE_WM8850: + wm8850_find_pll_bits(rate, *prate, &mul, &div1, &div2); + round_rate = WM8850_BITS_TO_FREQ(*prate, mul, div1, div2); + break; default: round_rate = 0; } @@ -552,6 +609,10 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw, pll_freq = parent_rate * WM8750_PLL_MUL(pll_val); pll_freq /= WM8750_PLL_DIV(pll_val); break; + case PLL_TYPE_WM8850: + pll_freq = parent_rate * WM8850_PLL_MUL(pll_val); + pll_freq /= WM8850_PLL_DIV(pll_val); + break; default: pll_freq = 0; } @@ -628,6 +689,12 @@ static void __init wm8750_pll_init(struct device_node *node) } CLK_OF_DECLARE(wm8750_pll, "wm,wm8750-pll-clock", wm8750_pll_init); +static void __init wm8850_pll_init(struct device_node *node) +{ + vtwm_pll_clk_init(node, PLL_TYPE_WM8850); +} +CLK_OF_DECLARE(wm8850_pll, "wm,wm8850-pll-clock", wm8850_pll_init); + void __init vtwm_clk_init(void __iomem *base) { if (!base) diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 16ed06808554..805b4c344006 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -31,7 +31,7 @@ struct wm831x_clk { bool xtal_ena; }; -static int wm831x_xtal_is_enabled(struct clk_hw *hw) +static int wm831x_xtal_is_prepared(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, xtal_hw); @@ -52,7 +52,7 @@ static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw, } static const struct clk_ops wm831x_xtal_ops = { - .is_enabled = wm831x_xtal_is_enabled, + .is_prepared = wm831x_xtal_is_prepared, .recalc_rate = wm831x_xtal_recalc_rate, }; @@ -73,7 +73,7 @@ static const unsigned long wm831x_fll_auto_rates[] = { 24576000, }; -static int wm831x_fll_is_enabled(struct clk_hw *hw) +static int wm831x_fll_is_prepared(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, fll_hw); @@ -97,7 +97,7 @@ static int wm831x_fll_prepare(struct clk_hw *hw) struct wm831x *wm831x = clkdata->wm831x; int ret; - ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, + ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, WM831X_FLL_ENA); if (ret != 0) dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret); @@ -114,9 +114,9 @@ static void wm831x_fll_unprepare(struct clk_hw *hw) struct wm831x *wm831x = clkdata->wm831x; int ret; - ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, WM831X_FLL_ENA, 0); + ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, 0); if (ret != 0) - dev_crit(wm831x->dev, "Failed to disaable FLL: %d\n", ret); + dev_crit(wm831x->dev, "Failed to disable FLL: %d\n", ret); } static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw, @@ -170,7 +170,7 @@ static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate, if (i == ARRAY_SIZE(wm831x_fll_auto_rates)) return -EINVAL; - if (wm831x_fll_is_enabled(hw)) + if (wm831x_fll_is_prepared(hw)) return -EPERM; return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2, @@ -220,7 +220,7 @@ static u8 wm831x_fll_get_parent(struct clk_hw *hw) } static const struct clk_ops wm831x_fll_ops = { - .is_enabled = wm831x_fll_is_enabled, + .is_prepared = wm831x_fll_is_prepared, .prepare = wm831x_fll_prepare, .unprepare = wm831x_fll_unprepare, .round_rate = wm831x_fll_round_rate, @@ -237,7 +237,7 @@ static struct clk_init_data wm831x_fll_init = { .flags = CLK_SET_RATE_GATE, }; -static int wm831x_clkout_is_enabled(struct clk_hw *hw) +static int wm831x_clkout_is_prepared(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, clkout_hw); @@ -299,8 +299,8 @@ static void wm831x_clkout_unprepare(struct clk_hw *hw) } static const char *wm831x_clkout_parents[] = { - "xtal", "fll", + "xtal", }; static u8 wm831x_clkout_get_parent(struct clk_hw *hw) @@ -318,9 +318,9 @@ static u8 wm831x_clkout_get_parent(struct clk_hw *hw) } if (ret & WM831X_CLKOUT_SRC) - return 0; - else return 1; + else + return 0; } static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent) @@ -335,7 +335,7 @@ static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent) } static const struct clk_ops wm831x_clkout_ops = { - .is_enabled = wm831x_clkout_is_enabled, + .is_prepared = wm831x_clkout_is_prepared, .prepare = wm831x_clkout_prepare, .unprepare = wm831x_clkout_unprepare, .get_parent = wm831x_clkout_get_parent, @@ -360,6 +360,8 @@ static int wm831x_clk_probe(struct platform_device *pdev) if (!clkdata) return -ENOMEM; + clkdata->wm831x = wm831x; + /* XTAL_ENA can only be set via OTP/InstantConfig so just read once */ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); if (ret < 0) { @@ -384,7 +386,7 @@ static int wm831x_clk_probe(struct platform_device *pdev) if (IS_ERR(clkdata->clkout)) return PTR_ERR(clkdata->clkout); - dev_set_drvdata(&pdev->dev, clkdata); + platform_set_drvdata(pdev, clkdata); return 0; } diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c deleted file mode 100644 index 32062977f453..000000000000 --- a/drivers/clk/clk-zynq.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright (c) 2012 National Instruments - * - * Josh Cartwright <josh.cartwright@ni.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ -#include <linux/io.h> -#include <linux/of.h> -#include <linux/slab.h> -#include <linux/kernel.h> -#include <linux/clk-provider.h> -#include <linux/clk/zynq.h> - -static void __iomem *slcr_base; - -struct zynq_pll_clk { - struct clk_hw hw; - void __iomem *pll_ctrl; - void __iomem *pll_cfg; -}; - -#define to_zynq_pll_clk(hw) container_of(hw, struct zynq_pll_clk, hw) - -#define CTRL_PLL_FDIV(x) ((x) >> 12) - -static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct zynq_pll_clk *pll = to_zynq_pll_clk(hw); - return parent_rate * CTRL_PLL_FDIV(ioread32(pll->pll_ctrl)); -} - -static const struct clk_ops zynq_pll_clk_ops = { - .recalc_rate = zynq_pll_recalc_rate, -}; - -static void __init zynq_pll_clk_setup(struct device_node *np) -{ - struct clk_init_data init; - struct zynq_pll_clk *pll; - const char *parent_name; - struct clk *clk; - u32 regs[2]; - int ret; - - ret = of_property_read_u32_array(np, "reg", regs, ARRAY_SIZE(regs)); - if (WARN_ON(ret)) - return; - - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (WARN_ON(!pll)) - return; - - pll->pll_ctrl = slcr_base + regs[0]; - pll->pll_cfg = slcr_base + regs[1]; - - of_property_read_string(np, "clock-output-names", &init.name); - - init.ops = &zynq_pll_clk_ops; - parent_name = of_clk_get_parent_name(np, 0); - init.parent_names = &parent_name; - init.num_parents = 1; - - pll->hw.init = &init; - - clk = clk_register(NULL, &pll->hw); - if (WARN_ON(IS_ERR(clk))) - return; - - ret = of_clk_add_provider(np, of_clk_src_simple_get, clk); - if (WARN_ON(ret)) - return; -} -CLK_OF_DECLARE(zynq_pll, "xlnx,zynq-pll", zynq_pll_clk_setup); - -struct zynq_periph_clk { - struct clk_hw hw; - struct clk_onecell_data onecell_data; - struct clk *gates[2]; - void __iomem *clk_ctrl; - spinlock_t clkact_lock; -}; - -#define to_zynq_periph_clk(hw) container_of(hw, struct zynq_periph_clk, hw) - -static const u8 periph_clk_parent_map[] = { - 0, 0, 1, 2 -}; -#define PERIPH_CLK_CTRL_SRC(x) (periph_clk_parent_map[((x) & 0x30) >> 4]) -#define PERIPH_CLK_CTRL_DIV(x) (((x) & 0x3F00) >> 8) - -static unsigned long zynq_periph_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct zynq_periph_clk *periph = to_zynq_periph_clk(hw); - return parent_rate / PERIPH_CLK_CTRL_DIV(ioread32(periph->clk_ctrl)); -} - -static u8 zynq_periph_get_parent(struct clk_hw *hw) -{ - struct zynq_periph_clk *periph = to_zynq_periph_clk(hw); - return PERIPH_CLK_CTRL_SRC(ioread32(periph->clk_ctrl)); -} - -static const struct clk_ops zynq_periph_clk_ops = { - .recalc_rate = zynq_periph_recalc_rate, - .get_parent = zynq_periph_get_parent, -}; - -static void __init zynq_periph_clk_setup(struct device_node *np) -{ - struct zynq_periph_clk *periph; - const char *parent_names[3]; - struct clk_init_data init; - int clk_num = 0, err; - const char *name; - struct clk *clk; - u32 reg; - int i; - - err = of_property_read_u32(np, "reg", ®); - if (WARN_ON(err)) - return; - - periph = kzalloc(sizeof(*periph), GFP_KERNEL); - if (WARN_ON(!periph)) - return; - - periph->clk_ctrl = slcr_base + reg; - spin_lock_init(&periph->clkact_lock); - - init.name = np->name; - init.ops = &zynq_periph_clk_ops; - for (i = 0; i < ARRAY_SIZE(parent_names); i++) - parent_names[i] = of_clk_get_parent_name(np, i); - init.parent_names = parent_names; - init.num_parents = ARRAY_SIZE(parent_names); - - periph->hw.init = &init; - - clk = clk_register(NULL, &periph->hw); - if (WARN_ON(IS_ERR(clk))) - return; - - err = of_clk_add_provider(np, of_clk_src_simple_get, clk); - if (WARN_ON(err)) - return; - - err = of_property_read_string_index(np, "clock-output-names", 0, - &name); - if (WARN_ON(err)) - return; - - periph->gates[0] = clk_register_gate(NULL, name, np->name, 0, - periph->clk_ctrl, 0, 0, - &periph->clkact_lock); - if (WARN_ON(IS_ERR(periph->gates[0]))) - return; - clk_num++; - - /* some periph clks have 2 downstream gates */ - err = of_property_read_string_index(np, "clock-output-names", 1, - &name); - if (err != -ENODATA) { - periph->gates[1] = clk_register_gate(NULL, name, np->name, 0, - periph->clk_ctrl, 1, 0, - &periph->clkact_lock); - if (WARN_ON(IS_ERR(periph->gates[1]))) - return; - clk_num++; - } - - periph->onecell_data.clks = periph->gates; - periph->onecell_data.clk_num = clk_num; - - err = of_clk_add_provider(np, of_clk_src_onecell_get, - &periph->onecell_data); - if (WARN_ON(err)) - return; -} -CLK_OF_DECLARE(zynq_periph, "xlnx,zynq-periph-clock", zynq_periph_clk_setup); - -/* CPU Clock domain is modelled as a mux with 4 children subclks, whose - * derivative rates depend on CLK_621_TRUE - */ - -struct zynq_cpu_clk { - struct clk_hw hw; - struct clk_onecell_data onecell_data; - struct clk *subclks[4]; - void __iomem *clk_ctrl; - spinlock_t clkact_lock; -}; - -#define to_zynq_cpu_clk(hw) container_of(hw, struct zynq_cpu_clk, hw) - -static const u8 zynq_cpu_clk_parent_map[] = { - 1, 1, 2, 0 -}; -#define CPU_CLK_SRCSEL(x) (zynq_cpu_clk_parent_map[(((x) & 0x30) >> 4)]) -#define CPU_CLK_CTRL_DIV(x) (((x) & 0x3F00) >> 8) - -static u8 zynq_cpu_clk_get_parent(struct clk_hw *hw) -{ - struct zynq_cpu_clk *cpuclk = to_zynq_cpu_clk(hw); - return CPU_CLK_SRCSEL(ioread32(cpuclk->clk_ctrl)); -} - -static unsigned long zynq_cpu_clk_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct zynq_cpu_clk *cpuclk = to_zynq_cpu_clk(hw); - return parent_rate / CPU_CLK_CTRL_DIV(ioread32(cpuclk->clk_ctrl)); -} - -static const struct clk_ops zynq_cpu_clk_ops = { - .get_parent = zynq_cpu_clk_get_parent, - .recalc_rate = zynq_cpu_clk_recalc_rate, -}; - -struct zynq_cpu_subclk { - struct clk_hw hw; - void __iomem *clk_621; - enum { - CPU_SUBCLK_6X4X, - CPU_SUBCLK_3X2X, - CPU_SUBCLK_2X, - CPU_SUBCLK_1X, - } which; -}; - -#define CLK_621_TRUE(x) ((x) & 1) - -#define to_zynq_cpu_subclk(hw) container_of(hw, struct zynq_cpu_subclk, hw); - -static unsigned long zynq_cpu_subclk_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - unsigned long uninitialized_var(rate); - struct zynq_cpu_subclk *subclk; - bool is_621; - - subclk = to_zynq_cpu_subclk(hw) - is_621 = CLK_621_TRUE(ioread32(subclk->clk_621)); - - switch (subclk->which) { - case CPU_SUBCLK_6X4X: - rate = parent_rate; - break; - case CPU_SUBCLK_3X2X: - rate = parent_rate / 2; - break; - case CPU_SUBCLK_2X: - rate = parent_rate / (is_621 ? 3 : 2); - break; - case CPU_SUBCLK_1X: - rate = parent_rate / (is_621 ? 6 : 4); - break; - }; - - return rate; -} - -static const struct clk_ops zynq_cpu_subclk_ops = { - .recalc_rate = zynq_cpu_subclk_recalc_rate, -}; - -static struct clk *zynq_cpu_subclk_setup(struct device_node *np, u8 which, - void __iomem *clk_621) -{ - struct zynq_cpu_subclk *subclk; - struct clk_init_data init; - struct clk *clk; - int err; - - err = of_property_read_string_index(np, "clock-output-names", - which, &init.name); - if (WARN_ON(err)) - goto err_read_output_name; - - subclk = kzalloc(sizeof(*subclk), GFP_KERNEL); - if (!subclk) - goto err_subclk_alloc; - - subclk->clk_621 = clk_621; - subclk->which = which; - - init.ops = &zynq_cpu_subclk_ops; - init.parent_names = &np->name; - init.num_parents = 1; - - subclk->hw.init = &init; - - clk = clk_register(NULL, &subclk->hw); - if (WARN_ON(IS_ERR(clk))) - goto err_clk_register; - - return clk; - -err_clk_register: - kfree(subclk); -err_subclk_alloc: -err_read_output_name: - return ERR_PTR(-EINVAL); -} - -static void __init zynq_cpu_clk_setup(struct device_node *np) -{ - struct zynq_cpu_clk *cpuclk; - const char *parent_names[3]; - struct clk_init_data init; - void __iomem *clk_621; - struct clk *clk; - u32 reg[2]; - int err; - int i; - - err = of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg)); - if (WARN_ON(err)) - return; - - cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); - if (WARN_ON(!cpuclk)) - return; - - cpuclk->clk_ctrl = slcr_base + reg[0]; - clk_621 = slcr_base + reg[1]; - spin_lock_init(&cpuclk->clkact_lock); - - init.name = np->name; - init.ops = &zynq_cpu_clk_ops; - for (i = 0; i < ARRAY_SIZE(parent_names); i++) - parent_names[i] = of_clk_get_parent_name(np, i); - init.parent_names = parent_names; - init.num_parents = ARRAY_SIZE(parent_names); - - cpuclk->hw.init = &init; - - clk = clk_register(NULL, &cpuclk->hw); - if (WARN_ON(IS_ERR(clk))) - return; - - err = of_clk_add_provider(np, of_clk_src_simple_get, clk); - if (WARN_ON(err)) - return; - - for (i = 0; i < 4; i++) { - cpuclk->subclks[i] = zynq_cpu_subclk_setup(np, i, clk_621); - if (WARN_ON(IS_ERR(cpuclk->subclks[i]))) - return; - } - - cpuclk->onecell_data.clks = cpuclk->subclks; - cpuclk->onecell_data.clk_num = i; - - err = of_clk_add_provider(np, of_clk_src_onecell_get, - &cpuclk->onecell_data); - if (WARN_ON(err)) - return; -} -CLK_OF_DECLARE(zynq_cpu, "xlnx,zynq-cpu-clock", zynq_cpu_clk_setup); - -void __init xilinx_zynq_clocks_init(void __iomem *slcr) -{ - slcr_base = slcr; - of_clk_init(NULL); -} diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 934cfd18f72d..a004769528e6 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -107,7 +107,7 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "%*s%-*s %-11d %-12d %-10lu", level * 3 + 1, "", 30 - level * 3, c->name, - c->enable_count, c->prepare_count, c->rate); + c->enable_count, c->prepare_count, clk_get_rate(c)); seq_printf(s, "\n"); } @@ -166,7 +166,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "\"%s\": { ", c->name); seq_printf(s, "\"enable_count\": %d,", c->enable_count); seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); - seq_printf(s, "\"rate\": %lu", c->rate); + seq_printf(s, "\"rate\": %lu", clk_get_rate(c)); } static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) @@ -458,7 +458,6 @@ static void clk_unprepare_unused_subtree(struct clk *clk) 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) @@ -534,7 +533,7 @@ static int clk_disable_unused(void) return 0; } -late_initcall(clk_disable_unused); +late_initcall_sync(clk_disable_unused); /*** helper functions ***/ @@ -559,6 +558,19 @@ struct clk *__clk_get_parent(struct clk *clk) return !clk ? NULL : clk->parent; } +struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) +{ + if (!clk || index >= clk->num_parents) + return NULL; + else if (!clk->parents) + return __clk_lookup(clk->parent_names[index]); + else if (!clk->parents[index]) + return clk->parents[index] = + __clk_lookup(clk->parent_names[index]); + else + return clk->parents[index]; +} + unsigned int __clk_get_enable_count(struct clk *clk) { return !clk ? 0 : clk->enable_count; @@ -594,6 +606,7 @@ unsigned long __clk_get_flags(struct clk *clk) { return !clk ? 0 : clk->flags; } +EXPORT_SYMBOL_GPL(__clk_get_flags); bool __clk_is_prepared(struct clk *clk) { @@ -679,6 +692,55 @@ struct clk *__clk_lookup(const char *name) return NULL; } +/* + * Helper for finding best parent to provide a given frequency. This can be used + * directly as a determine_rate callback (e.g. for a mux), or from a more + * complex clock that may combine a mux with other operations. + */ +long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p) +{ + struct clk *clk = hw->clk, *parent, *best_parent = NULL; + int i, num_parents; + unsigned long parent_rate, best = 0; + + /* if NO_REPARENT flag set, pass through to current parent */ + if (clk->flags & CLK_SET_RATE_NO_REPARENT) { + parent = clk->parent; + if (clk->flags & CLK_SET_RATE_PARENT) + best = __clk_round_rate(parent, rate); + else if (parent) + best = __clk_get_rate(parent); + else + best = __clk_get_rate(clk); + goto out; + } + + /* find the parent that can provide the fastest rate <= rate */ + num_parents = clk->num_parents; + for (i = 0; i < num_parents; i++) { + parent = clk_get_parent_by_index(clk, i); + if (!parent) + continue; + if (clk->flags & CLK_SET_RATE_PARENT) + parent_rate = __clk_round_rate(parent, rate); + else + parent_rate = __clk_get_rate(parent); + if (parent_rate <= rate && parent_rate > best) { + best_parent = parent; + best = parent_rate; + } + } + +out: + if (best_parent) + *best_parent_p = best_parent; + *best_parent_rate = best; + + return best; +} + /*** clk api ***/ void __clk_unprepare(struct clk *clk) @@ -702,7 +764,7 @@ void __clk_unprepare(struct clk *clk) /** * clk_unprepare - undo preparation of a clock source - * @clk: the clk being unprepare + * @clk: the clk being unprepared * * clk_unprepare may sleep, which differentiates it from clk_disable. In a * simple case, clk_unprepare can be used instead of clk_disable to gate a clk @@ -869,27 +931,31 @@ EXPORT_SYMBOL_GPL(clk_enable); /** * __clk_round_rate - round the given rate for a clk * @clk: round the rate of this clock + * @rate: the rate which is to be rounded * * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate */ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { unsigned long parent_rate = 0; + struct clk *parent; if (!clk) return 0; - if (!clk->ops->round_rate) { - if (clk->flags & CLK_SET_RATE_PARENT) - return __clk_round_rate(clk->parent, rate); - else - return clk->rate; - } - - if (clk->parent) - parent_rate = clk->parent->rate; - - return clk->ops->round_rate(clk->hw, rate, &parent_rate); + parent = clk->parent; + if (parent) + parent_rate = parent->rate; + + if (clk->ops->determine_rate) + return clk->ops->determine_rate(clk->hw, rate, &parent_rate, + &parent); + else if (clk->ops->round_rate) + return clk->ops->round_rate(clk->hw, rate, &parent_rate); + else if (clk->flags & CLK_SET_RATE_PARENT) + return __clk_round_rate(clk->parent, rate); + else + return clk->rate; } /** @@ -956,7 +1022,7 @@ static int __clk_notify(struct clk *clk, unsigned long msg, * * Walks the subtree of clks starting with clk and recalculates rates as it * goes. Note that if a clk does not implement the .recalc_rate callback then - * it is assumed that the clock will take on the rate of it's parent. + * it is assumed that the clock will take on the rate of its parent. * * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, * if necessary. @@ -1014,6 +1080,115 @@ unsigned long clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_rate); +static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) +{ + u8 i; + + if (!clk->parents) + clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), + GFP_KERNEL); + + /* + * find index of new parent clock using cached parent ptrs, + * or if not yet cached, use string name comparison and cache + * them now to avoid future calls to __clk_lookup. + */ + for (i = 0; i < clk->num_parents; i++) { + if (clk->parents && clk->parents[i] == parent) + break; + else if (!strcmp(clk->parent_names[i], parent->name)) { + if (clk->parents) + clk->parents[i] = __clk_lookup(parent->name); + break; + } + } + + return i; +} + +static void clk_reparent(struct clk *clk, struct clk *new_parent) +{ + hlist_del(&clk->child_node); + + if (new_parent) { + /* avoid duplicate POST_RATE_CHANGE notifications */ + if (new_parent->new_child == clk) + new_parent->new_child = NULL; + + hlist_add_head(&clk->child_node, &new_parent->children); + } else { + hlist_add_head(&clk->child_node, &clk_orphan_list); + } + + clk->parent = new_parent; +} + +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; + + /* + * Migrate prepare state between parents and prevent race with + * clk_enable(). + * + * If the clock is not prepared, then a race with + * clk_enable/disable() is impossible since we already have the + * prepare lock (future calls to clk_enable() need to be preceded by + * a clk_prepare()). + * + * If the clock is prepared, migrate the prepared state to the new + * parent and also protect against a race with clk_enable() by + * forcing the clock and the new parent on. This ensures that all + * future calls to clk_enable() are practically NOPs with respect to + * hardware and software states. + * + * See also: Comment for clk_set_parent() below. + */ + if (clk->prepare_count) { + __clk_prepare(parent); + clk_enable(parent); + clk_enable(clk); + } + + /* update the clk tree topology */ + flags = clk_enable_lock(); + clk_reparent(clk, parent); + clk_enable_unlock(flags); + + /* change clock input source */ + if (parent && clk->ops->set_parent) + ret = clk->ops->set_parent(clk->hw, p_index); + + if (ret) { + flags = clk_enable_lock(); + clk_reparent(clk, old_parent); + clk_enable_unlock(flags); + + if (clk->prepare_count) { + clk_disable(clk); + clk_disable(parent); + __clk_unprepare(parent); + } + return ret; + } + + /* + * Finish the migration of prepare state and undo the changes done + * for preventing a race with clk_enable(). + */ + if (clk->prepare_count) { + clk_disable(clk); + clk_disable(old_parent); + __clk_unprepare(old_parent); + } + + /* update debugfs with new clk tree topology */ + clk_debug_reparent(clk, parent); + return 0; +} + /** * __clk_speculate_rates * @clk: first clk in the subtree @@ -1026,7 +1201,7 @@ EXPORT_SYMBOL_GPL(clk_get_rate); * pre-rate change notifications and returns early if no clks in the * subtree have subscribed to the notifications. Note that if a clk does not * implement the .recalc_rate callback then it is assumed that the clock will - * take on the rate of it's parent. + * take on the rate of its parent. * * Caller must hold prepare_lock. */ @@ -1058,18 +1233,25 @@ out: return ret; } -static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) +static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + struct clk *new_parent, u8 p_index) { struct clk *child; clk->new_rate = new_rate; + clk->new_parent = new_parent; + clk->new_parent_index = p_index; + /* include clk in new parent's PRE_RATE_CHANGE notifications */ + clk->new_child = NULL; + if (new_parent && new_parent != clk->parent) + new_parent->new_child = clk; hlist_for_each_entry(child, &clk->children, child_node) { if (child->ops->recalc_rate) child->new_rate = child->ops->recalc_rate(child->hw, new_rate); else child->new_rate = new_rate; - clk_calc_subtree(child, child->new_rate); + clk_calc_subtree(child, child->new_rate, NULL, 0); } } @@ -1080,50 +1262,63 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) { struct clk *top = clk; + struct clk *old_parent, *parent; unsigned long best_parent_rate = 0; unsigned long new_rate; + u8 p_index = 0; /* sanity */ if (IS_ERR_OR_NULL(clk)) return NULL; /* save parent rate, if it exists */ - if (clk->parent) - best_parent_rate = clk->parent->rate; - - /* never propagate up to the parent */ - if (!(clk->flags & CLK_SET_RATE_PARENT)) { - if (!clk->ops->round_rate) { - clk->new_rate = clk->rate; - return NULL; - } - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); + parent = old_parent = clk->parent; + if (parent) + best_parent_rate = parent->rate; + + /* find the closest rate and parent clk/rate */ + if (clk->ops->determine_rate) { + new_rate = clk->ops->determine_rate(clk->hw, rate, + &best_parent_rate, + &parent); + } else if (clk->ops->round_rate) { + new_rate = clk->ops->round_rate(clk->hw, rate, + &best_parent_rate); + } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) { + /* pass-through clock without adjustable parent */ + clk->new_rate = clk->rate; + return NULL; + } else { + /* pass-through clock with adjustable parent */ + top = clk_calc_new_rates(parent, rate); + new_rate = parent->new_rate; goto out; } - /* need clk->parent from here on out */ - if (!clk->parent) { - pr_debug("%s: %s has NULL parent\n", __func__, clk->name); + /* some clocks must be gated to change parent */ + if (parent != old_parent && + (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + pr_debug("%s: %s not gated but wants to reparent\n", + __func__, clk->name); return NULL; } - if (!clk->ops->round_rate) { - top = clk_calc_new_rates(clk->parent, rate); - new_rate = clk->parent->new_rate; - - goto out; + /* try finding the new parent index */ + if (parent) { + p_index = clk_fetch_parent_index(clk, parent); + if (p_index == clk->num_parents) { + pr_debug("%s: clk %s can not be parent of clk %s\n", + __func__, parent->name, clk->name); + return NULL; + } } - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); - - if (best_parent_rate != clk->parent->rate) { - top = clk_calc_new_rates(clk->parent, best_parent_rate); - - goto out; - } + if ((clk->flags & CLK_SET_RATE_PARENT) && parent && + best_parent_rate != parent->rate) + top = clk_calc_new_rates(parent, best_parent_rate); out: - clk_calc_subtree(clk, new_rate); + clk_calc_subtree(clk, new_rate, parent, p_index); return top; } @@ -1135,7 +1330,7 @@ out: */ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event) { - struct clk *child, *fail_clk = NULL; + struct clk *child, *tmp_clk, *fail_clk = NULL; int ret = NOTIFY_DONE; if (clk->rate == clk->new_rate) @@ -1148,9 +1343,19 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even } hlist_for_each_entry(child, &clk->children, child_node) { - clk = clk_propagate_rate_change(child, event); - if (clk) - fail_clk = clk; + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) + continue; + tmp_clk = clk_propagate_rate_change(child, event); + if (tmp_clk) + fail_clk = tmp_clk; + } + + /* handle the new child who might not be in clk->children yet */ + if (clk->new_child) { + tmp_clk = clk_propagate_rate_change(clk->new_child, event); + if (tmp_clk) + fail_clk = tmp_clk; } return fail_clk; @@ -1168,6 +1373,10 @@ static void clk_change_rate(struct clk *clk) old_rate = clk->rate; + /* set parent */ + if (clk->new_parent && clk->new_parent != clk->parent) + __clk_set_parent(clk, clk->new_parent, clk->new_parent_index); + if (clk->parent) best_parent_rate = clk->parent->rate; @@ -1182,8 +1391,16 @@ static void clk_change_rate(struct clk *clk) if (clk->notifier_count && old_rate != clk->rate) __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &clk->children, child_node) { + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) + continue; clk_change_rate(child); + } + + /* handle the new child who might not be in clk->children yet */ + if (clk->new_child) + clk_change_rate(clk->new_child); } /** @@ -1198,7 +1415,7 @@ static void clk_change_rate(struct clk *clk) * outcome of clk's .round_rate implementation. If *parent_rate is unchanged * after calling .round_rate then upstream parent propagation is ignored. If * *parent_rate comes back with a new rate for clk's parent then we propagate - * up to clk's parent and set it's rate. Upward propagation will continue + * up to clk's parent and set its rate. Upward propagation will continue * until either a clk does not support the CLK_SET_RATE_PARENT flag or * .round_rate stops requesting changes to clk's parent_rate. * @@ -1212,11 +1429,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate) struct clk *top, *fail_clk; int ret = 0; + if (!clk) + return 0; + /* prevent racing with updates to the clock topology */ clk_prepare_lock(); /* bail early if nothing to do */ - if (rate == clk->rate) + if (rate == clk_get_rate(clk)) goto out; if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) { @@ -1315,30 +1535,12 @@ static struct clk *__clk_init_parent(struct clk *clk) kzalloc((sizeof(struct clk*) * clk->num_parents), GFP_KERNEL); - if (!clk->parents) - ret = __clk_lookup(clk->parent_names[index]); - else if (!clk->parents[index]) - ret = clk->parents[index] = - __clk_lookup(clk->parent_names[index]); - else - ret = clk->parents[index]; + ret = clk_get_parent_by_index(clk, index); out: return ret; } -static void clk_reparent(struct clk *clk, struct clk *new_parent) -{ - hlist_del(&clk->child_node); - - if (new_parent) - hlist_add_head(&clk->child_node, &new_parent->children); - else - hlist_add_head(&clk->child_node, &clk_orphan_list); - - clk->parent = new_parent; -} - void __clk_reparent(struct clk *clk, struct clk *new_parent) { clk_reparent(clk, new_parent); @@ -1346,115 +1548,22 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent) __clk_recalc_rates(clk, POST_RATE_CHANGE); } -static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) -{ - u8 i; - - if (!clk->parents) - clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), - GFP_KERNEL); - - /* - * find index of new parent clock using cached parent ptrs, - * or if not yet cached, use string name comparison and cache - * them now to avoid future calls to __clk_lookup. - */ - for (i = 0; i < clk->num_parents; i++) { - if (clk->parents && clk->parents[i] == parent) - break; - else if (!strcmp(clk->parent_names[i], parent->name)) { - if (clk->parents) - clk->parents[i] = __clk_lookup(parent->name); - break; - } - } - - return i; -} - -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); - - flags = clk_enable_lock(); - - /* migrate enable */ - if (clk->enable_count) { - __clk_enable(parent); - migrated_enable = true; - } - - /* update the clk tree topology */ - clk_reparent(clk, parent); - - clk_enable_unlock(flags); - - /* change clock input source */ - if (parent && clk->ops->set_parent) - ret = clk->ops->set_parent(clk->hw, p_index); - - 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); - clk_enable_unlock(flags); - } - - /* clean up prepare for old parent if migration was done */ - if (clk->prepare_count) - __clk_unprepare(old_parent); - - /* update debugfs with new clk tree topology */ - clk_debug_reparent(clk, parent); - return 0; -} - /** * clk_set_parent - switch the parent of a mux clk * @clk: the mux clk whose input we are switching * @parent: the new input to clk * - * Re-parent clk to use parent as it's new input source. If clk has the - * CLK_SET_PARENT_GATE flag set then clk must be gated for this - * operation to succeed. After successfully changing clk's parent - * clk_set_parent will update the clk topology, sysfs topology and - * propagate rate recalculation via __clk_recalc_rates. Returns 0 on - * success, -EERROR otherwise. + * Re-parent clk to use parent as its new input source. If clk is in + * prepared state, the clk will get enabled for the duration of this call. If + * that's not acceptable for a specific clk (Eg: the consumer can't handle + * that, the reparenting is glitchy in hardware, etc), use the + * CLK_SET_PARENT_GATE flag to allow reparenting only when clk is unprepared. + * + * After successfully changing clk's parent clk_set_parent will update the + * clk topology, sysfs topology and propagate rate recalculation via + * __clk_recalc_rates. + * + * Returns 0 on success, -EERROR otherwise. */ int clk_set_parent(struct clk *clk, struct clk *parent) { @@ -1462,7 +1571,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) u8 p_index = 0; unsigned long p_rate = 0; - if (!clk || !clk->ops) + if (!clk) + return 0; + + if (!clk->ops) return -EINVAL; /* verify ops for for multi-parent clks */ @@ -1494,8 +1606,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) } /* propagate PRE_RATE_CHANGE notifications */ - if (clk->notifier_count) - ret = __clk_speculate_rates(clk, p_rate); + ret = __clk_speculate_rates(clk, p_rate); /* abort if a driver objects */ if (ret & NOTIFY_STOP_MASK) @@ -1546,8 +1657,9 @@ int __clk_init(struct device *dev, struct clk *clk) /* check that clk_ops are sane. See Documentation/clk.txt */ if (clk->ops->set_rate && - !(clk->ops->round_rate && clk->ops->recalc_rate)) { - pr_warning("%s: %s must implement .round_rate & .recalc_rate\n", + !((clk->ops->round_rate || clk->ops->determine_rate) && + clk->ops->recalc_rate)) { + pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", __func__, clk->name); ret = -EINVAL; goto out; @@ -1630,7 +1742,7 @@ int __clk_init(struct device *dev, struct clk *clk) * this clock */ hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { - if (orphan->ops->get_parent) { + if (orphan->num_parents && orphan->ops->get_parent) { i = orphan->ops->get_parent(orphan->hw); if (!strcmp(clk->name, orphan->parent_names[i])) __clk_reparent(orphan, clk); @@ -1650,7 +1762,7 @@ int __clk_init(struct device *dev, struct clk *clk) * The .init callback is not used by any of the basic clock types, but * exists for weird hardware that must perform initialization magic. * Please consider other ways of solving initialization problems before - * using this callback, as it's use is discouraged. + * using this callback, as its use is discouraged. */ if (clk->ops->init) clk->ops->init(clk->hw); @@ -1677,7 +1789,7 @@ out: * very large numbers of clocks that need to be statically initialized. It is * a layering violation to include clk-private.h from any code which implements * a clock's .ops; as such any statically initialized clock data MUST be in a - * separate C file from the logic that implements it's operations. Returns 0 + * separate C file from the logic that implements its operations. Returns 0 * on success, otherwise an error code. */ struct clk *__clk_register(struct device *dev, struct clk_hw *hw) @@ -1955,6 +2067,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) /* XXX the notifier code should handle this better */ if (!cn->notifier_head.head) { srcu_cleanup_notifier_head(&cn->notifier_head); + list_del(&cn->node); kfree(cn); } @@ -2116,13 +2229,13 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_name); */ void __init of_clk_init(const struct of_device_id *matches) { + const struct of_device_id *match; struct device_node *np; if (!matches) matches = __clk_of_table; - for_each_matching_node(np, matches) { - const struct of_device_id *match = of_match_node(matches, np); + for_each_matching_node_and_match(np, matches, &match) { of_clk_init_cb_t clk_init_cb = match->data; clk_init_cb(np); } diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index d1f1a19d4351..b2721cae257a 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -248,7 +248,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp2-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -258,7 +259,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -268,7 +270,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -278,7 +281,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "uart3_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART3, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.3", NULL); @@ -288,7 +292,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.3"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -297,7 +302,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -306,7 +312,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.1"); clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.2", NULL); @@ -315,7 +322,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.2"); clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.3", NULL); @@ -324,7 +332,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.3"); clk = clk_register_mux(NULL, "sdh_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 8, 2, 0, &clk_lock); clk_register_clkdev(clk, "sdh_mux", NULL); @@ -354,7 +363,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "usb_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -376,7 +386,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "disp_sphy.0", NULL); clk = clk_register_mux(NULL, "disp1_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP1, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.1", NULL); @@ -394,7 +405,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "ccic_arbiter", NULL); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -421,7 +433,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0"); clk = clk_register_mux(NULL, "ccic1_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC1, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.1", NULL); diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c index 28b3b51c794b..014396b028a2 100644 --- a/drivers/clk/mmp/clk-pxa168.c +++ b/drivers/clk/mmp/clk-pxa168.c @@ -199,7 +199,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa168-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -209,7 +210,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -219,7 +221,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -229,7 +232,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -238,7 +242,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -247,7 +252,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.1"); clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.2", NULL); @@ -256,7 +262,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.2"); clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.3", NULL); @@ -265,7 +272,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.3"); clk = clk_register_mux(NULL, "ssp4_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP4, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.4", NULL); @@ -278,7 +286,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa3xx-nand.0"); clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh0_mux", NULL); @@ -287,7 +296,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "sdhci-pxa.0"); clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh1_mux", NULL); @@ -304,7 +314,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, "sph_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -317,7 +328,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, "hclk", "mmp-disp.0"); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -327,8 +339,8 @@ void __init pxa168_clk_init(void) clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent, ARRAY_SIZE(ccic_phy_parent), - CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, - 7, 1, 0, &clk_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_phy_mux.0", NULL); clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux", diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c index 6ec05698ed38..9efc6a47535d 100644 --- a/drivers/clk/mmp/clk-pxa910.c +++ b/drivers/clk/mmp/clk-pxa910.c @@ -204,7 +204,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa910-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -214,7 +215,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -224,7 +226,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbcp_base + APBCP_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -234,7 +237,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -243,7 +247,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -256,7 +261,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa3xx-nand.0"); clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh0_mux", NULL); @@ -265,7 +271,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "sdhci-pxa.0"); clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh1_mux", NULL); @@ -282,7 +289,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, "sph_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -291,7 +299,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-disp.0"); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -301,8 +310,8 @@ void __init pxa910_clk_init(void) clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent, ARRAY_SIZE(ccic_phy_parent), - CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, - 7, 1, 0, &clk_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_phy_mux.0", NULL); clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux", diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index 57323fd15ec9..0b0f3e729cf7 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -1,8 +1,23 @@ -config MVEBU_CLK_CORE - bool +config MVEBU_CLK_COMMON + bool config MVEBU_CLK_CPU - bool + bool -config MVEBU_CLK_GATING - bool +config ARMADA_370_CLK + bool + select MVEBU_CLK_COMMON + select MVEBU_CLK_CPU + +config ARMADA_XP_CLK + bool + select MVEBU_CLK_COMMON + select MVEBU_CLK_CPU + +config DOVE_CLK + bool + select MVEBU_CLK_COMMON + +config KIRKWOOD_CLK + bool + select MVEBU_CLK_COMMON diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 58df3dc49363..1c7e70c63fb2 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -1,3 +1,7 @@ -obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o +obj-$(CONFIG_MVEBU_CLK_COMMON) += common.o obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o -obj-$(CONFIG_MVEBU_CLK_GATING) += clk-gating-ctrl.o + +obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o +obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o +obj-$(CONFIG_DOVE_CLK) += dove.o +obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c new file mode 100644 index 000000000000..fc777bdc1886 --- /dev/null +++ b/drivers/clk/mvebu/armada-370.c @@ -0,0 +1,176 @@ +/* + * Marvell Armada 370 SoC clocks + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + +/* + * Core Clocks + */ + +#define SARL 0 /* Low part [0:31] */ +#define SARL_A370_PCLK_FREQ_OPT 11 +#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF +#define SARL_A370_FAB_FREQ_OPT 15 +#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F +#define SARL_A370_TCLK_FREQ_OPT 20 +#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 + +enum { A370_CPU_TO_NBCLK, A370_CPU_TO_HCLK, A370_CPU_TO_DRAMCLK }; + +static const struct coreclk_ratio a370_coreclk_ratios[] __initconst = { + { .id = A370_CPU_TO_NBCLK, .name = "nbclk" }, + { .id = A370_CPU_TO_HCLK, .name = "hclk" }, + { .id = A370_CPU_TO_DRAMCLK, .name = "dramclk" }, +}; + +static const u32 a370_tclk_freqs[] __initconst = { + 16600000, + 20000000, +}; + +static u32 __init a370_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select = 0; + + tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) & + SARL_A370_TCLK_FREQ_OPT_MASK); + return a370_tclk_freqs[tclk_freq_select]; +} + +static const u32 a370_cpu_freqs[] __initconst = { + 400000000, + 533000000, + 667000000, + 800000000, + 1000000000, + 1067000000, + 1200000000, +}; + +static u32 __init a370_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & + SARL_A370_PCLK_FREQ_OPT_MASK); + if (cpu_freq_select >= ARRAY_SIZE(a370_cpu_freqs)) { + pr_err("CPU freq select unsupported %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = a370_cpu_freqs[cpu_freq_select]; + + return cpu_freq; +} + +static const int a370_nbclk_ratios[32][2] __initconst = { + {0, 1}, {1, 2}, {2, 2}, {2, 2}, + {1, 2}, {1, 2}, {1, 1}, {2, 3}, + {0, 1}, {1, 2}, {2, 4}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {2, 2}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int a370_hclk_ratios[32][2] __initconst = { + {0, 1}, {1, 2}, {2, 6}, {2, 3}, + {1, 3}, {1, 4}, {1, 2}, {2, 6}, + {0, 1}, {1, 6}, {2, 10}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 2}, + {2, 6}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int a370_dramclk_ratios[32][2] __initconst = { + {0, 1}, {1, 2}, {2, 3}, {2, 3}, + {1, 3}, {1, 2}, {1, 2}, {2, 6}, + {0, 1}, {1, 3}, {2, 5}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init a370_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) & + SARL_A370_FAB_FREQ_OPT_MASK); + + switch (id) { + case A370_CPU_TO_NBCLK: + *mult = a370_nbclk_ratios[opt][0]; + *div = a370_nbclk_ratios[opt][1]; + break; + case A370_CPU_TO_HCLK: + *mult = a370_hclk_ratios[opt][0]; + *div = a370_hclk_ratios[opt][1]; + break; + case A370_CPU_TO_DRAMCLK: + *mult = a370_dramclk_ratios[opt][0]; + *div = a370_dramclk_ratios[opt][1]; + break; + } +} + +static const struct coreclk_soc_desc a370_coreclks = { + .get_tclk_freq = a370_get_tclk_freq, + .get_cpu_freq = a370_get_cpu_freq, + .get_clk_ratio = a370_get_clk_ratio, + .ratios = a370_coreclk_ratios, + .num_ratios = ARRAY_SIZE(a370_coreclk_ratios), +}; + +static void __init a370_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &a370_coreclks); +} +CLK_OF_DECLARE(a370_core_clk, "marvell,armada-370-core-clock", + a370_coreclk_init); + +/* + * Clock Gating Control + */ + +static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = { + { "audio", NULL, 0, 0 }, + { "pex0_en", NULL, 1, 0 }, + { "pex1_en", NULL, 2, 0 }, + { "ge1", NULL, 3, 0 }, + { "ge0", NULL, 4, 0 }, + { "pex0", "pex0_en", 5, 0 }, + { "pex1", "pex1_en", 9, 0 }, + { "sata0", NULL, 15, 0 }, + { "sdio", NULL, 17, 0 }, + { "tdm", NULL, 25, 0 }, + { "ddr", NULL, 28, CLK_IGNORE_UNUSED }, + { "sata1", NULL, 30, 0 }, + { } +}; + +static void __init a370_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, a370_gating_desc); +} +CLK_OF_DECLARE(a370_clk_gating, "marvell,armada-370-gating-clock", + a370_clk_gating_init); diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c new file mode 100644 index 000000000000..9922c4475aa8 --- /dev/null +++ b/drivers/clk/mvebu/armada-xp.c @@ -0,0 +1,210 @@ +/* + * Marvell Armada XP SoC clocks + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + +/* + * Core Clocks + * + * Armada XP Sample At Reset is a 64 bit bitfiled split in two + * register of 32 bits + */ + +#define SARL 0 /* Low part [0:31] */ +#define SARL_AXP_PCLK_FREQ_OPT 21 +#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 +#define SARL_AXP_FAB_FREQ_OPT 24 +#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF +#define SARH 4 /* High part [32:63] */ +#define SARH_AXP_PCLK_FREQ_OPT (52-32) +#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 +#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 +#define SARH_AXP_FAB_FREQ_OPT (51-32) +#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 +#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 + +enum { AXP_CPU_TO_NBCLK, AXP_CPU_TO_HCLK, AXP_CPU_TO_DRAMCLK }; + +static const struct coreclk_ratio axp_coreclk_ratios[] __initconst = { + { .id = AXP_CPU_TO_NBCLK, .name = "nbclk" }, + { .id = AXP_CPU_TO_HCLK, .name = "hclk" }, + { .id = AXP_CPU_TO_DRAMCLK, .name = "dramclk" }, +}; + +/* Armada XP TCLK frequency is fixed to 250MHz */ +static u32 __init axp_get_tclk_freq(void __iomem *sar) +{ + return 250000000; +} + +static const u32 axp_cpu_freqs[] __initconst = { + 1000000000, + 1066000000, + 1200000000, + 1333000000, + 1500000000, + 1666000000, + 1800000000, + 2000000000, + 667000000, + 0, + 800000000, + 1600000000, +}; + +static u32 __init axp_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar + SARL) >> SARL_AXP_PCLK_FREQ_OPT) & + SARL_AXP_PCLK_FREQ_OPT_MASK); + /* + * The upper bit is not contiguous to the other ones and + * located in the high part of the SAR registers + */ + cpu_freq_select |= (((readl(sar + SARH) >> SARH_AXP_PCLK_FREQ_OPT) & + SARH_AXP_PCLK_FREQ_OPT_MASK) << SARH_AXP_PCLK_FREQ_OPT_SHIFT); + if (cpu_freq_select >= ARRAY_SIZE(axp_cpu_freqs)) { + pr_err("CPU freq select unsupported: %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = axp_cpu_freqs[cpu_freq_select]; + + return cpu_freq; +} + +static const int axp_nbclk_ratios[32][2] __initconst = { + {0, 1}, {1, 2}, {2, 2}, {2, 2}, + {1, 2}, {1, 2}, {1, 1}, {2, 3}, + {0, 1}, {1, 2}, {2, 4}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {2, 2}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int axp_hclk_ratios[32][2] __initconst = { + {0, 1}, {1, 2}, {2, 6}, {2, 3}, + {1, 3}, {1, 4}, {1, 2}, {2, 6}, + {0, 1}, {1, 6}, {2, 10}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 2}, + {2, 6}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int axp_dramclk_ratios[32][2] __initconst = { + {0, 1}, {1, 2}, {2, 3}, {2, 3}, + {1, 3}, {1, 2}, {1, 2}, {2, 6}, + {0, 1}, {1, 3}, {2, 5}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init axp_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar + SARL) >> SARL_AXP_FAB_FREQ_OPT) & + SARL_AXP_FAB_FREQ_OPT_MASK); + /* + * The upper bit is not contiguous to the other ones and + * located in the high part of the SAR registers + */ + opt |= (((readl(sar + SARH) >> SARH_AXP_FAB_FREQ_OPT) & + SARH_AXP_FAB_FREQ_OPT_MASK) << SARH_AXP_FAB_FREQ_OPT_SHIFT); + + switch (id) { + case AXP_CPU_TO_NBCLK: + *mult = axp_nbclk_ratios[opt][0]; + *div = axp_nbclk_ratios[opt][1]; + break; + case AXP_CPU_TO_HCLK: + *mult = axp_hclk_ratios[opt][0]; + *div = axp_hclk_ratios[opt][1]; + break; + case AXP_CPU_TO_DRAMCLK: + *mult = axp_dramclk_ratios[opt][0]; + *div = axp_dramclk_ratios[opt][1]; + break; + } +} + +static const struct coreclk_soc_desc axp_coreclks = { + .get_tclk_freq = axp_get_tclk_freq, + .get_cpu_freq = axp_get_cpu_freq, + .get_clk_ratio = axp_get_clk_ratio, + .ratios = axp_coreclk_ratios, + .num_ratios = ARRAY_SIZE(axp_coreclk_ratios), +}; + +static void __init axp_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &axp_coreclks); +} +CLK_OF_DECLARE(axp_core_clk, "marvell,armada-xp-core-clock", + axp_coreclk_init); + +/* + * Clock Gating Control + */ + +static const struct clk_gating_soc_desc axp_gating_desc[] __initconst = { + { "audio", NULL, 0, 0 }, + { "ge3", NULL, 1, 0 }, + { "ge2", NULL, 2, 0 }, + { "ge1", NULL, 3, 0 }, + { "ge0", NULL, 4, 0 }, + { "pex00", NULL, 5, 0 }, + { "pex01", NULL, 6, 0 }, + { "pex02", NULL, 7, 0 }, + { "pex03", NULL, 8, 0 }, + { "pex10", NULL, 9, 0 }, + { "pex11", NULL, 10, 0 }, + { "pex12", NULL, 11, 0 }, + { "pex13", NULL, 12, 0 }, + { "bp", NULL, 13, 0 }, + { "sata0lnk", NULL, 14, 0 }, + { "sata0", "sata0lnk", 15, 0 }, + { "lcd", NULL, 16, 0 }, + { "sdio", NULL, 17, 0 }, + { "usb0", NULL, 18, 0 }, + { "usb1", NULL, 19, 0 }, + { "usb2", NULL, 20, 0 }, + { "xor0", NULL, 22, 0 }, + { "crypto", NULL, 23, 0 }, + { "tdm", NULL, 25, 0 }, + { "pex20", NULL, 26, 0 }, + { "pex30", NULL, 27, 0 }, + { "xor1", NULL, 28, 0 }, + { "sata1lnk", NULL, 29, 0 }, + { "sata1", "sata1lnk", 30, 0 }, + { } +}; + +static void __init axp_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, axp_gating_desc); +} +CLK_OF_DECLARE(axp_clk_gating, "marvell,armada-xp-gating-clock", + axp_clk_gating_init); diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c deleted file mode 100644 index 0a53edbae8b8..000000000000 --- a/drivers/clk/mvebu/clk-core.c +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Marvell EBU clock core handling defined at reset - * - * Copyright (C) 2012 Marvell - * - * Gregory CLEMENT <gregory.clement@free-electrons.com> - * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include <linux/kernel.h> -#include <linux/clk.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/of_address.h> -#include <linux/io.h> -#include <linux/of.h> -#include "clk-core.h" - -struct core_ratio { - int id; - const char *name; -}; - -struct core_clocks { - u32 (*get_tclk_freq)(void __iomem *sar); - u32 (*get_cpu_freq)(void __iomem *sar); - void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); - const struct core_ratio *ratios; - int num_ratios; -}; - -static struct clk_onecell_data clk_data; - -static void __init mvebu_clk_core_setup(struct device_node *np, - struct core_clocks *coreclk) -{ - const char *tclk_name = "tclk"; - const char *cpuclk_name = "cpuclk"; - void __iomem *base; - unsigned long rate; - int n; - - base = of_iomap(np, 0); - if (WARN_ON(!base)) - return; - - /* - * Allocate struct for TCLK, cpu clk, and core ratio clocks - */ - clk_data.clk_num = 2 + coreclk->num_ratios; - clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *), - GFP_KERNEL); - if (WARN_ON(!clk_data.clks)) - return; - - /* - * Register TCLK - */ - of_property_read_string_index(np, "clock-output-names", 0, - &tclk_name); - rate = coreclk->get_tclk_freq(base); - clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, - CLK_IS_ROOT, rate); - WARN_ON(IS_ERR(clk_data.clks[0])); - - /* - * Register CPU clock - */ - of_property_read_string_index(np, "clock-output-names", 1, - &cpuclk_name); - rate = coreclk->get_cpu_freq(base); - clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, - CLK_IS_ROOT, rate); - WARN_ON(IS_ERR(clk_data.clks[1])); - - /* - * Register fixed-factor clocks derived from CPU clock - */ - for (n = 0; n < coreclk->num_ratios; n++) { - const char *rclk_name = coreclk->ratios[n].name; - int mult, div; - - of_property_read_string_index(np, "clock-output-names", - 2+n, &rclk_name); - coreclk->get_clk_ratio(base, coreclk->ratios[n].id, - &mult, &div); - clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, - cpuclk_name, 0, mult, div); - WARN_ON(IS_ERR(clk_data.clks[2+n])); - }; - - /* - * SAR register isn't needed anymore - */ - iounmap(base); - - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); -} - -#ifdef CONFIG_MACH_ARMADA_370_XP -/* - * Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two - * register of 32 bits - */ - -#define SARL 0 /* Low part [0:31] */ -#define SARL_AXP_PCLK_FREQ_OPT 21 -#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 -#define SARL_A370_PCLK_FREQ_OPT 11 -#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF -#define SARL_AXP_FAB_FREQ_OPT 24 -#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF -#define SARL_A370_FAB_FREQ_OPT 15 -#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F -#define SARL_A370_TCLK_FREQ_OPT 20 -#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 -#define SARH 4 /* High part [32:63] */ -#define SARH_AXP_PCLK_FREQ_OPT (52-32) -#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 -#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 -#define SARH_AXP_FAB_FREQ_OPT (51-32) -#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 -#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 - -static const u32 __initconst armada_370_tclk_frequencies[] = { - 16600000, - 20000000, -}; - -static u32 __init armada_370_get_tclk_freq(void __iomem *sar) -{ - u8 tclk_freq_select = 0; - - tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) & - SARL_A370_TCLK_FREQ_OPT_MASK); - return armada_370_tclk_frequencies[tclk_freq_select]; -} - -static const u32 __initconst armada_370_cpu_frequencies[] = { - 400000000, - 533000000, - 667000000, - 800000000, - 1000000000, - 1067000000, - 1200000000, -}; - -static u32 __init armada_370_get_cpu_freq(void __iomem *sar) -{ - u32 cpu_freq; - u8 cpu_freq_select = 0; - - cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & - SARL_A370_PCLK_FREQ_OPT_MASK); - if (cpu_freq_select >= ARRAY_SIZE(armada_370_cpu_frequencies)) { - pr_err("CPU freq select unsupported %d\n", cpu_freq_select); - cpu_freq = 0; - } else - cpu_freq = armada_370_cpu_frequencies[cpu_freq_select]; - - return cpu_freq; -} - -enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK }; - -static const struct core_ratio __initconst armada_370_xp_core_ratios[] = { - { .id = A370_XP_NBCLK, .name = "nbclk" }, - { .id = A370_XP_HCLK, .name = "hclk" }, - { .id = A370_XP_DRAMCLK, .name = "dramclk" }, -}; - -static const int __initconst armada_370_xp_nbclk_ratios[32][2] = { - {0, 1}, {1, 2}, {2, 2}, {2, 2}, - {1, 2}, {1, 2}, {1, 1}, {2, 3}, - {0, 1}, {1, 2}, {2, 4}, {0, 1}, - {1, 2}, {0, 1}, {0, 1}, {2, 2}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {2, 3}, {0, 1}, {0, 1}, {0, 1}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {0, 1}, {0, 1}, {0, 1}, {0, 1}, -}; - -static const int __initconst armada_370_xp_hclk_ratios[32][2] = { - {0, 1}, {1, 2}, {2, 6}, {2, 3}, - {1, 3}, {1, 4}, {1, 2}, {2, 6}, - {0, 1}, {1, 6}, {2, 10}, {0, 1}, - {1, 4}, {0, 1}, {0, 1}, {2, 5}, - {0, 1}, {0, 1}, {0, 1}, {1, 2}, - {2, 6}, {0, 1}, {0, 1}, {0, 1}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {0, 1}, {0, 1}, {0, 1}, {0, 1}, -}; - -static const int __initconst armada_370_xp_dramclk_ratios[32][2] = { - {0, 1}, {1, 2}, {2, 3}, {2, 3}, - {1, 3}, {1, 2}, {1, 2}, {2, 6}, - {0, 1}, {1, 3}, {2, 5}, {0, 1}, - {1, 4}, {0, 1}, {0, 1}, {2, 5}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {2, 3}, {0, 1}, {0, 1}, {0, 1}, - {0, 1}, {0, 1}, {0, 1}, {1, 1}, - {0, 1}, {0, 1}, {0, 1}, {0, 1}, -}; - -static void __init armada_370_xp_get_clk_ratio(u32 opt, - void __iomem *sar, int id, int *mult, int *div) -{ - switch (id) { - case A370_XP_NBCLK: - *mult = armada_370_xp_nbclk_ratios[opt][0]; - *div = armada_370_xp_nbclk_ratios[opt][1]; - break; - case A370_XP_HCLK: - *mult = armada_370_xp_hclk_ratios[opt][0]; - *div = armada_370_xp_hclk_ratios[opt][1]; - break; - case A370_XP_DRAMCLK: - *mult = armada_370_xp_dramclk_ratios[opt][0]; - *div = armada_370_xp_dramclk_ratios[opt][1]; - break; - } -} - -static void __init armada_370_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) & - SARL_A370_FAB_FREQ_OPT_MASK); - - armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); -} - - -static const struct core_clocks armada_370_core_clocks = { - .get_tclk_freq = armada_370_get_tclk_freq, - .get_cpu_freq = armada_370_get_cpu_freq, - .get_clk_ratio = armada_370_get_clk_ratio, - .ratios = armada_370_xp_core_ratios, - .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), -}; - -static const u32 __initconst armada_xp_cpu_frequencies[] = { - 1000000000, - 1066000000, - 1200000000, - 1333000000, - 1500000000, - 1666000000, - 1800000000, - 2000000000, - 667000000, - 0, - 800000000, - 1600000000, -}; - -/* For Armada XP TCLK frequency is fix: 250MHz */ -static u32 __init armada_xp_get_tclk_freq(void __iomem *sar) -{ - return 250 * 1000 * 1000; -} - -static u32 __init armada_xp_get_cpu_freq(void __iomem *sar) -{ - u32 cpu_freq; - u8 cpu_freq_select = 0; - - cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) & - SARL_AXP_PCLK_FREQ_OPT_MASK); - /* - * The upper bit is not contiguous to the other ones and - * located in the high part of the SAR registers - */ - cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) & - SARH_AXP_PCLK_FREQ_OPT_MASK) - << SARH_AXP_PCLK_FREQ_OPT_SHIFT); - if (cpu_freq_select >= ARRAY_SIZE(armada_xp_cpu_frequencies)) { - pr_err("CPU freq select unsupported: %d\n", cpu_freq_select); - cpu_freq = 0; - } else - cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select]; - - return cpu_freq; -} - -static void __init armada_xp_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - - u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) & - SARL_AXP_FAB_FREQ_OPT_MASK); - /* - * The upper bit is not contiguous to the other ones and - * located in the high part of the SAR registers - */ - opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) & - SARH_AXP_FAB_FREQ_OPT_MASK) - << SARH_AXP_FAB_FREQ_OPT_SHIFT); - - armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); -} - -static const struct core_clocks armada_xp_core_clocks = { - .get_tclk_freq = armada_xp_get_tclk_freq, - .get_cpu_freq = armada_xp_get_cpu_freq, - .get_clk_ratio = armada_xp_get_clk_ratio, - .ratios = armada_370_xp_core_ratios, - .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), -}; - -#endif /* CONFIG_MACH_ARMADA_370_XP */ - -/* - * Dove PLL sample-at-reset configuration - * - * SAR0[8:5] : CPU frequency - * 5 = 1000 MHz - * 6 = 933 MHz - * 7 = 933 MHz - * 8 = 800 MHz - * 9 = 800 MHz - * 10 = 800 MHz - * 11 = 1067 MHz - * 12 = 667 MHz - * 13 = 533 MHz - * 14 = 400 MHz - * 15 = 333 MHz - * others reserved. - * - * SAR0[11:9] : CPU to L2 Clock divider ratio - * 0 = (1/1) * CPU - * 2 = (1/2) * CPU - * 4 = (1/3) * CPU - * 6 = (1/4) * CPU - * others reserved. - * - * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio - * 0 = (1/1) * CPU - * 2 = (1/2) * CPU - * 3 = (2/5) * CPU - * 4 = (1/3) * CPU - * 6 = (1/4) * CPU - * 8 = (1/5) * CPU - * 10 = (1/6) * CPU - * 12 = (1/7) * CPU - * 14 = (1/8) * CPU - * 15 = (1/10) * CPU - * others reserved. - * - * SAR0[24:23] : TCLK frequency - * 0 = 166 MHz - * 1 = 125 MHz - * others reserved. - */ -#ifdef CONFIG_ARCH_DOVE -#define SAR_DOVE_CPU_FREQ 5 -#define SAR_DOVE_CPU_FREQ_MASK 0xf -#define SAR_DOVE_L2_RATIO 9 -#define SAR_DOVE_L2_RATIO_MASK 0x7 -#define SAR_DOVE_DDR_RATIO 12 -#define SAR_DOVE_DDR_RATIO_MASK 0xf -#define SAR_DOVE_TCLK_FREQ 23 -#define SAR_DOVE_TCLK_FREQ_MASK 0x3 - -static const u32 __initconst dove_tclk_frequencies[] = { - 166666667, - 125000000, - 0, 0 -}; - -static u32 __init dove_get_tclk_freq(void __iomem *sar) -{ - u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) & - SAR_DOVE_TCLK_FREQ_MASK; - return dove_tclk_frequencies[opt]; -} - -static const u32 __initconst dove_cpu_frequencies[] = { - 0, 0, 0, 0, 0, - 1000000000, - 933333333, 933333333, - 800000000, 800000000, 800000000, - 1066666667, - 666666667, - 533333333, - 400000000, - 333333333 -}; - -static u32 __init dove_get_cpu_freq(void __iomem *sar) -{ - u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) & - SAR_DOVE_CPU_FREQ_MASK; - return dove_cpu_frequencies[opt]; -} - -enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR }; - -static const struct core_ratio __initconst dove_core_ratios[] = { - { .id = DOVE_CPU_TO_L2, .name = "l2clk", }, - { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", } -}; - -static const int __initconst dove_cpu_l2_ratios[8][2] = { - { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, - { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 } -}; - -static const int __initconst dove_cpu_ddr_ratios[16][2] = { - { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 }, - { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }, - { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 }, - { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 } -}; - -static void __init dove_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - switch (id) { - case DOVE_CPU_TO_L2: - { - u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) & - SAR_DOVE_L2_RATIO_MASK; - *mult = dove_cpu_l2_ratios[opt][0]; - *div = dove_cpu_l2_ratios[opt][1]; - break; - } - case DOVE_CPU_TO_DDR: - { - u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) & - SAR_DOVE_DDR_RATIO_MASK; - *mult = dove_cpu_ddr_ratios[opt][0]; - *div = dove_cpu_ddr_ratios[opt][1]; - break; - } - } -} - -static const struct core_clocks dove_core_clocks = { - .get_tclk_freq = dove_get_tclk_freq, - .get_cpu_freq = dove_get_cpu_freq, - .get_clk_ratio = dove_get_clk_ratio, - .ratios = dove_core_ratios, - .num_ratios = ARRAY_SIZE(dove_core_ratios), -}; -#endif /* CONFIG_ARCH_DOVE */ - -/* - * Kirkwood PLL sample-at-reset configuration - * (6180 has different SAR layout than other Kirkwood SoCs) - * - * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282) - * 4 = 600 MHz - * 6 = 800 MHz - * 7 = 1000 MHz - * 9 = 1200 MHz - * 12 = 1500 MHz - * 13 = 1600 MHz - * 14 = 1800 MHz - * 15 = 2000 MHz - * others reserved. - * - * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282) - * 1 = (1/2) * CPU - * 3 = (1/3) * CPU - * 5 = (1/4) * CPU - * others reserved. - * - * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282) - * 2 = (1/2) * CPU - * 4 = (1/3) * CPU - * 6 = (1/4) * CPU - * 7 = (2/9) * CPU - * 8 = (1/5) * CPU - * 9 = (1/6) * CPU - * others reserved. - * - * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only) - * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU] - * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU] - * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU] - * others reserved. - * - * SAR0[21] : TCLK frequency - * 0 = 200 MHz - * 1 = 166 MHz - * others reserved. - */ -#ifdef CONFIG_ARCH_KIRKWOOD -#define SAR_KIRKWOOD_CPU_FREQ(x) \ - (((x & (1 << 1)) >> 1) | \ - ((x & (1 << 22)) >> 21) | \ - ((x & (3 << 3)) >> 1)) -#define SAR_KIRKWOOD_L2_RATIO(x) \ - (((x & (3 << 9)) >> 9) | \ - (((x & (1 << 19)) >> 17))) -#define SAR_KIRKWOOD_DDR_RATIO 5 -#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf -#define SAR_MV88F6180_CLK 2 -#define SAR_MV88F6180_CLK_MASK 0x7 -#define SAR_KIRKWOOD_TCLK_FREQ 21 -#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1 - -enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR }; - -static const struct core_ratio __initconst kirkwood_core_ratios[] = { - { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", }, - { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", } -}; - -static u32 __init kirkwood_get_tclk_freq(void __iomem *sar) -{ - u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) & - SAR_KIRKWOOD_TCLK_FREQ_MASK; - return (opt) ? 166666667 : 200000000; -} - -static const u32 __initconst kirkwood_cpu_frequencies[] = { - 0, 0, 0, 0, - 600000000, - 0, - 800000000, - 1000000000, - 0, - 1200000000, - 0, 0, - 1500000000, - 1600000000, - 1800000000, - 2000000000 -}; - -static u32 __init kirkwood_get_cpu_freq(void __iomem *sar) -{ - u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar)); - return kirkwood_cpu_frequencies[opt]; -} - -static const int __initconst kirkwood_cpu_l2_ratios[8][2] = { - { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, - { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 } -}; - -static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = { - { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, - { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 }, - { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 }, - { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } -}; - -static void __init kirkwood_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - switch (id) { - case KIRKWOOD_CPU_TO_L2: - { - u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar)); - *mult = kirkwood_cpu_l2_ratios[opt][0]; - *div = kirkwood_cpu_l2_ratios[opt][1]; - break; - } - case KIRKWOOD_CPU_TO_DDR: - { - u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) & - SAR_KIRKWOOD_DDR_RATIO_MASK; - *mult = kirkwood_cpu_ddr_ratios[opt][0]; - *div = kirkwood_cpu_ddr_ratios[opt][1]; - break; - } - } -} - -static const struct core_clocks kirkwood_core_clocks = { - .get_tclk_freq = kirkwood_get_tclk_freq, - .get_cpu_freq = kirkwood_get_cpu_freq, - .get_clk_ratio = kirkwood_get_clk_ratio, - .ratios = kirkwood_core_ratios, - .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), -}; - -static const u32 __initconst mv88f6180_cpu_frequencies[] = { - 0, 0, 0, 0, 0, - 600000000, - 800000000, - 1000000000 -}; - -static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar) -{ - u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; - return mv88f6180_cpu_frequencies[opt]; -} - -static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = { - { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, - { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 } -}; - -static void __init mv88f6180_get_clk_ratio( - void __iomem *sar, int id, int *mult, int *div) -{ - switch (id) { - case KIRKWOOD_CPU_TO_L2: - { - /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */ - *mult = 1; - *div = 2; - break; - } - case KIRKWOOD_CPU_TO_DDR: - { - u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & - SAR_MV88F6180_CLK_MASK; - *mult = mv88f6180_cpu_ddr_ratios[opt][0]; - *div = mv88f6180_cpu_ddr_ratios[opt][1]; - break; - } - } -} - -static const struct core_clocks mv88f6180_core_clocks = { - .get_tclk_freq = kirkwood_get_tclk_freq, - .get_cpu_freq = mv88f6180_get_cpu_freq, - .get_clk_ratio = mv88f6180_get_clk_ratio, - .ratios = kirkwood_core_ratios, - .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), -}; -#endif /* CONFIG_ARCH_KIRKWOOD */ - -static const __initdata struct of_device_id clk_core_match[] = { -#ifdef CONFIG_MACH_ARMADA_370_XP - { - .compatible = "marvell,armada-370-core-clock", - .data = &armada_370_core_clocks, - }, - { - .compatible = "marvell,armada-xp-core-clock", - .data = &armada_xp_core_clocks, - }, -#endif -#ifdef CONFIG_ARCH_DOVE - { - .compatible = "marvell,dove-core-clock", - .data = &dove_core_clocks, - }, -#endif - -#ifdef CONFIG_ARCH_KIRKWOOD - { - .compatible = "marvell,kirkwood-core-clock", - .data = &kirkwood_core_clocks, - }, - { - .compatible = "marvell,mv88f6180-core-clock", - .data = &mv88f6180_core_clocks, - }, -#endif - - { } -}; - -void __init mvebu_core_clk_init(void) -{ - struct device_node *np; - - for_each_matching_node(np, clk_core_match) { - const struct of_device_id *match = - of_match_node(clk_core_match, np); - mvebu_clk_core_setup(np, (struct core_clocks *)match->data); - } -} diff --git a/drivers/clk/mvebu/clk-core.h b/drivers/clk/mvebu/clk-core.h deleted file mode 100644 index 28b5e02e9885..000000000000 --- a/drivers/clk/mvebu/clk-core.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * * Marvell EBU clock core handling defined at reset - * - * 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_CORE_H -#define __MVEBU_CLK_CORE_H - -void __init mvebu_core_clk_init(void); - -#endif diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index b0fbc0715491..1466865b0743 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -119,7 +119,7 @@ void __init of_cpu_clk_setup(struct device_node *node) cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL); if (WARN_ON(!cpuclk)) - return; + goto cpuclk_out; clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL); if (WARN_ON(!clks)) @@ -170,6 +170,8 @@ bail_out: kfree(cpuclk[ncpus].clk_name); clks_out: kfree(cpuclk); +cpuclk_out: + iounmap(clock_complex_base); } CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock", diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c deleted file mode 100644 index ebf141d4374b..000000000000 --- a/drivers/clk/mvebu/clk-gating-ctrl.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Marvell MVEBU clock gating control. - * - * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> - * Andrew Lunn <andrew@lunn.ch> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include <linux/kernel.h> -#include <linux/bitops.h> -#include <linux/io.h> -#include <linux/clk.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/clk/mvebu.h> -#include <linux/of.h> -#include <linux/of_address.h> - -struct mvebu_gating_ctrl { - spinlock_t lock; - struct clk **gates; - int num_gates; -}; - -struct mvebu_soc_descr { - const char *name; - const char *parent; - int bit_idx; -}; - -#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) - -static struct clk *mvebu_clk_gating_get_src( - struct of_phandle_args *clkspec, void *data) -{ - struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data; - int n; - - if (clkspec->args_count < 1) - return ERR_PTR(-EINVAL); - - for (n = 0; n < ctrl->num_gates; n++) { - struct clk_gate *gate = - to_clk_gate(__clk_get_hw(ctrl->gates[n])); - if (clkspec->args[0] == gate->bit_idx) - return ctrl->gates[n]; - } - return ERR_PTR(-ENODEV); -} - -static void __init mvebu_clk_gating_setup( - struct device_node *np, const struct mvebu_soc_descr *descr) -{ - struct mvebu_gating_ctrl *ctrl; - struct clk *clk; - void __iomem *base; - const char *default_parent = NULL; - int n; - - base = of_iomap(np, 0); - - clk = of_clk_get(np, 0); - if (!IS_ERR(clk)) { - default_parent = __clk_get_name(clk); - clk_put(clk); - } - - ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL); - if (WARN_ON(!ctrl)) - return; - - spin_lock_init(&ctrl->lock); - - /* - * Count, allocate, and register clock gates - */ - for (n = 0; descr[n].name;) - n++; - - ctrl->num_gates = n; - ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *), - GFP_KERNEL); - if (WARN_ON(!ctrl->gates)) { - kfree(ctrl); - return; - } - - for (n = 0; n < ctrl->num_gates; n++) { - u8 flags = 0; - const char *parent = - (descr[n].parent) ? descr[n].parent : default_parent; - - /* - * On Armada 370, the DDR clock is a special case: it - * isn't taken by any driver, but should anyway be - * kept enabled, so we mark it as IGNORE_UNUSED for - * now. - */ - if (!strcmp(descr[n].name, "ddr")) - flags |= CLK_IGNORE_UNUSED; - - ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, - flags, base, descr[n].bit_idx, 0, &ctrl->lock); - WARN_ON(IS_ERR(ctrl->gates[n])); - } - of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); -} - -/* - * SoC specific clock gating control - */ - -#ifdef CONFIG_MACH_ARMADA_370 -static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = { - { "audio", NULL, 0 }, - { "pex0_en", NULL, 1 }, - { "pex1_en", NULL, 2 }, - { "ge1", NULL, 3 }, - { "ge0", NULL, 4 }, - { "pex0", NULL, 5 }, - { "pex1", NULL, 9 }, - { "sata0", NULL, 15 }, - { "sdio", NULL, 17 }, - { "tdm", NULL, 25 }, - { "ddr", NULL, 28 }, - { "sata1", NULL, 30 }, - { } -}; -#endif - -#ifdef CONFIG_MACH_ARMADA_XP -static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = { - { "audio", NULL, 0 }, - { "ge3", NULL, 1 }, - { "ge2", NULL, 2 }, - { "ge1", NULL, 3 }, - { "ge0", NULL, 4 }, - { "pex0", NULL, 5 }, - { "pex1", NULL, 6 }, - { "pex2", NULL, 7 }, - { "pex3", NULL, 8 }, - { "bp", NULL, 13 }, - { "sata0lnk", NULL, 14 }, - { "sata0", "sata0lnk", 15 }, - { "lcd", NULL, 16 }, - { "sdio", NULL, 17 }, - { "usb0", NULL, 18 }, - { "usb1", NULL, 19 }, - { "usb2", NULL, 20 }, - { "xor0", NULL, 22 }, - { "crypto", NULL, 23 }, - { "tdm", NULL, 25 }, - { "xor1", NULL, 28 }, - { "sata1lnk", NULL, 29 }, - { "sata1", "sata1lnk", 30 }, - { } -}; -#endif - -#ifdef CONFIG_ARCH_DOVE -static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { - { "usb0", NULL, 0 }, - { "usb1", NULL, 1 }, - { "ge", "gephy", 2 }, - { "sata", NULL, 3 }, - { "pex0", NULL, 4 }, - { "pex1", NULL, 5 }, - { "sdio0", NULL, 8 }, - { "sdio1", NULL, 9 }, - { "nand", NULL, 10 }, - { "camera", NULL, 11 }, - { "i2s0", NULL, 12 }, - { "i2s1", NULL, 13 }, - { "crypto", NULL, 15 }, - { "ac97", NULL, 21 }, - { "pdma", NULL, 22 }, - { "xor0", NULL, 23 }, - { "xor1", NULL, 24 }, - { "gephy", NULL, 30 }, - { } -}; -#endif - -#ifdef CONFIG_ARCH_KIRKWOOD -static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { - { "ge0", NULL, 0 }, - { "pex0", NULL, 2 }, - { "usb0", NULL, 3 }, - { "sdio", NULL, 4 }, - { "tsu", NULL, 5 }, - { "runit", NULL, 7 }, - { "xor0", NULL, 8 }, - { "audio", NULL, 9 }, - { "powersave", "cpuclk", 11 }, - { "sata0", NULL, 14 }, - { "sata1", NULL, 15 }, - { "xor1", NULL, 16 }, - { "crypto", NULL, 17 }, - { "pex1", NULL, 18 }, - { "ge1", NULL, 19 }, - { "tdm", NULL, 20 }, - { } -}; -#endif - -static const __initdata struct of_device_id clk_gating_match[] = { -#ifdef CONFIG_MACH_ARMADA_370 - { - .compatible = "marvell,armada-370-gating-clock", - .data = armada_370_gating_descr, - }, -#endif - -#ifdef CONFIG_MACH_ARMADA_XP - { - .compatible = "marvell,armada-xp-gating-clock", - .data = armada_xp_gating_descr, - }, -#endif - -#ifdef CONFIG_ARCH_DOVE - { - .compatible = "marvell,dove-gating-clock", - .data = dove_gating_descr, - }, -#endif - -#ifdef CONFIG_ARCH_KIRKWOOD - { - .compatible = "marvell,kirkwood-gating-clock", - .data = kirkwood_gating_descr, - }, -#endif - - { } -}; - -void __init mvebu_gating_clk_init(void) -{ - struct device_node *np; - - for_each_matching_node(np, clk_gating_match) { - const struct of_device_id *match = - of_match_node(clk_gating_match, np); - mvebu_clk_gating_setup(np, - (const struct mvebu_soc_descr *)match->data); - } -} diff --git a/drivers/clk/mvebu/clk-gating-ctrl.h b/drivers/clk/mvebu/clk-gating-ctrl.h deleted file mode 100644 index 9275d1e51f1b..000000000000 --- a/drivers/clk/mvebu/clk-gating-ctrl.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Marvell EBU gating clock handling - * - * Copyright (C) 2012 Marvell - * - * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __MVEBU_CLK_GATING_H -#define __MVEBU_CLK_GATING_H - -#ifdef CONFIG_MVEBU_CLK_GATING -void __init mvebu_gating_clk_init(void); -#else -void mvebu_gating_clk_init(void) {} -#endif - -#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c deleted file mode 100644 index 29f10fb3006c..000000000000 --- a/drivers/clk/mvebu/clk.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Marvell EBU SoC 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. - */ -#include <linux/kernel.h> -#include <linux/clk-provider.h> -#include <linux/of.h> -#include "clk-core.h" -#include "clk-gating-ctrl.h" - -void __init mvebu_clocks_init(void) -{ - mvebu_core_clk_init(); - mvebu_gating_clk_init(); - of_clk_init(NULL); -} diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c new file mode 100644 index 000000000000..25ceccf939ad --- /dev/null +++ b/drivers/clk/mvebu/common.c @@ -0,0 +1,169 @@ +/* + * Marvell EBU SoC common clock handling + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "common.h" + +/* + * Core Clocks + */ + +static struct clk_onecell_data clk_data; + +void __init mvebu_coreclk_setup(struct device_node *np, + const struct coreclk_soc_desc *desc) +{ + const char *tclk_name = "tclk"; + const char *cpuclk_name = "cpuclk"; + void __iomem *base; + unsigned long rate; + int n; + + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return; + + /* Allocate struct for TCLK, cpu clk, and core ratio clocks */ + clk_data.clk_num = 2 + desc->num_ratios; + clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!clk_data.clks)) { + iounmap(base); + return; + } + + /* Register TCLK */ + of_property_read_string_index(np, "clock-output-names", 0, + &tclk_name); + rate = desc->get_tclk_freq(base); + clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, + CLK_IS_ROOT, rate); + WARN_ON(IS_ERR(clk_data.clks[0])); + + /* Register CPU clock */ + of_property_read_string_index(np, "clock-output-names", 1, + &cpuclk_name); + rate = desc->get_cpu_freq(base); + clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, + CLK_IS_ROOT, rate); + WARN_ON(IS_ERR(clk_data.clks[1])); + + /* Register fixed-factor clocks derived from CPU clock */ + for (n = 0; n < desc->num_ratios; n++) { + const char *rclk_name = desc->ratios[n].name; + int mult, div; + + of_property_read_string_index(np, "clock-output-names", + 2+n, &rclk_name); + desc->get_clk_ratio(base, desc->ratios[n].id, &mult, &div); + clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, + cpuclk_name, 0, mult, div); + WARN_ON(IS_ERR(clk_data.clks[2+n])); + }; + + /* SAR register isn't needed anymore */ + iounmap(base); + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} + +/* + * Clock Gating Control + */ + +struct clk_gating_ctrl { + spinlock_t lock; + struct clk **gates; + int num_gates; +}; + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +static struct clk *clk_gating_get_src( + struct of_phandle_args *clkspec, void *data) +{ + struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data; + int n; + + if (clkspec->args_count < 1) + return ERR_PTR(-EINVAL); + + for (n = 0; n < ctrl->num_gates; n++) { + struct clk_gate *gate = + to_clk_gate(__clk_get_hw(ctrl->gates[n])); + if (clkspec->args[0] == gate->bit_idx) + return ctrl->gates[n]; + } + return ERR_PTR(-ENODEV); +} + +void __init mvebu_clk_gating_setup(struct device_node *np, + const struct clk_gating_soc_desc *desc) +{ + struct clk_gating_ctrl *ctrl; + struct clk *clk; + void __iomem *base; + const char *default_parent = NULL; + int n; + + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return; + + clk = of_clk_get(np, 0); + if (!IS_ERR(clk)) { + default_parent = __clk_get_name(clk); + clk_put(clk); + } + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (WARN_ON(!ctrl)) + goto ctrl_out; + + spin_lock_init(&ctrl->lock); + + /* Count, allocate, and register clock gates */ + for (n = 0; desc[n].name;) + n++; + + ctrl->num_gates = n; + ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!ctrl->gates)) + goto gates_out; + + for (n = 0; n < ctrl->num_gates; n++) { + const char *parent = + (desc[n].parent) ? desc[n].parent : default_parent; + ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent, + desc[n].flags, base, desc[n].bit_idx, + 0, &ctrl->lock); + WARN_ON(IS_ERR(ctrl->gates[n])); + } + + of_clk_add_provider(np, clk_gating_get_src, ctrl); + + return; +gates_out: + kfree(ctrl); +ctrl_out: + iounmap(base); +} diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h new file mode 100644 index 000000000000..f968b4d9df92 --- /dev/null +++ b/drivers/clk/mvebu/common.h @@ -0,0 +1,48 @@ +/* + * Marvell EBU SoC common clock handling + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __CLK_MVEBU_COMMON_H_ +#define __CLK_MVEBU_COMMON_H_ + +#include <linux/kernel.h> + +struct device_node; + +struct coreclk_ratio { + int id; + const char *name; +}; + +struct coreclk_soc_desc { + u32 (*get_tclk_freq)(void __iomem *sar); + u32 (*get_cpu_freq)(void __iomem *sar); + void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); + const struct coreclk_ratio *ratios; + int num_ratios; +}; + +struct clk_gating_soc_desc { + const char *name; + const char *parent; + int bit_idx; + unsigned long flags; +}; + +void __init mvebu_coreclk_setup(struct device_node *np, + const struct coreclk_soc_desc *desc); + +void __init mvebu_clk_gating_setup(struct device_node *np, + const struct clk_gating_soc_desc *desc); + +#endif diff --git a/drivers/clk/mvebu/dove.c b/drivers/clk/mvebu/dove.c new file mode 100644 index 000000000000..38aee1e3f242 --- /dev/null +++ b/drivers/clk/mvebu/dove.c @@ -0,0 +1,194 @@ +/* + * Marvell Dove SoC clocks + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + +/* + * Core Clocks + * + * Dove PLL sample-at-reset configuration + * + * SAR0[8:5] : CPU frequency + * 5 = 1000 MHz + * 6 = 933 MHz + * 7 = 933 MHz + * 8 = 800 MHz + * 9 = 800 MHz + * 10 = 800 MHz + * 11 = 1067 MHz + * 12 = 667 MHz + * 13 = 533 MHz + * 14 = 400 MHz + * 15 = 333 MHz + * others reserved. + * + * SAR0[11:9] : CPU to L2 Clock divider ratio + * 0 = (1/1) * CPU + * 2 = (1/2) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * others reserved. + * + * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio + * 0 = (1/1) * CPU + * 2 = (1/2) * CPU + * 3 = (2/5) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * 8 = (1/5) * CPU + * 10 = (1/6) * CPU + * 12 = (1/7) * CPU + * 14 = (1/8) * CPU + * 15 = (1/10) * CPU + * others reserved. + * + * SAR0[24:23] : TCLK frequency + * 0 = 166 MHz + * 1 = 125 MHz + * others reserved. + */ + +#define SAR_DOVE_CPU_FREQ 5 +#define SAR_DOVE_CPU_FREQ_MASK 0xf +#define SAR_DOVE_L2_RATIO 9 +#define SAR_DOVE_L2_RATIO_MASK 0x7 +#define SAR_DOVE_DDR_RATIO 12 +#define SAR_DOVE_DDR_RATIO_MASK 0xf +#define SAR_DOVE_TCLK_FREQ 23 +#define SAR_DOVE_TCLK_FREQ_MASK 0x3 + +enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR }; + +static const struct coreclk_ratio dove_coreclk_ratios[] __initconst = { + { .id = DOVE_CPU_TO_L2, .name = "l2clk", }, + { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", } +}; + +static const u32 dove_tclk_freqs[] __initconst = { + 166666667, + 125000000, + 0, 0 +}; + +static u32 __init dove_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) & + SAR_DOVE_TCLK_FREQ_MASK; + return dove_tclk_freqs[opt]; +} + +static const u32 dove_cpu_freqs[] __initconst = { + 0, 0, 0, 0, 0, + 1000000000, + 933333333, 933333333, + 800000000, 800000000, 800000000, + 1066666667, + 666666667, + 533333333, + 400000000, + 333333333 +}; + +static u32 __init dove_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) & + SAR_DOVE_CPU_FREQ_MASK; + return dove_cpu_freqs[opt]; +} + +static const int dove_cpu_l2_ratios[8][2] __initconst = { + { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 } +}; + +static const int dove_cpu_ddr_ratios[16][2] __initconst = { + { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }, + { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 }, + { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 } +}; + +static void __init dove_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case DOVE_CPU_TO_L2: + { + u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) & + SAR_DOVE_L2_RATIO_MASK; + *mult = dove_cpu_l2_ratios[opt][0]; + *div = dove_cpu_l2_ratios[opt][1]; + break; + } + case DOVE_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) & + SAR_DOVE_DDR_RATIO_MASK; + *mult = dove_cpu_ddr_ratios[opt][0]; + *div = dove_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct coreclk_soc_desc dove_coreclks = { + .get_tclk_freq = dove_get_tclk_freq, + .get_cpu_freq = dove_get_cpu_freq, + .get_clk_ratio = dove_get_clk_ratio, + .ratios = dove_coreclk_ratios, + .num_ratios = ARRAY_SIZE(dove_coreclk_ratios), +}; + +static void __init dove_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &dove_coreclks); +} +CLK_OF_DECLARE(dove_core_clk, "marvell,dove-core-clock", dove_coreclk_init); + +/* + * Clock Gating Control + */ + +static const struct clk_gating_soc_desc dove_gating_desc[] __initconst = { + { "usb0", NULL, 0, 0 }, + { "usb1", NULL, 1, 0 }, + { "ge", "gephy", 2, 0 }, + { "sata", NULL, 3, 0 }, + { "pex0", NULL, 4, 0 }, + { "pex1", NULL, 5, 0 }, + { "sdio0", NULL, 8, 0 }, + { "sdio1", NULL, 9, 0 }, + { "nand", NULL, 10, 0 }, + { "camera", NULL, 11, 0 }, + { "i2s0", NULL, 12, 0 }, + { "i2s1", NULL, 13, 0 }, + { "crypto", NULL, 15, 0 }, + { "ac97", NULL, 21, 0 }, + { "pdma", NULL, 22, 0 }, + { "xor0", NULL, 23, 0 }, + { "xor1", NULL, 24, 0 }, + { "gephy", NULL, 30, 0 }, + { } +}; + +static void __init dove_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, dove_gating_desc); +} +CLK_OF_DECLARE(dove_clk_gating, "marvell,dove-gating-clock", + dove_clk_gating_init); diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c new file mode 100644 index 000000000000..2636a55f29f9 --- /dev/null +++ b/drivers/clk/mvebu/kirkwood.c @@ -0,0 +1,247 @@ +/* + * Marvell Kirkwood SoC clocks + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + +/* + * Core Clocks + * + * Kirkwood PLL sample-at-reset configuration + * (6180 has different SAR layout than other Kirkwood SoCs) + * + * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282) + * 4 = 600 MHz + * 6 = 800 MHz + * 7 = 1000 MHz + * 9 = 1200 MHz + * 12 = 1500 MHz + * 13 = 1600 MHz + * 14 = 1800 MHz + * 15 = 2000 MHz + * others reserved. + * + * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282) + * 1 = (1/2) * CPU + * 3 = (1/3) * CPU + * 5 = (1/4) * CPU + * others reserved. + * + * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282) + * 2 = (1/2) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * 7 = (2/9) * CPU + * 8 = (1/5) * CPU + * 9 = (1/6) * CPU + * others reserved. + * + * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only) + * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU] + * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU] + * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU] + * others reserved. + * + * SAR0[21] : TCLK frequency + * 0 = 200 MHz + * 1 = 166 MHz + * others reserved. + */ + +#define SAR_KIRKWOOD_CPU_FREQ(x) \ + (((x & (1 << 1)) >> 1) | \ + ((x & (1 << 22)) >> 21) | \ + ((x & (3 << 3)) >> 1)) +#define SAR_KIRKWOOD_L2_RATIO(x) \ + (((x & (3 << 9)) >> 9) | \ + (((x & (1 << 19)) >> 17))) +#define SAR_KIRKWOOD_DDR_RATIO 5 +#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf +#define SAR_MV88F6180_CLK 2 +#define SAR_MV88F6180_CLK_MASK 0x7 +#define SAR_KIRKWOOD_TCLK_FREQ 21 +#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1 + +enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR }; + +static const struct coreclk_ratio kirkwood_coreclk_ratios[] __initconst = { + { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", }, + { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", } +}; + +static u32 __init kirkwood_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) & + SAR_KIRKWOOD_TCLK_FREQ_MASK; + return (opt) ? 166666667 : 200000000; +} + +static const u32 kirkwood_cpu_freqs[] __initconst = { + 0, 0, 0, 0, + 600000000, + 0, + 800000000, + 1000000000, + 0, + 1200000000, + 0, 0, + 1500000000, + 1600000000, + 1800000000, + 2000000000 +}; + +static u32 __init kirkwood_get_cpu_freq(void __iomem *sar) +{ + u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar)); + return kirkwood_cpu_freqs[opt]; +} + +static const int kirkwood_cpu_l2_ratios[8][2] __initconst = { + { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, + { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 } +}; + +static const int kirkwood_cpu_ddr_ratios[16][2] __initconst = { + { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 }, + { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 }, + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } +}; + +static void __init kirkwood_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case KIRKWOOD_CPU_TO_L2: + { + u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar)); + *mult = kirkwood_cpu_l2_ratios[opt][0]; + *div = kirkwood_cpu_l2_ratios[opt][1]; + break; + } + case KIRKWOOD_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) & + SAR_KIRKWOOD_DDR_RATIO_MASK; + *mult = kirkwood_cpu_ddr_ratios[opt][0]; + *div = kirkwood_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const u32 mv88f6180_cpu_freqs[] __initconst = { + 0, 0, 0, 0, 0, + 600000000, + 800000000, + 1000000000 +}; + +static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; + return mv88f6180_cpu_freqs[opt]; +} + +static const int mv88f6180_cpu_ddr_ratios[8][2] __initconst = { + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, + { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 } +}; + +static void __init mv88f6180_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case KIRKWOOD_CPU_TO_L2: + { + /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */ + *mult = 1; + *div = 2; + break; + } + case KIRKWOOD_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & + SAR_MV88F6180_CLK_MASK; + *mult = mv88f6180_cpu_ddr_ratios[opt][0]; + *div = mv88f6180_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct coreclk_soc_desc kirkwood_coreclks = { + .get_tclk_freq = kirkwood_get_tclk_freq, + .get_cpu_freq = kirkwood_get_cpu_freq, + .get_clk_ratio = kirkwood_get_clk_ratio, + .ratios = kirkwood_coreclk_ratios, + .num_ratios = ARRAY_SIZE(kirkwood_coreclk_ratios), +}; + +static void __init kirkwood_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &kirkwood_coreclks); +} +CLK_OF_DECLARE(kirkwood_core_clk, "marvell,kirkwood-core-clock", + kirkwood_coreclk_init); + +static const struct coreclk_soc_desc mv88f6180_coreclks = { + .get_tclk_freq = kirkwood_get_tclk_freq, + .get_cpu_freq = mv88f6180_get_cpu_freq, + .get_clk_ratio = mv88f6180_get_clk_ratio, + .ratios = kirkwood_coreclk_ratios, + .num_ratios = ARRAY_SIZE(kirkwood_coreclk_ratios), +}; + +static void __init mv88f6180_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &mv88f6180_coreclks); +} +CLK_OF_DECLARE(mv88f6180_core_clk, "marvell,mv88f6180-core-clock", + mv88f6180_coreclk_init); + +/* + * Clock Gating Control + */ + +static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { + { "ge0", NULL, 0, 0 }, + { "pex0", NULL, 2, 0 }, + { "usb0", NULL, 3, 0 }, + { "sdio", NULL, 4, 0 }, + { "tsu", NULL, 5, 0 }, + { "runit", NULL, 7, 0 }, + { "xor0", NULL, 8, 0 }, + { "audio", NULL, 9, 0 }, + { "powersave", "cpuclk", 11, 0 }, + { "sata0", NULL, 14, 0 }, + { "sata1", NULL, 15, 0 }, + { "xor1", NULL, 16, 0 }, + { "crypto", NULL, 17, 0 }, + { "pex1", NULL, 18, 0 }, + { "ge1", NULL, 19, 0 }, + { "tdm", NULL, 20, 0 }, + { } +}; + +static void __init kirkwood_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, kirkwood_gating_desc); +} +CLK_OF_DECLARE(kirkwood_clk_gating, "marvell,kirkwood-gating-clock", + kirkwood_clk_gating_init); diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index f6a74872f14e..c396fe361589 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -10,6 +10,7 @@ */ #include <linux/clk.h> +#include <linux/clk/mxs.h> #include <linux/clkdev.h> #include <linux/err.h> #include <linux/init.h> diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index 81421e28e69c..ef10ad9b5daa 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -52,8 +52,8 @@ static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char **parent_names, int num_parents) { return clk_register_mux(NULL, name, parent_names, num_parents, - CLK_SET_RATE_PARENT, reg, shift, width, - 0, &mxs_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + reg, shift, width, 0, &mxs_lock); } static inline struct clk *mxs_clk_fixed_factor(const char *name, diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile new file mode 100644 index 000000000000..8d3aefad2e73 --- /dev/null +++ b/drivers/clk/rockchip/Makefile @@ -0,0 +1,5 @@ +# +# Rockchip Clock specific Makefile +# + +obj-y += clk-rockchip.o diff --git a/drivers/clk/rockchip/clk-rockchip.c b/drivers/clk/rockchip/clk-rockchip.c new file mode 100644 index 000000000000..967c141b1a20 --- /dev/null +++ b/drivers/clk/rockchip/clk-rockchip.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner <heiko@sntech.de> + * + * 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/of.h> +#include <linux/of_address.h> + +static DEFINE_SPINLOCK(clk_lock); + +/* + * Gate clocks + */ + +static void __init rk2928_gate_clk_init(struct device_node *node, + void *data) +{ + struct clk_onecell_data *clk_data; + const char *clk_parent; + const char *clk_name; + void __iomem *reg; + void __iomem *reg_idx; + int flags; + int qty; + int reg_bit; + int clkflags = CLK_SET_RATE_PARENT; + int i; + + qty = of_property_count_strings(node, "clock-output-names"); + if (qty < 0) { + pr_err("%s: error in clock-output-names %d\n", __func__, qty); + return; + } + + if (qty == 0) { + pr_info("%s: nothing to do\n", __func__); + return; + } + + reg = of_iomap(node, 0); + + clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->clks = kzalloc(qty * sizeof(struct clk *), GFP_KERNEL); + if (!clk_data->clks) { + kfree(clk_data); + return; + } + + flags = CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE; + + for (i = 0; i < qty; i++) { + of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + + /* ignore empty slots */ + if (!strcmp("reserved", clk_name)) + continue; + + clk_parent = of_clk_get_parent_name(node, i); + + /* keep all gates untouched for now */ + clkflags |= CLK_IGNORE_UNUSED; + + reg_idx = reg + (4 * (i / 16)); + reg_bit = (i % 16); + + clk_data->clks[i] = clk_register_gate(NULL, clk_name, + clk_parent, clkflags, + reg_idx, reg_bit, + flags, + &clk_lock); + WARN_ON(IS_ERR(clk_data->clks[i])); + } + + clk_data->clk_num = qty; + + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} +CLK_OF_DECLARE(rk2928_gate, "rockchip,rk2928-gate-clk", rk2928_gate_clk_init); diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index b7c232e67425..3413380086d5 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -5,4 +5,9 @@ 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_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o +obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +ifdef CONFIG_COMMON_CLK +obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o +endif diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c new file mode 100644 index 000000000000..39b40aaede2b --- /dev/null +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Padmavathi Venna <padma.v@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 Audio Subsystem Clock Controller. +*/ + +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/syscore_ops.h> + +#include <dt-bindings/clk/exynos-audss-clk.h> + +static DEFINE_SPINLOCK(lock); +static struct clk **clk_table; +static void __iomem *reg_base; +static struct clk_onecell_data clk_data; + +#define ASS_CLK_SRC 0x0 +#define ASS_CLK_DIV 0x4 +#define ASS_CLK_GATE 0x8 + +static unsigned long reg_save[][2] = { + {ASS_CLK_SRC, 0}, + {ASS_CLK_DIV, 0}, + {ASS_CLK_GATE, 0}, +}; + +/* list of all parent clock list */ +static const char *mout_audss_p[] = { "fin_pll", "fout_epll" }; +static const char *mout_i2s_p[] = { "mout_audss", "cdclk0", "sclk_audio0" }; + +#ifdef CONFIG_PM_SLEEP +static int exynos_audss_clk_suspend(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_save); i++) + reg_save[i][1] = readl(reg_base + reg_save[i][0]); + + return 0; +} + +static void exynos_audss_clk_resume(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_save); i++) + writel(reg_save[i][1], reg_base + reg_save[i][0]); +} + +static struct syscore_ops exynos_audss_clk_syscore_ops = { + .suspend = exynos_audss_clk_suspend, + .resume = exynos_audss_clk_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +/* register exynos_audss clocks */ +static void __init exynos_audss_clk_init(struct device_node *np) +{ + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: failed to map audss registers\n", __func__); + return; + } + + clk_table = kzalloc(sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS, + GFP_KERNEL); + if (!clk_table) { + pr_err("%s: could not allocate clk lookup table\n", __func__); + return; + } + + clk_data.clks = clk_table; + clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss", + mout_audss_p, ARRAY_SIZE(mout_audss_p), + CLK_SET_RATE_NO_REPARENT, + reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); + + clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s", + mout_i2s_p, ARRAY_SIZE(mout_i2s_p), + CLK_SET_RATE_NO_REPARENT, + reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); + + clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp", + "mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4, + 0, &lock); + + clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL, + "dout_aud_bus", "dout_srp", 0, + reg_base + ASS_CLK_DIV, 4, 4, 0, &lock); + + clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s", + "mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0, + &lock); + + clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk", + "dout_srp", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 0, 0, &lock); + + clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus", + "dout_aud_bus", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 2, 0, &lock); + + clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s", + "dout_i2s", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 3, 0, &lock); + + clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus", + "sclk_pcm", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 4, 0, &lock); + + clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm", + "div_pcm0", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 5, 0, &lock); + +#ifdef CONFIG_PM_SLEEP + register_syscore_ops(&exynos_audss_clk_syscore_ops); +#endif + + pr_info("Exynos: Audss: clock setup completed\n"); +} +CLK_OF_DECLARE(exynos4210_audss_clk, "samsung,exynos4210-audss-clock", + exynos_audss_clk_init); +CLK_OF_DECLARE(exynos5250_audss_clk, "samsung,exynos5250-audss-clock", + exynos_audss_clk_init); diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 3c1f88868f29..ad5ff50c5f28 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -17,7 +17,6 @@ #include <linux/of_address.h> #include "clk.h" -#include "clk-pll.h" /* Exynos4 clock controller register offsets */ #define SRC_LEFTBUS 0x4200 @@ -97,12 +96,15 @@ #define GATE_IP_PERIL 0xc950 #define E4210_GATE_IP_PERIR 0xc960 #define GATE_BLOCK 0xc970 +#define E4X12_MPLL_LOCK 0x10008 #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_LOCK 0x14000 +#define E4210_MPLL_LOCK 0x14008 #define APLL_CON0 0x14100 #define E4210_MPLL_CON0 0x14108 #define SRC_CPU 0x14200 @@ -121,6 +123,12 @@ enum exynos4_soc { EXYNOS4X12, }; +/* list of PLLs to be registered */ +enum exynos4_plls { + apll, mpll, epll, vpll, + nr_plls /* number of PLLs */ +}; + /* * 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 @@ -151,7 +159,7 @@ enum exynos4_clks { 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, + sclk_spi0_isp, sclk_spi1_isp, sclk_uart_isp, sclk_fimg2d, /* gate clocks */ fimc0 = 256, fimc1, fimc2, fimc3, csis0, csis1, jpeg, smmu_fimc0, @@ -169,7 +177,7 @@ enum exynos4_clks { 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, + spi1_isp_sclk, uart_isp_sclk, tmu_apbif, /* mux clocks */ mout_fimc0 = 384, mout_fimc1, mout_fimc2, mout_fimc3, mout_cam0, @@ -187,7 +195,7 @@ enum exynos4_clks { * list of controller registers to be saved and restored during a * suspend/resume cycle. */ -static __initdata unsigned long exynos4210_clk_save[] = { +static unsigned long exynos4210_clk_save[] __initdata = { E4210_SRC_IMAGE, E4210_SRC_LCD1, E4210_SRC_MASK_LCD1, @@ -198,7 +206,7 @@ static __initdata unsigned long exynos4210_clk_save[] = { E4210_MPLL_CON0, }; -static __initdata unsigned long exynos4x12_clk_save[] = { +static unsigned long exynos4x12_clk_save[] __initdata = { E4X12_GATE_IP_IMAGE, E4X12_GATE_IP_PERIR, E4X12_SRC_CAM1, @@ -207,7 +215,7 @@ static __initdata unsigned long exynos4x12_clk_save[] = { E4X12_MPLL_CON0, }; -static __initdata unsigned long exynos4_clk_regs[] = { +static unsigned long exynos4_clk_regs[] __initdata = { SRC_LEFTBUS, DIV_LEFTBUS, GATE_IP_LEFTBUS, @@ -338,26 +346,26 @@ 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 = { +static 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 = { +static 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 = { +static 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), +static struct samsung_mux_clock exynos4_mux_clks[] __initdata = { + MUX_FA(mout_apll, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, + CLK_SET_RATE_PARENT, 0, "mout_apll"), 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), @@ -367,17 +375,20 @@ struct samsung_mux_clock exynos4_mux_clks[] __initdata = { 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(sclk_epll, "sclk_epll", mout_epll_p, SRC_TOP0, 4, 1), 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 = { +static struct samsung_mux_clock exynos4210_mux_early[] __initdata = { + MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP1, 0, 1), +}; + +static 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), @@ -385,11 +396,9 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { 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(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1), + MUX(mout_core, "mout_core", mout_core_p4210, SRC_CPU, 16, 1), + MUX(sclk_vpll, "sclk_vpll", sclk_vpll_p4210, SRC_TOP0, 8, 1), 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), @@ -423,7 +432,7 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { }; /* list of mux clocks supported in exynos4x12 soc */ -struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { +static 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), @@ -445,10 +454,8 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { 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(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_DMC, 12, 1), + MUX(sclk_vpll, "sclk_vpll", mout_vpll_p, SRC_TOP0, 8, 1), 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), @@ -484,10 +491,13 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { 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), + MUX(none, "mout_g2d0", sclk_ampll_p4210, SRC_DMC, 20, 1), + MUX(none, "mout_g2d1", sclk_evpll_p, SRC_DMC, 24, 1), + MUX(none, "mout_g2d", mout_g2d_p, SRC_DMC, 28, 1), }; /* list of divider clocks supported in all exynos4 soc's */ -struct samsung_div_clock exynos4_div_clks[] __initdata = { +static 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), @@ -534,9 +544,8 @@ struct samsung_div_clock exynos4_div_clks[] __initdata = { 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(arm_clk, "arm_clk", "div_core2", DIV_CPU0, 28, 3), + DIV(sclk_apll, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3), 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, @@ -550,9 +559,9 @@ struct samsung_div_clock exynos4_div_clks[] __initdata = { }; /* list of divider clocks supported in exynos4210 soc */ -struct samsung_div_clock exynos4210_div_clks[] __initdata = { +static 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(sclk_fimg2d, "sclk_fimg2d", "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), @@ -561,7 +570,7 @@ struct samsung_div_clock exynos4210_div_clks[] __initdata = { }; /* list of divider clocks supported in exynos4x12 soc */ -struct samsung_div_clock exynos4x12_div_clks[] __initdata = { +static 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), @@ -577,15 +586,20 @@ struct samsung_div_clock exynos4x12_div_clks[] __initdata = { 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_F(div_isp0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3, + CLK_GET_RATE_NOCACHE, 0), + DIV_F(div_isp1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3, + CLK_GET_RATE_NOCACHE, 0), 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), + DIV_F(div_mcuisp0, "div_mcuisp0", "aclk400_mcuisp", E4X12_DIV_ISP1, + 4, 3, CLK_GET_RATE_NOCACHE, 0), + DIV_F(div_mcuisp1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, + 8, 3, CLK_GET_RATE_NOCACHE, 0), + DIV(sclk_fimg2d, "sclk_fimg2d", "mout_g2d", DIV_DMC1, 0, 4), }; /* list of gate clocks supported in all exynos4 soc's */ -struct samsung_gate_clock exynos4_gate_clks[] __initdata = { +static 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 @@ -620,164 +634,151 @@ struct samsung_gate_clock exynos4_gate_clks[] __initdata = { 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(vp, "vp", "aclk160", GATE_IP_TV, 0, 0, 0), + GATE(mixer, "mixer", "aclk160", GATE_IP_TV, 1, 0, 0), + GATE(hdmi, "hdmi", "aclk160", GATE_IP_TV, 3, 0, 0), + GATE(pwm, "pwm", "aclk100", GATE_IP_PERIL, 24, 0, 0), + GATE(sdmmc4, "sdmmc4", "aclk133", GATE_IP_FSYS, 9, 0, 0), + GATE(usb_host, "usb_host", "aclk133", GATE_IP_FSYS, 12, 0, 0), + GATE(sclk_fimc0, "sclk_fimc0", "div_fimc0", SRC_MASK_CAM, 0, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_fimc1, "sclk_fimc1", "div_fimc1", SRC_MASK_CAM, 4, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_fimc2, "sclk_fimc2", "div_fimc2", SRC_MASK_CAM, 8, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_fimc3, "sclk_fimc3", "div_fimc3", SRC_MASK_CAM, 12, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_csis0, "sclk_csis0", "div_csis0", SRC_MASK_CAM, 24, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_csis1, "sclk_csis1", "div_csis1", SRC_MASK_CAM, 28, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_fimd0, "sclk_fimd0", "div_fimd0", SRC_MASK_LCD0, 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_mmc4, "sclk_mmc4", "div_mmc_pre4", SRC_MASK_FSYS, 16, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart0, "uclk0", "div_uart0", SRC_MASK_PERIL0, 0, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart1, "uclk1", "div_uart1", SRC_MASK_PERIL0, 4, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart2, "uclk2", "div_uart2", SRC_MASK_PERIL0, 8, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart3, "uclk3", "div_uart3", SRC_MASK_PERIL0, 12, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart4, "uclk4", "div_uart4", SRC_MASK_PERIL0, 16, + CLK_SET_RATE_PARENT, 0), 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(sclk_spi0, "sclk_spi0", "div_spi_pre0", SRC_MASK_PERIL1, 16, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi1, "sclk_spi1", "div_spi_pre1", SRC_MASK_PERIL1, 20, + CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi2, "sclk_spi2", "div_spi_pre2", SRC_MASK_PERIL1, 24, + CLK_SET_RATE_PARENT, 0), + GATE(fimc0, "fimc0", "aclk160", GATE_IP_CAM, 0, + 0, 0), + GATE(fimc1, "fimc1", "aclk160", GATE_IP_CAM, 1, + 0, 0), + GATE(fimc2, "fimc2", "aclk160", GATE_IP_CAM, 2, + 0, 0), + GATE(fimc3, "fimc3", "aclk160", GATE_IP_CAM, 3, + 0, 0), + GATE(csis0, "csis0", "aclk160", GATE_IP_CAM, 4, + 0, 0), + GATE(csis1, "csis1", "aclk160", GATE_IP_CAM, 5, + 0, 0), + GATE(smmu_fimc0, "smmu_fimc0", "aclk160", GATE_IP_CAM, 7, + 0, 0), + GATE(smmu_fimc1, "smmu_fimc1", "aclk160", GATE_IP_CAM, 8, + 0, 0), + GATE(smmu_fimc2, "smmu_fimc2", "aclk160", GATE_IP_CAM, 9, + 0, 0), + GATE(smmu_fimc3, "smmu_fimc3", "aclk160", GATE_IP_CAM, 10, + 0, 0), + GATE(smmu_jpeg, "smmu_jpeg", "aclk160", GATE_IP_CAM, 11, + 0, 0), 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"), + GATE(smmu_tv, "smmu_tv", "aclk160", GATE_IP_TV, 4, + 0, 0), + GATE(mfc, "mfc", "aclk100", GATE_IP_MFC, 0, 0, 0), + GATE(smmu_mfcl, "smmu_mfcl", "aclk100", GATE_IP_MFC, 1, + 0, 0), + GATE(smmu_mfcr, "smmu_mfcr", "aclk100", GATE_IP_MFC, 2, + 0, 0), + GATE(fimd0, "fimd0", "aclk160", GATE_IP_LCD0, 0, + 0, 0), + GATE(smmu_fimd0, "smmu_fimd0", "aclk160", GATE_IP_LCD0, 4, + 0, 0), + GATE(pdma0, "pdma0", "aclk133", GATE_IP_FSYS, 0, + 0, 0), + GATE(pdma1, "pdma1", "aclk133", GATE_IP_FSYS, 1, + 0, 0), + GATE(sdmmc0, "sdmmc0", "aclk133", GATE_IP_FSYS, 5, + 0, 0), + GATE(sdmmc1, "sdmmc1", "aclk133", GATE_IP_FSYS, 6, + 0, 0), + GATE(sdmmc2, "sdmmc2", "aclk133", GATE_IP_FSYS, 7, + 0, 0), + GATE(sdmmc3, "sdmmc3", "aclk133", GATE_IP_FSYS, 8, + 0, 0), + GATE(uart0, "uart0", "aclk100", GATE_IP_PERIL, 0, + 0, 0), + GATE(uart1, "uart1", "aclk100", GATE_IP_PERIL, 1, + 0, 0), + GATE(uart2, "uart2", "aclk100", GATE_IP_PERIL, 2, + 0, 0), + GATE(uart3, "uart3", "aclk100", GATE_IP_PERIL, 3, + 0, 0), + GATE(uart4, "uart4", "aclk100", GATE_IP_PERIL, 4, + 0, 0), + GATE(i2c0, "i2c0", "aclk100", GATE_IP_PERIL, 6, + 0, 0), + GATE(i2c1, "i2c1", "aclk100", GATE_IP_PERIL, 7, + 0, 0), + GATE(i2c2, "i2c2", "aclk100", GATE_IP_PERIL, 8, + 0, 0), + GATE(i2c3, "i2c3", "aclk100", GATE_IP_PERIL, 9, + 0, 0), + GATE(i2c4, "i2c4", "aclk100", GATE_IP_PERIL, 10, + 0, 0), + GATE(i2c5, "i2c5", "aclk100", GATE_IP_PERIL, 11, + 0, 0), + GATE(i2c6, "i2c6", "aclk100", GATE_IP_PERIL, 12, + 0, 0), + GATE(i2c7, "i2c7", "aclk100", GATE_IP_PERIL, 13, + 0, 0), + GATE(i2c_hdmi, "i2c-hdmi", "aclk100", GATE_IP_PERIL, 14, + 0, 0), + GATE(spi0, "spi0", "aclk100", GATE_IP_PERIL, 16, + 0, 0), + GATE(spi1, "spi1", "aclk100", GATE_IP_PERIL, 17, + 0, 0), + GATE(spi2, "spi2", "aclk100", GATE_IP_PERIL, 18, + 0, 0), + GATE(i2s1, "i2s1", "aclk100", GATE_IP_PERIL, 20, + 0, 0), + GATE(i2s2, "i2s2", "aclk100", GATE_IP_PERIL, 21, + 0, 0), + GATE(pcm1, "pcm1", "aclk100", GATE_IP_PERIL, 22, + 0, 0), + GATE(pcm2, "pcm2", "aclk100", GATE_IP_PERIL, 23, + 0, 0), + GATE(spdif, "spdif", "aclk100", GATE_IP_PERIL, 26, + 0, 0), + GATE(ac97, "ac97", "aclk100", GATE_IP_PERIL, 27, + 0, 0), }; /* list of gate clocks supported in exynos4210 soc */ -struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { +static 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), @@ -802,17 +803,23 @@ struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { 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"), + GATE(tsadc, "tsadc", "aclk100", GATE_IP_PERIL, 15, + 0, 0), + GATE(mct, "mct", "aclk100", E4210_GATE_IP_PERIR, 13, + 0, 0), + GATE(wdt, "watchdog", "aclk100", E4210_GATE_IP_PERIR, 14, + 0, 0), + GATE(rtc, "rtc", "aclk100", E4210_GATE_IP_PERIR, 15, + 0, 0), + GATE(keyif, "keyif", "aclk100", E4210_GATE_IP_PERIR, 16, + 0, 0), + GATE(sclk_fimd1, "sclk_fimd1", "div_fimd1", E4210_SRC_MASK_LCD1, 0, + CLK_SET_RATE_PARENT, 0), + GATE(tmu_apbif, "tmu_apbif", "aclk100", E4210_GATE_IP_PERIR, 17, 0, 0), }; /* list of gate clocks supported in exynos4x12 soc */ -struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { +static 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), @@ -831,10 +838,11 @@ struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { 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(mct, "mct", "aclk100", E4X12_GATE_IP_PERIR, 13, + 0, 0), + GATE(rtc, "rtc", "aclk100", E4X12_GATE_IP_PERIR, 15, + 0, 0), + GATE(keyif, "keyif", "aclk100", E4X12_GATE_IP_PERIR, 16, 0, 0), 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", @@ -851,64 +859,79 @@ struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { 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(wdt, "watchdog", "aclk100", E4X12_GATE_IP_PERIR, 14, 0, 0), + GATE(pcm0, "pcm0", "aclk100", E4X12_GATE_IP_MAUDIO, 2, + 0, 0), + GATE(i2s0, "i2s0", "aclk100", E4X12_GATE_IP_MAUDIO, 3, + 0, 0), GATE(fimc_isp, "isp", "aclk200", E4X12_GATE_ISP0, 0, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(fimc_drc, "drc", "aclk200", E4X12_GATE_ISP0, 1, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(fimc_fd, "fd", "aclk200", E4X12_GATE_ISP0, 2, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(fimc_lite0, "lite0", "aclk200", E4X12_GATE_ISP0, 3, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(fimc_lite1, "lite1", "aclk200", E4X12_GATE_ISP0, 4, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(mcuisp, "mcuisp", "aclk200", E4X12_GATE_ISP0, 5, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(gicisp, "gicisp", "aclk200", E4X12_GATE_ISP0, 7, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_isp, "smmu_isp", "aclk200", E4X12_GATE_ISP0, 8, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_drc, "smmu_drc", "aclk200", E4X12_GATE_ISP0, 9, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_fd, "smmu_fd", "aclk200", E4X12_GATE_ISP0, 10, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_lite0, "smmu_lite0", "aclk200", E4X12_GATE_ISP0, 11, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_lite1, "smmu_lite1", "aclk200", E4X12_GATE_ISP0, 12, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(ppmuispmx, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(ppmuispx, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(mcuctl_isp, "mcuctl_isp", "aclk200", E4X12_GATE_ISP0, 23, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(mpwm_isp, "mpwm_isp", "aclk200", E4X12_GATE_ISP0, 24, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(i2c0_isp, "i2c0_isp", "aclk200", E4X12_GATE_ISP0, 25, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(i2c1_isp, "i2c1_isp", "aclk200", E4X12_GATE_ISP0, 26, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(mtcadc_isp, "mtcadc_isp", "aclk200", E4X12_GATE_ISP0, 27, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(pwm_isp, "pwm_isp", "aclk200", E4X12_GATE_ISP0, 28, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(wdt_isp, "wdt_isp", "aclk200", E4X12_GATE_ISP0, 30, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(uart_isp, "uart_isp", "aclk200", E4X12_GATE_ISP0, 31, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(asyncaxim, "asyncaxim", "aclk200", E4X12_GATE_ISP1, 0, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_ispcx, "smmu_ispcx", "aclk200", E4X12_GATE_ISP1, 4, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(spi0_isp, "spi0_isp", "aclk200", E4X12_GATE_ISP1, 12, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(spi1_isp, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), + GATE(g2d, "g2d", "aclk200", GATE_IP_DMC, 23, 0, 0), + GATE(tmu_apbif, "tmu_apbif", "aclk100", E4X12_GATE_IP_PERIR, 17, 0, 0), +}; + +static struct samsung_clock_alias exynos4_aliases[] __initdata = { + ALIAS(mout_core, NULL, "moutcore"), + ALIAS(arm_clk, NULL, "armclk"), + ALIAS(sclk_apll, NULL, "mout_apll"), +}; + +static struct samsung_clock_alias exynos4210_aliases[] __initdata = { + ALIAS(sclk_mpll, NULL, "mout_mpll"), +}; + +static struct samsung_clock_alias exynos4x12_aliases[] __initdata = { + ALIAS(mout_mpll_user_c, NULL, "mout_mpll"), }; /* @@ -963,36 +986,116 @@ static void __init exynos4_clk_register_finpll(unsigned long xom) } -/* - * 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[] = { +static struct of_device_id ext_clk_match[] __initdata = { { .compatible = "samsung,clock-xxti", .data = (void *)0, }, { .compatible = "samsung,clock-xusbxti", .data = (void *)1, }, {}, }; +/* PLLs PMS values */ +static struct samsung_pll_rate_table exynos4210_apll_rates[] __initdata = { + PLL_45XX_RATE(1200000000, 150, 3, 1, 28), + PLL_45XX_RATE(1000000000, 250, 6, 1, 28), + PLL_45XX_RATE( 800000000, 200, 6, 1, 28), + PLL_45XX_RATE( 666857142, 389, 14, 1, 13), + PLL_45XX_RATE( 600000000, 100, 4, 1, 13), + PLL_45XX_RATE( 533000000, 533, 24, 1, 5), + PLL_45XX_RATE( 500000000, 250, 6, 2, 28), + PLL_45XX_RATE( 400000000, 200, 6, 2, 28), + PLL_45XX_RATE( 200000000, 200, 6, 3, 28), + { /* sentinel */ } +}; + +static struct samsung_pll_rate_table exynos4210_epll_rates[] __initdata = { + PLL_4600_RATE(192000000, 48, 3, 1, 0, 0), + PLL_4600_RATE(180633605, 45, 3, 1, 10381, 0), + PLL_4600_RATE(180000000, 45, 3, 1, 0, 0), + PLL_4600_RATE( 73727996, 73, 3, 3, 47710, 1), + PLL_4600_RATE( 67737602, 90, 4, 3, 20762, 1), + PLL_4600_RATE( 49151992, 49, 3, 3, 9961, 0), + PLL_4600_RATE( 45158401, 45, 3, 3, 10381, 0), + { /* sentinel */ } +}; + +static struct samsung_pll_rate_table exynos4210_vpll_rates[] __initdata = { + PLL_4650_RATE(360000000, 44, 3, 0, 1024, 0, 14, 0), + PLL_4650_RATE(324000000, 53, 2, 1, 1024, 1, 1, 1), + PLL_4650_RATE(259617187, 63, 3, 1, 1950, 0, 20, 1), + PLL_4650_RATE(110000000, 53, 3, 2, 2048, 0, 17, 0), + PLL_4650_RATE( 55360351, 53, 3, 3, 2417, 0, 17, 0), + { /* sentinel */ } +}; + +static struct samsung_pll_rate_table exynos4x12_apll_rates[] __initdata = { + PLL_35XX_RATE(1500000000, 250, 4, 0), + PLL_35XX_RATE(1400000000, 175, 3, 0), + PLL_35XX_RATE(1300000000, 325, 6, 0), + PLL_35XX_RATE(1200000000, 200, 4, 0), + PLL_35XX_RATE(1100000000, 275, 6, 0), + PLL_35XX_RATE(1000000000, 125, 3, 0), + PLL_35XX_RATE( 900000000, 150, 4, 0), + PLL_35XX_RATE( 800000000, 100, 3, 0), + PLL_35XX_RATE( 700000000, 175, 3, 1), + PLL_35XX_RATE( 600000000, 200, 4, 1), + PLL_35XX_RATE( 500000000, 125, 3, 1), + PLL_35XX_RATE( 400000000, 100, 3, 1), + PLL_35XX_RATE( 300000000, 200, 4, 2), + PLL_35XX_RATE( 200000000, 100, 3, 2), + { /* sentinel */ } +}; + +static struct samsung_pll_rate_table exynos4x12_epll_rates[] __initdata = { + PLL_36XX_RATE(192000000, 48, 3, 1, 0), + PLL_36XX_RATE(180633605, 45, 3, 1, 10381), + PLL_36XX_RATE(180000000, 45, 3, 1, 0), + PLL_36XX_RATE( 73727996, 73, 3, 3, 47710), + PLL_36XX_RATE( 67737602, 90, 4, 3, 20762), + PLL_36XX_RATE( 49151992, 49, 3, 3, 9961), + PLL_36XX_RATE( 45158401, 45, 3, 3, 10381), + { /* sentinel */ } +}; + +static struct samsung_pll_rate_table exynos4x12_vpll_rates[] __initdata = { + PLL_36XX_RATE(533000000, 133, 3, 1, 16384), + PLL_36XX_RATE(440000000, 110, 3, 1, 0), + PLL_36XX_RATE(350000000, 175, 3, 2, 0), + PLL_36XX_RATE(266000000, 133, 3, 2, 0), + PLL_36XX_RATE(160000000, 160, 3, 3, 0), + PLL_36XX_RATE(106031250, 53, 3, 2, 1024), + PLL_36XX_RATE( 53015625, 53, 3, 3, 1024), + { /* sentinel */ } +}; + +static struct samsung_pll_clock exynos4210_plls[nr_plls] __initdata = { + [apll] = PLL_A(pll_4508, fout_apll, "fout_apll", "fin_pll", APLL_LOCK, + APLL_CON0, "fout_apll", NULL), + [mpll] = PLL_A(pll_4508, fout_mpll, "fout_mpll", "fin_pll", + E4210_MPLL_LOCK, E4210_MPLL_CON0, "fout_mpll", NULL), + [epll] = PLL_A(pll_4600, fout_epll, "fout_epll", "fin_pll", EPLL_LOCK, + EPLL_CON0, "fout_epll", NULL), + [vpll] = PLL_A(pll_4650c, fout_vpll, "fout_vpll", "mout_vpllsrc", + VPLL_LOCK, VPLL_CON0, "fout_vpll", NULL), +}; + +static struct samsung_pll_clock exynos4x12_plls[nr_plls] __initdata = { + [apll] = PLL(pll_35xx, fout_apll, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON0, NULL), + [mpll] = PLL(pll_35xx, fout_mpll, "fout_mpll", "fin_pll", + E4X12_MPLL_LOCK, E4X12_MPLL_CON0, NULL), + [epll] = PLL(pll_36xx, fout_epll, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), + [vpll] = PLL(pll_36xx, fout_vpll, "fout_vpll", "fin_pll", + VPLL_LOCK, VPLL_CON0, NULL), +}; + /* register exynos4 clocks */ -void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_soc, void __iomem *reg_base, unsigned long xom) +static 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__); - } + 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, @@ -1003,37 +1106,42 @@ void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_so 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, + 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); + samsung_clk_register_mux(exynos4210_mux_early, + ARRAY_SIZE(exynos4210_mux_early)); + + if (_get_rate("fin_pll") == 24000000) { + exynos4210_plls[apll].rate_table = + exynos4210_apll_rates; + exynos4210_plls[epll].rate_table = + exynos4210_epll_rates; + } + + if (_get_rate("mout_vpllsrc") == 24000000) + exynos4210_plls[vpll].rate_table = + exynos4210_vpll_rates; + + samsung_clk_register_pll(exynos4210_plls, + ARRAY_SIZE(exynos4210_plls), reg_base); } 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); - } + if (_get_rate("fin_pll") == 24000000) { + exynos4x12_plls[apll].rate_table = + exynos4x12_apll_rates; + exynos4x12_plls[epll].rate_table = + exynos4x12_epll_rates; + exynos4x12_plls[vpll].rate_table = + exynos4x12_vpll_rates; + } - 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_pll(exynos4x12_plls, + ARRAY_SIZE(exynos4x12_plls), reg_base); + } samsung_clk_register_fixed_rate(exynos4_fixed_rate_clks, ARRAY_SIZE(exynos4_fixed_rate_clks)); @@ -1053,6 +1161,8 @@ void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_so ARRAY_SIZE(exynos4210_div_clks)); samsung_clk_register_gate(exynos4210_gate_clks, ARRAY_SIZE(exynos4210_gate_clks)); + samsung_clk_register_alias(exynos4210_aliases, + ARRAY_SIZE(exynos4210_aliases)); } else { samsung_clk_register_mux(exynos4x12_mux_clks, ARRAY_SIZE(exynos4x12_mux_clks)); @@ -1060,8 +1170,13 @@ void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_so ARRAY_SIZE(exynos4x12_div_clks)); samsung_clk_register_gate(exynos4x12_gate_clks, ARRAY_SIZE(exynos4x12_gate_clks)); + samsung_clk_register_alias(exynos4x12_aliases, + ARRAY_SIZE(exynos4x12_aliases)); } + samsung_clk_register_alias(exynos4_aliases, + ARRAY_SIZE(exynos4_aliases)); + 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", diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 5c97e75924a8..adf32343c9f9 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -17,11 +17,22 @@ #include <linux/of_address.h> #include "clk.h" -#include "clk-pll.h" +#define APLL_LOCK 0x0 +#define APLL_CON0 0x100 #define SRC_CPU 0x200 #define DIV_CPU0 0x500 +#define MPLL_LOCK 0x4000 +#define MPLL_CON0 0x4100 #define SRC_CORE1 0x4204 +#define CPLL_LOCK 0x10020 +#define EPLL_LOCK 0x10030 +#define VPLL_LOCK 0x10040 +#define GPLL_LOCK 0x10050 +#define CPLL_CON0 0x10120 +#define EPLL_CON0 0x10130 +#define VPLL_CON0 0x10140 +#define GPLL_CON0 0x10150 #define SRC_TOP0 0x10210 #define SRC_TOP2 0x10218 #define SRC_GSCL 0x10220 @@ -59,9 +70,18 @@ #define GATE_IP_FSYS 0x10944 #define GATE_IP_PERIC 0x10950 #define GATE_IP_PERIS 0x10960 +#define BPLL_LOCK 0x20010 +#define BPLL_CON0 0x20110 #define SRC_CDREX 0x20200 #define PLL_DIV2_SEL 0x20a24 #define GATE_IP_DISP1 0x10928 +#define GATE_IP_ACP 0x10000 + +/* list of PLLs to be registered */ +enum exynos5250_plls { + apll, mpll, cpll, epll, vpll, gpll, bpll, + nr_plls /* number of PLLs */ +}; /* * Let each supported clock get a unique id. This id is used to lookup the clock @@ -79,7 +99,8 @@ enum exynos5250_clks { none, /* core clocks */ - fin_pll, + fin_pll, fout_apll, fout_mpll, fout_bpll, fout_gpll, fout_cpll, + fout_epll, fout_vpll, /* gate for special clocks (sclk) */ sclk_cam_bayer = 128, sclk_cam0, sclk_cam1, sclk_gscl_wa, sclk_gscl_wb, @@ -87,6 +108,7 @@ enum exynos5250_clks { 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, + div_i2s1, div_i2s2, sclk_hdmiphy, /* gate clocks */ gscl0 = 256, gscl1, gscl2, gscl3, gscl_wa, gscl_wb, smmu_gscl0, @@ -98,7 +120,10 @@ enum exynos5250_clks { 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, + wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, g2d, + + /* mux clocks */ + mout_hdmi = 1024, nr_clks, }; @@ -107,7 +132,7 @@ enum exynos5250_clks { * list of controller registers to be saved and restored during a * suspend/resume cycle. */ -static __initdata unsigned long exynos5250_clk_regs[] = { +static unsigned long exynos5250_clk_regs[] __initdata = { SRC_CPU, DIV_CPU0, SRC_CORE1, @@ -151,11 +176,12 @@ static __initdata unsigned long exynos5250_clk_regs[] = { SRC_CDREX, PLL_DIV2_SEL, GATE_IP_DISP1, + GATE_IP_ACP, }; /* list of all parent clock list */ PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; -PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", }; +PNAME(mout_cpu_p) = { "mout_apll", "sclk_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" }; @@ -190,31 +216,34 @@ 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 = { +static 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), +static struct samsung_fixed_rate_clock exynos5250_fixed_rate_clks[] __initdata = { + FRATE(sclk_hdmiphy, "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 = { +static 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), +static struct samsung_mux_clock exynos5250_pll_pmux_clks[] __initdata = { + MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1), +}; + +static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { + MUX_A(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, "mout_apll"), + MUX_A(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1, "mout_cpu"), 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_A(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1, "mout_mpll"), 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), @@ -231,7 +260,7 @@ struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { 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(mout_hdmi, "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), @@ -253,7 +282,7 @@ struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { MUX(none, "mout_spi2", mout_group1_p, SRC_PERIC1, 24, 4), }; -struct samsung_div_clock exynos5250_div_clks[] __initdata = { +static 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), @@ -291,8 +320,8 @@ struct samsung_div_clock exynos5250_div_clks[] __initdata = { 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(div_i2s1, "div_i2s1", "sclk_audio1", DIV_PERIC5, 0, 6), + DIV(div_i2s2, "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", @@ -313,7 +342,7 @@ struct samsung_div_clock exynos5250_div_clks[] __initdata = { DIV_PERIC2, 8, 8, CLK_SET_RATE_PARENT, 0), }; -struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { +static 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), @@ -378,7 +407,7 @@ struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { 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(pmu, "pmu", "aclk66", GATE_IP_PERIS, 2, CLK_IGNORE_UNUSED, 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), @@ -460,20 +489,60 @@ struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { 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), + GATE(mixer, "mixer", "mout_aclk200_disp1", GATE_IP_DISP1, 5, 0, 0), + GATE(hdmi, "hdmi", "mout_aclk200_disp1", GATE_IP_DISP1, 6, 0, 0), + GATE(g2d, "g2d", "aclk200", GATE_IP_ACP, 3, 0, 0), +}; + +static struct samsung_pll_rate_table vpll_24mhz_tbl[] __initdata = { + /* sorted in descending order */ + /* PLL_36XX_RATE(rate, m, p, s, k) */ + PLL_36XX_RATE(266000000, 266, 3, 3, 0), + /* Not in UM, but need for eDP on snow */ + PLL_36XX_RATE(70500000, 94, 2, 4, 0), + { }, +}; + +static struct samsung_pll_rate_table epll_24mhz_tbl[] __initdata = { + /* sorted in descending order */ + /* PLL_36XX_RATE(rate, m, p, s, k) */ + PLL_36XX_RATE(192000000, 64, 2, 2, 0), + PLL_36XX_RATE(180633600, 90, 3, 2, 20762), + PLL_36XX_RATE(180000000, 90, 3, 2, 0), + PLL_36XX_RATE(73728000, 98, 2, 4, 19923), + PLL_36XX_RATE(67737600, 90, 2, 4, 20762), + PLL_36XX_RATE(49152000, 98, 3, 4, 19923), + PLL_36XX_RATE(45158400, 90, 3, 4, 20762), + PLL_36XX_RATE(32768000, 131, 3, 5, 4719), + { }, +}; + +static struct samsung_pll_clock exynos5250_plls[nr_plls] __initdata = { + [apll] = PLL_A(pll_35xx, fout_apll, "fout_apll", "fin_pll", APLL_LOCK, + APLL_CON0, "fout_apll", NULL), + [mpll] = PLL_A(pll_35xx, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK, + MPLL_CON0, "fout_mpll", NULL), + [bpll] = PLL(pll_35xx, fout_bpll, "fout_bpll", "fin_pll", BPLL_LOCK, + BPLL_CON0, NULL), + [gpll] = PLL(pll_35xx, fout_gpll, "fout_gpll", "fin_pll", GPLL_LOCK, + GPLL_CON0, NULL), + [cpll] = PLL(pll_35xx, fout_cpll, "fout_cpll", "fin_pll", CPLL_LOCK, + CPLL_CON0, NULL), + [epll] = PLL(pll_36xx, fout_epll, "fout_epll", "fin_pll", EPLL_LOCK, + EPLL_CON0, NULL), + [vpll] = PLL(pll_36xx, fout_vpll, "fout_vpll", "mout_vpllsrc", + VPLL_LOCK, VPLL_CON0, NULL), }; -static __initdata struct of_device_id ext_clk_match[] = { +static struct of_device_id ext_clk_match[] __initdata = { { .compatible = "samsung,clock-xxti", .data = (void *)0, }, { }, }; /* register exynox5250 clocks */ -void __init exynos5250_clk_init(struct device_node *np) +static 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); @@ -489,22 +558,17 @@ void __init exynos5250_clk_init(struct device_node *np) samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks, ARRAY_SIZE(exynos5250_fixed_rate_ext_clks), ext_clk_match); + samsung_clk_register_mux(exynos5250_pll_pmux_clks, + ARRAY_SIZE(exynos5250_pll_pmux_clks)); + + if (_get_rate("fin_pll") == 24 * MHZ) + exynos5250_plls[epll].rate_table = epll_24mhz_tbl; - 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); + if (_get_rate("mout_vpllsrc") == 24 * MHZ) + exynos5250_plls[vpll].rate_table = vpll_24mhz_tbl; + samsung_clk_register_pll(exynos5250_plls, ARRAY_SIZE(exynos5250_plls), + reg_base); samsung_clk_register_fixed_rate(exynos5250_fixed_rate_clks, ARRAY_SIZE(exynos5250_fixed_rate_clks)); samsung_clk_register_fixed_factor(exynos5250_fixed_factor_clks, diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c new file mode 100644 index 000000000000..48c4a9350b91 --- /dev/null +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -0,0 +1,799 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Authors: Thomas Abraham <thomas.ab@samsung.com> + * Chander Kashyap <k.chander@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 Exynos5420 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" + +#define APLL_LOCK 0x0 +#define APLL_CON0 0x100 +#define SRC_CPU 0x200 +#define DIV_CPU0 0x500 +#define DIV_CPU1 0x504 +#define GATE_BUS_CPU 0x700 +#define GATE_SCLK_CPU 0x800 +#define CPLL_LOCK 0x10020 +#define DPLL_LOCK 0x10030 +#define EPLL_LOCK 0x10040 +#define RPLL_LOCK 0x10050 +#define IPLL_LOCK 0x10060 +#define SPLL_LOCK 0x10070 +#define VPLL_LOCK 0x10070 +#define MPLL_LOCK 0x10090 +#define CPLL_CON0 0x10120 +#define DPLL_CON0 0x10128 +#define EPLL_CON0 0x10130 +#define RPLL_CON0 0x10140 +#define IPLL_CON0 0x10150 +#define SPLL_CON0 0x10160 +#define VPLL_CON0 0x10170 +#define MPLL_CON0 0x10180 +#define SRC_TOP0 0x10200 +#define SRC_TOP1 0x10204 +#define SRC_TOP2 0x10208 +#define SRC_TOP3 0x1020c +#define SRC_TOP4 0x10210 +#define SRC_TOP5 0x10214 +#define SRC_TOP6 0x10218 +#define SRC_TOP7 0x1021c +#define SRC_DISP10 0x1022c +#define SRC_MAU 0x10240 +#define SRC_FSYS 0x10244 +#define SRC_PERIC0 0x10250 +#define SRC_PERIC1 0x10254 +#define SRC_TOP10 0x10280 +#define SRC_TOP11 0x10284 +#define SRC_TOP12 0x10288 +#define SRC_MASK_DISP10 0x1032c +#define SRC_MASK_FSYS 0x10340 +#define SRC_MASK_PERIC0 0x10350 +#define SRC_MASK_PERIC1 0x10354 +#define DIV_TOP0 0x10500 +#define DIV_TOP1 0x10504 +#define DIV_TOP2 0x10508 +#define DIV_DISP10 0x1052c +#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 GATE_BUS_TOP 0x10700 +#define GATE_BUS_FSYS0 0x10740 +#define GATE_BUS_PERIC 0x10750 +#define GATE_BUS_PERIC1 0x10754 +#define GATE_BUS_PERIS0 0x10760 +#define GATE_BUS_PERIS1 0x10764 +#define GATE_IP_GSCL0 0x10910 +#define GATE_IP_GSCL1 0x10920 +#define GATE_IP_MFC 0x1092c +#define GATE_IP_DISP1 0x10928 +#define GATE_IP_G3D 0x10930 +#define GATE_IP_GEN 0x10934 +#define GATE_IP_MSCL 0x10970 +#define GATE_TOP_SCLK_GSCL 0x10820 +#define GATE_TOP_SCLK_DISP1 0x10828 +#define GATE_TOP_SCLK_MAU 0x1083c +#define GATE_TOP_SCLK_FSYS 0x10840 +#define GATE_TOP_SCLK_PERIC 0x10850 +#define BPLL_LOCK 0x20010 +#define BPLL_CON0 0x20110 +#define SRC_CDREX 0x20200 +#define KPLL_LOCK 0x28000 +#define KPLL_CON0 0x28100 +#define SRC_KFC 0x28200 +#define DIV_KFC0 0x28500 + +/* list of PLLs */ +enum exynos5420_plls { + apll, cpll, dpll, epll, rpll, ipll, spll, vpll, mpll, + bpll, kpll, + nr_plls /* number of PLLs */ +}; + +enum exynos5420_clks { + none, + + /* core clocks */ + fin_pll, fout_apll, fout_cpll, fout_dpll, fout_epll, fout_rpll, + fout_ipll, fout_spll, fout_vpll, fout_mpll, fout_bpll, fout_kpll, + + /* gate for special clocks (sclk) */ + sclk_uart0 = 128, sclk_uart1, sclk_uart2, sclk_uart3, sclk_mmc0, + sclk_mmc1, sclk_mmc2, sclk_spi0, sclk_spi1, sclk_spi2, sclk_i2s1, + sclk_i2s2, sclk_pcm1, sclk_pcm2, sclk_spdif, sclk_hdmi, sclk_pixel, + sclk_dp1, sclk_mipi1, sclk_fimd1, sclk_maudio0, sclk_maupcm0, + sclk_usbd300, sclk_usbd301, sclk_usbphy300, sclk_usbphy301, sclk_unipro, + sclk_pwm, sclk_gscl_wa, sclk_gscl_wb, sclk_hdmiphy, + + /* gate clocks */ + aclk66_peric = 256, uart0, uart1, uart2, uart3, i2c0, i2c1, i2c2, i2c3, + i2c4, i2c5, i2c6, i2c7, i2c_hdmi, tsadc, spi0, spi1, spi2, keyif, i2s1, + i2s2, pcm1, pcm2, pwm, spdif, i2c8, i2c9, i2c10, aclk66_psgen = 300, + chipid, sysreg, tzpc0, tzpc1, tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, + tzpc8, tzpc9, hdmi_cec, seckey, mct, wdt, rtc, tmu, tmu_gpu, + pclk66_gpio = 330, aclk200_fsys2 = 350, mmc0, mmc1, mmc2, sromc, ufs, + aclk200_fsys = 360, tsi, pdma0, pdma1, rtic, usbh20, usbd300, usbd301, + aclk400_mscl = 380, mscl0, mscl1, mscl2, smmu_mscl0, smmu_mscl1, + smmu_mscl2, aclk333 = 400, mfc, smmu_mfcl, smmu_mfcr, + aclk200_disp1 = 410, dsim1, dp1, hdmi, aclk300_disp1 = 420, fimd1, + smmu_fimd1, aclk166 = 430, mixer, aclk266 = 440, rotator, mdma1, + smmu_rotator, smmu_mdma1, aclk300_jpeg = 450, jpeg, jpeg2, smmu_jpeg, + aclk300_gscl = 460, smmu_gscl0, smmu_gscl1, gscl_wa, gscl_wb, gscl0, + gscl1, clk_3aa, aclk266_g2d = 470, sss, slim_sss, mdma0, + aclk333_g2d = 480, g2d, aclk333_432_gscl = 490, smmu_3aa, smmu_fimcl0, + smmu_fimcl1, smmu_fimcl3, fimc_lite3, aclk_g3d = 500, g3d, smmu_mixer, + + /* mux clocks */ + mout_hdmi = 640, + + /* divider clocks */ + dout_pixel = 768, + + nr_clks, +}; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static unsigned long exynos5420_clk_regs[] __initdata = { + SRC_CPU, + DIV_CPU0, + DIV_CPU1, + GATE_BUS_CPU, + GATE_SCLK_CPU, + SRC_TOP0, + SRC_TOP1, + SRC_TOP2, + SRC_TOP3, + SRC_TOP4, + SRC_TOP5, + SRC_TOP6, + SRC_TOP7, + SRC_DISP10, + SRC_MAU, + SRC_FSYS, + SRC_PERIC0, + SRC_PERIC1, + SRC_TOP10, + SRC_TOP11, + SRC_TOP12, + SRC_MASK_DISP10, + SRC_MASK_FSYS, + SRC_MASK_PERIC0, + SRC_MASK_PERIC1, + DIV_TOP0, + DIV_TOP1, + DIV_TOP2, + DIV_DISP10, + DIV_MAU, + DIV_FSYS0, + DIV_FSYS1, + DIV_FSYS2, + DIV_PERIC0, + DIV_PERIC1, + DIV_PERIC2, + DIV_PERIC3, + DIV_PERIC4, + GATE_BUS_TOP, + GATE_BUS_FSYS0, + GATE_BUS_PERIC, + GATE_BUS_PERIC1, + GATE_BUS_PERIS0, + GATE_BUS_PERIS1, + GATE_IP_GSCL0, + GATE_IP_GSCL1, + GATE_IP_MFC, + GATE_IP_DISP1, + GATE_IP_G3D, + GATE_IP_GEN, + GATE_IP_MSCL, + GATE_TOP_SCLK_GSCL, + GATE_TOP_SCLK_DISP1, + GATE_TOP_SCLK_MAU, + GATE_TOP_SCLK_FSYS, + GATE_TOP_SCLK_PERIC, + SRC_CDREX, + SRC_KFC, + DIV_KFC0, +}; + +/* list of all parent clocks */ +PNAME(mspll_cpu_p) = { "sclk_cpll", "sclk_dpll", + "sclk_mpll", "sclk_spll" }; +PNAME(cpu_p) = { "mout_apll" , "mout_mspll_cpu" }; +PNAME(kfc_p) = { "mout_kpll" , "mout_mspll_kfc" }; +PNAME(apll_p) = { "fin_pll", "fout_apll", }; +PNAME(bpll_p) = { "fin_pll", "fout_bpll", }; +PNAME(cpll_p) = { "fin_pll", "fout_cpll", }; +PNAME(dpll_p) = { "fin_pll", "fout_dpll", }; +PNAME(epll_p) = { "fin_pll", "fout_epll", }; +PNAME(ipll_p) = { "fin_pll", "fout_ipll", }; +PNAME(kpll_p) = { "fin_pll", "fout_kpll", }; +PNAME(mpll_p) = { "fin_pll", "fout_mpll", }; +PNAME(rpll_p) = { "fin_pll", "fout_rpll", }; +PNAME(spll_p) = { "fin_pll", "fout_spll", }; +PNAME(vpll_p) = { "fin_pll", "fout_vpll", }; + +PNAME(group1_p) = { "sclk_cpll", "sclk_dpll", "sclk_mpll" }; +PNAME(group2_p) = { "fin_pll", "sclk_cpll", "sclk_dpll", "sclk_mpll", + "sclk_spll", "sclk_ipll", "sclk_epll", "sclk_rpll" }; +PNAME(group3_p) = { "sclk_rpll", "sclk_spll" }; +PNAME(group4_p) = { "sclk_ipll", "sclk_dpll", "sclk_mpll" }; +PNAME(group5_p) = { "sclk_vpll", "sclk_dpll" }; + +PNAME(sw_aclk66_p) = { "dout_aclk66", "sclk_spll" }; +PNAME(aclk66_peric_p) = { "fin_pll", "mout_sw_aclk66" }; + +PNAME(sw_aclk200_fsys_p) = { "dout_aclk200_fsys", "sclk_spll"}; +PNAME(user_aclk200_fsys_p) = { "fin_pll", "mout_sw_aclk200_fsys" }; + +PNAME(sw_aclk200_fsys2_p) = { "dout_aclk200_fsys2", "sclk_spll"}; +PNAME(user_aclk200_fsys2_p) = { "fin_pll", "mout_sw_aclk200_fsys2" }; + +PNAME(sw_aclk200_p) = { "dout_aclk200", "sclk_spll"}; +PNAME(aclk200_disp1_p) = { "fin_pll", "mout_sw_aclk200" }; + +PNAME(sw_aclk400_mscl_p) = { "dout_aclk400_mscl", "sclk_spll"}; +PNAME(user_aclk400_mscl_p) = { "fin_pll", "mout_sw_aclk400_mscl" }; + +PNAME(sw_aclk333_p) = { "dout_aclk333", "sclk_spll"}; +PNAME(user_aclk333_p) = { "fin_pll", "mout_sw_aclk333" }; + +PNAME(sw_aclk166_p) = { "dout_aclk166", "sclk_spll"}; +PNAME(user_aclk166_p) = { "fin_pll", "mout_sw_aclk166" }; + +PNAME(sw_aclk266_p) = { "dout_aclk266", "sclk_spll"}; +PNAME(user_aclk266_p) = { "fin_pll", "mout_sw_aclk266" }; + +PNAME(sw_aclk333_432_gscl_p) = { "dout_aclk333_432_gscl", "sclk_spll"}; +PNAME(user_aclk333_432_gscl_p) = { "fin_pll", "mout_sw_aclk333_432_gscl" }; + +PNAME(sw_aclk300_gscl_p) = { "dout_aclk300_gscl", "sclk_spll"}; +PNAME(user_aclk300_gscl_p) = { "fin_pll", "mout_sw_aclk300_gscl" }; + +PNAME(sw_aclk300_disp1_p) = { "dout_aclk300_disp1", "sclk_spll"}; +PNAME(user_aclk300_disp1_p) = { "fin_pll", "mout_sw_aclk300_disp1" }; + +PNAME(sw_aclk300_jpeg_p) = { "dout_aclk300_jpeg", "sclk_spll"}; +PNAME(user_aclk300_jpeg_p) = { "fin_pll", "mout_sw_aclk300_jpeg" }; + +PNAME(sw_aclk_g3d_p) = { "dout_aclk_g3d", "sclk_spll"}; +PNAME(user_aclk_g3d_p) = { "fin_pll", "mout_sw_aclk_g3d" }; + +PNAME(sw_aclk266_g2d_p) = { "dout_aclk266_g2d", "sclk_spll"}; +PNAME(user_aclk266_g2d_p) = { "fin_pll", "mout_sw_aclk266_g2d" }; + +PNAME(sw_aclk333_g2d_p) = { "dout_aclk333_g2d", "sclk_spll"}; +PNAME(user_aclk333_g2d_p) = { "fin_pll", "mout_sw_aclk333_g2d" }; + +PNAME(audio0_p) = { "fin_pll", "cdclk0", "sclk_dpll", "sclk_mpll", + "sclk_spll", "sclk_ipll", "sclk_epll", "sclk_rpll" }; +PNAME(audio1_p) = { "fin_pll", "cdclk1", "sclk_dpll", "sclk_mpll", + "sclk_spll", "sclk_ipll", "sclk_epll", "sclk_rpll" }; +PNAME(audio2_p) = { "fin_pll", "cdclk2", "sclk_dpll", "sclk_mpll", + "sclk_spll", "sclk_ipll", "sclk_epll", "sclk_rpll" }; +PNAME(spdif_p) = { "fin_pll", "dout_audio0", "dout_audio1", "dout_audio2", + "spdif_extclk", "sclk_ipll", "sclk_epll", "sclk_rpll" }; +PNAME(hdmi_p) = { "dout_hdmi_pixel", "sclk_hdmiphy" }; +PNAME(maudio0_p) = { "fin_pll", "maudio_clk", "sclk_dpll", "sclk_mpll", + "sclk_spll", "sclk_ipll", "sclk_epll", "sclk_rpll" }; + +/* fixed rate clocks generated outside the soc */ +static struct samsung_fixed_rate_clock exynos5420_fixed_rate_ext_clks[] __initdata = { + FRATE(fin_pll, "fin_pll", NULL, CLK_IS_ROOT, 0), +}; + +/* fixed rate clocks generated inside the soc */ +static struct samsung_fixed_rate_clock exynos5420_fixed_rate_clks[] __initdata = { + FRATE(sclk_hdmiphy, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000), + FRATE(none, "sclk_pwi", NULL, CLK_IS_ROOT, 24000000), + FRATE(none, "sclk_usbh20", NULL, CLK_IS_ROOT, 48000000), + FRATE(none, "mphy_refclk_ixtal24", NULL, CLK_IS_ROOT, 48000000), + FRATE(none, "sclk_usbh20_scan_clk", NULL, CLK_IS_ROOT, 480000000), +}; + +static struct samsung_fixed_factor_clock exynos5420_fixed_factor_clks[] __initdata = { + FFACTOR(none, "sclk_hsic_12m", "fin_pll", 1, 2, 0), +}; + +static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { + MUX(none, "mout_mspll_kfc", mspll_cpu_p, SRC_TOP7, 8, 2), + MUX(none, "mout_mspll_cpu", mspll_cpu_p, SRC_TOP7, 12, 2), + MUX(none, "mout_apll", apll_p, SRC_CPU, 0, 1), + MUX(none, "mout_cpu", cpu_p, SRC_CPU, 16, 1), + MUX(none, "mout_kpll", kpll_p, SRC_KFC, 0, 1), + MUX(none, "mout_cpu_kfc", kfc_p, SRC_KFC, 16, 1), + + MUX(none, "sclk_bpll", bpll_p, SRC_CDREX, 0, 1), + + MUX_A(none, "mout_aclk400_mscl", group1_p, + SRC_TOP0, 4, 2, "aclk400_mscl"), + MUX(none, "mout_aclk200", group1_p, SRC_TOP0, 8, 2), + MUX(none, "mout_aclk200_fsys2", group1_p, SRC_TOP0, 12, 2), + MUX(none, "mout_aclk200_fsys", group1_p, SRC_TOP0, 28, 2), + + MUX(none, "mout_aclk333_432_gscl", group4_p, SRC_TOP1, 0, 2), + MUX(none, "mout_aclk66", group1_p, SRC_TOP1, 8, 2), + MUX(none, "mout_aclk266", group1_p, SRC_TOP1, 20, 2), + MUX(none, "mout_aclk166", group1_p, SRC_TOP1, 24, 2), + MUX(none, "mout_aclk333", group1_p, SRC_TOP1, 28, 2), + + MUX(none, "mout_aclk333_g2d", group1_p, SRC_TOP2, 8, 2), + MUX(none, "mout_aclk266_g2d", group1_p, SRC_TOP2, 12, 2), + MUX(none, "mout_aclk_g3d", group5_p, SRC_TOP2, 16, 1), + MUX(none, "mout_aclk300_jpeg", group1_p, SRC_TOP2, 20, 2), + MUX(none, "mout_aclk300_disp1", group1_p, SRC_TOP2, 24, 2), + MUX(none, "mout_aclk300_gscl", group1_p, SRC_TOP2, 28, 2), + + MUX(none, "mout_user_aclk400_mscl", user_aclk400_mscl_p, + SRC_TOP3, 4, 1), + MUX_A(none, "mout_aclk200_disp1", aclk200_disp1_p, + SRC_TOP3, 8, 1, "aclk200_disp1"), + MUX(none, "mout_user_aclk200_fsys2", user_aclk200_fsys2_p, + SRC_TOP3, 12, 1), + MUX(none, "mout_user_aclk200_fsys", user_aclk200_fsys_p, + SRC_TOP3, 28, 1), + + MUX(none, "mout_user_aclk333_432_gscl", user_aclk333_432_gscl_p, + SRC_TOP4, 0, 1), + MUX(none, "mout_aclk66_peric", aclk66_peric_p, SRC_TOP4, 8, 1), + MUX(none, "mout_user_aclk266", user_aclk266_p, SRC_TOP4, 20, 1), + MUX(none, "mout_user_aclk166", user_aclk166_p, SRC_TOP4, 24, 1), + MUX(none, "mout_user_aclk333", user_aclk333_p, SRC_TOP4, 28, 1), + + MUX(none, "mout_aclk66_psgen", aclk66_peric_p, SRC_TOP5, 4, 1), + MUX(none, "mout_user_aclk333_g2d", user_aclk333_g2d_p, SRC_TOP5, 8, 1), + MUX(none, "mout_user_aclk266_g2d", user_aclk266_g2d_p, SRC_TOP5, 12, 1), + MUX_A(none, "mout_user_aclk_g3d", user_aclk_g3d_p, + SRC_TOP5, 16, 1, "aclkg3d"), + MUX(none, "mout_user_aclk300_jpeg", user_aclk300_jpeg_p, + SRC_TOP5, 20, 1), + MUX(none, "mout_user_aclk300_disp1", user_aclk300_disp1_p, + SRC_TOP5, 24, 1), + MUX(none, "mout_user_aclk300_gscl", user_aclk300_gscl_p, + SRC_TOP5, 28, 1), + + MUX(none, "sclk_mpll", mpll_p, SRC_TOP6, 0, 1), + MUX(none, "sclk_vpll", vpll_p, SRC_TOP6, 4, 1), + MUX(none, "sclk_spll", spll_p, SRC_TOP6, 8, 1), + MUX(none, "sclk_ipll", ipll_p, SRC_TOP6, 12, 1), + MUX(none, "sclk_rpll", rpll_p, SRC_TOP6, 16, 1), + MUX(none, "sclk_epll", epll_p, SRC_TOP6, 20, 1), + MUX(none, "sclk_dpll", dpll_p, SRC_TOP6, 24, 1), + MUX(none, "sclk_cpll", cpll_p, SRC_TOP6, 28, 1), + + MUX(none, "mout_sw_aclk400_mscl", sw_aclk400_mscl_p, SRC_TOP10, 4, 1), + MUX(none, "mout_sw_aclk200", sw_aclk200_p, SRC_TOP10, 8, 1), + MUX(none, "mout_sw_aclk200_fsys2", sw_aclk200_fsys2_p, + SRC_TOP10, 12, 1), + MUX(none, "mout_sw_aclk200_fsys", sw_aclk200_fsys_p, SRC_TOP10, 28, 1), + + MUX(none, "mout_sw_aclk333_432_gscl", sw_aclk333_432_gscl_p, + SRC_TOP11, 0, 1), + MUX(none, "mout_sw_aclk66", sw_aclk66_p, SRC_TOP11, 8, 1), + MUX(none, "mout_sw_aclk266", sw_aclk266_p, SRC_TOP11, 20, 1), + MUX(none, "mout_sw_aclk166", sw_aclk166_p, SRC_TOP11, 24, 1), + MUX(none, "mout_sw_aclk333", sw_aclk333_p, SRC_TOP11, 28, 1), + + MUX(none, "mout_sw_aclk333_g2d", sw_aclk333_g2d_p, SRC_TOP12, 8, 1), + MUX(none, "mout_sw_aclk266_g2d", sw_aclk266_g2d_p, SRC_TOP12, 12, 1), + MUX(none, "mout_sw_aclk_g3d", sw_aclk_g3d_p, SRC_TOP12, 16, 1), + MUX(none, "mout_sw_aclk300_jpeg", sw_aclk300_jpeg_p, SRC_TOP12, 20, 1), + MUX(none, "mout_sw_aclk300_disp1", sw_aclk300_disp1_p, + SRC_TOP12, 24, 1), + MUX(none, "mout_sw_aclk300_gscl", sw_aclk300_gscl_p, SRC_TOP12, 28, 1), + + /* DISP1 Block */ + MUX(none, "mout_fimd1", group3_p, SRC_DISP10, 4, 1), + MUX(none, "mout_mipi1", group2_p, SRC_DISP10, 16, 3), + MUX(none, "mout_dp1", group2_p, SRC_DISP10, 20, 3), + MUX(none, "mout_pixel", group2_p, SRC_DISP10, 24, 3), + MUX(mout_hdmi, "mout_hdmi", hdmi_p, SRC_DISP10, 28, 1), + + /* MAU Block */ + MUX(none, "mout_maudio0", maudio0_p, SRC_MAU, 28, 3), + + /* FSYS Block */ + MUX(none, "mout_usbd301", group2_p, SRC_FSYS, 4, 3), + MUX(none, "mout_mmc0", group2_p, SRC_FSYS, 8, 3), + MUX(none, "mout_mmc1", group2_p, SRC_FSYS, 12, 3), + MUX(none, "mout_mmc2", group2_p, SRC_FSYS, 16, 3), + MUX(none, "mout_usbd300", group2_p, SRC_FSYS, 20, 3), + MUX(none, "mout_unipro", group2_p, SRC_FSYS, 24, 3), + + /* PERIC Block */ + MUX(none, "mout_uart0", group2_p, SRC_PERIC0, 4, 3), + MUX(none, "mout_uart1", group2_p, SRC_PERIC0, 8, 3), + MUX(none, "mout_uart2", group2_p, SRC_PERIC0, 12, 3), + MUX(none, "mout_uart3", group2_p, SRC_PERIC0, 16, 3), + MUX(none, "mout_pwm", group2_p, SRC_PERIC0, 24, 3), + MUX(none, "mout_spdif", spdif_p, SRC_PERIC0, 28, 3), + MUX(none, "mout_audio0", audio0_p, SRC_PERIC1, 8, 3), + MUX(none, "mout_audio1", audio1_p, SRC_PERIC1, 12, 3), + MUX(none, "mout_audio2", audio2_p, SRC_PERIC1, 16, 3), + MUX(none, "mout_spi0", group2_p, SRC_PERIC1, 20, 3), + MUX(none, "mout_spi1", group2_p, SRC_PERIC1, 24, 3), + MUX(none, "mout_spi2", group2_p, SRC_PERIC1, 28, 3), +}; + +static struct samsung_div_clock exynos5420_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, "armclk2", "div_arm", DIV_CPU0, 28, 3), + DIV(none, "div_kfc", "mout_cpu_kfc", DIV_KFC0, 0, 3), + DIV(none, "sclk_kpll", "mout_kpll", DIV_KFC0, 24, 3), + + DIV(none, "dout_aclk400_mscl", "mout_aclk400_mscl", DIV_TOP0, 4, 3), + DIV(none, "dout_aclk200", "mout_aclk200", DIV_TOP0, 8, 3), + DIV(none, "dout_aclk200_fsys2", "mout_aclk200_fsys2", DIV_TOP0, 12, 3), + DIV(none, "dout_pclk200_fsys", "mout_pclk200_fsys", DIV_TOP0, 24, 3), + DIV(none, "dout_aclk200_fsys", "mout_aclk200_fsys", DIV_TOP0, 28, 3), + + DIV(none, "dout_aclk333_432_gscl", "mout_aclk333_432_gscl", + DIV_TOP1, 0, 3), + DIV(none, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6), + DIV(none, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3), + DIV(none, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3), + DIV(none, "dout_aclk333", "mout_aclk333", DIV_TOP1, 28, 3), + + DIV(none, "dout_aclk333_g2d", "mout_aclk333_g2d", DIV_TOP2, 8, 3), + DIV(none, "dout_aclk266_g2d", "mout_aclk266_g2d", DIV_TOP2, 12, 3), + DIV(none, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, 16, 3), + DIV(none, "dout_aclk300_jpeg", "mout_aclk300_jpeg", DIV_TOP2, 20, 3), + DIV_A(none, "dout_aclk300_disp1", "mout_aclk300_disp1", + DIV_TOP2, 24, 3, "aclk300_disp1"), + DIV(none, "dout_aclk300_gscl", "mout_aclk300_gscl", DIV_TOP2, 28, 3), + + /* DISP1 Block */ + DIV(none, "dout_fimd1", "mout_fimd1", DIV_DISP10, 0, 4), + DIV(none, "dout_mipi1", "mout_mipi1", DIV_DISP10, 16, 8), + DIV(none, "dout_dp1", "mout_dp1", DIV_DISP10, 24, 4), + DIV(dout_pixel, "dout_hdmi_pixel", "mout_pixel", DIV_DISP10, 28, 4), + + /* Audio Block */ + DIV(none, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4), + DIV(none, "dout_maupcm0", "dout_maudio0", DIV_MAU, 24, 8), + + /* USB3.0 */ + DIV(none, "dout_usbphy301", "mout_usbd301", DIV_FSYS0, 12, 4), + DIV(none, "dout_usbphy300", "mout_usbd300", DIV_FSYS0, 16, 4), + DIV(none, "dout_usbd301", "mout_usbd301", DIV_FSYS0, 20, 4), + DIV(none, "dout_usbd300", "mout_usbd300", DIV_FSYS0, 24, 4), + + /* MMC */ + DIV(none, "dout_mmc0", "mout_mmc0", DIV_FSYS1, 0, 10), + DIV(none, "dout_mmc1", "mout_mmc1", DIV_FSYS1, 10, 10), + DIV(none, "dout_mmc2", "mout_mmc2", DIV_FSYS1, 20, 10), + + DIV(none, "dout_unipro", "mout_unipro", DIV_FSYS2, 24, 8), + + /* UART and PWM */ + DIV(none, "dout_uart0", "mout_uart0", DIV_PERIC0, 8, 4), + DIV(none, "dout_uart1", "mout_uart1", DIV_PERIC0, 12, 4), + DIV(none, "dout_uart2", "mout_uart2", DIV_PERIC0, 16, 4), + DIV(none, "dout_uart3", "mout_uart3", DIV_PERIC0, 20, 4), + DIV(none, "dout_pwm", "mout_pwm", DIV_PERIC0, 28, 4), + + /* SPI */ + DIV(none, "dout_spi0", "mout_spi0", DIV_PERIC1, 20, 4), + DIV(none, "dout_spi1", "mout_spi1", DIV_PERIC1, 24, 4), + DIV(none, "dout_spi2", "mout_spi2", DIV_PERIC1, 28, 4), + + /* PCM */ + DIV(none, "dout_pcm1", "dout_audio1", DIV_PERIC2, 16, 8), + DIV(none, "dout_pcm2", "dout_audio2", DIV_PERIC2, 24, 8), + + /* Audio - I2S */ + DIV(none, "dout_i2s1", "dout_audio1", DIV_PERIC3, 6, 6), + DIV(none, "dout_i2s2", "dout_audio2", DIV_PERIC3, 12, 6), + DIV(none, "dout_audio0", "mout_audio0", DIV_PERIC3, 20, 4), + DIV(none, "dout_audio1", "mout_audio1", DIV_PERIC3, 24, 4), + DIV(none, "dout_audio2", "mout_audio2", DIV_PERIC3, 28, 4), + + /* SPI Pre-Ratio */ + DIV(none, "dout_pre_spi0", "dout_spi0", DIV_PERIC4, 8, 8), + DIV(none, "dout_pre_spi1", "dout_spi1", DIV_PERIC4, 16, 8), + DIV(none, "dout_pre_spi2", "dout_spi2", DIV_PERIC4, 24, 8), +}; + +static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { + /* TODO: Re-verify the CG bits for all the gate clocks */ + GATE_A(mct, "pclk_st", "aclk66_psgen", GATE_BUS_PERIS1, 2, 0, 0, "mct"), + + GATE(0, "aclk200_fsys", "mout_user_aclk200_fsys", + GATE_BUS_FSYS0, 9, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk200_fsys2", "mout_user_aclk200_fsys2", + GATE_BUS_FSYS0, 10, CLK_IGNORE_UNUSED, 0), + + GATE(0, "aclk333_g2d", "mout_user_aclk333_g2d", + GATE_BUS_TOP, 0, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk266_g2d", "mout_user_aclk266_g2d", + GATE_BUS_TOP, 1, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk300_jpeg", "mout_user_aclk300_jpeg", + GATE_BUS_TOP, 4, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk300_gscl", "mout_user_aclk300_gscl", + GATE_BUS_TOP, 6, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk333_432_gscl", "mout_user_aclk333_432_gscl", + GATE_BUS_TOP, 7, CLK_IGNORE_UNUSED, 0), + GATE(0, "pclk66_gpio", "mout_sw_aclk66", + GATE_BUS_TOP, 9, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk66_psgen", "mout_aclk66_psgen", + GATE_BUS_TOP, 10, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk66_peric", "mout_aclk66_peric", + GATE_BUS_TOP, 11, 0, 0), + GATE(0, "aclk166", "mout_user_aclk166", + GATE_BUS_TOP, 14, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk333", "mout_aclk333", + GATE_BUS_TOP, 15, CLK_IGNORE_UNUSED, 0), + + /* sclk */ + GATE(sclk_uart0, "sclk_uart0", "dout_uart0", + GATE_TOP_SCLK_PERIC, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart1, "sclk_uart1", "dout_uart1", + GATE_TOP_SCLK_PERIC, 1, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart2, "sclk_uart2", "dout_uart2", + GATE_TOP_SCLK_PERIC, 2, CLK_SET_RATE_PARENT, 0), + GATE(sclk_uart3, "sclk_uart3", "dout_uart3", + GATE_TOP_SCLK_PERIC, 3, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi0, "sclk_spi0", "dout_pre_spi0", + GATE_TOP_SCLK_PERIC, 6, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi1, "sclk_spi1", "dout_pre_spi1", + GATE_TOP_SCLK_PERIC, 7, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spi2, "sclk_spi2", "dout_pre_spi2", + GATE_TOP_SCLK_PERIC, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_spdif, "sclk_spdif", "mout_spdif", + GATE_TOP_SCLK_PERIC, 9, CLK_SET_RATE_PARENT, 0), + GATE(sclk_pwm, "sclk_pwm", "dout_pwm", + GATE_TOP_SCLK_PERIC, 11, CLK_SET_RATE_PARENT, 0), + GATE(sclk_pcm1, "sclk_pcm1", "dout_pcm1", + GATE_TOP_SCLK_PERIC, 15, CLK_SET_RATE_PARENT, 0), + GATE(sclk_pcm2, "sclk_pcm2", "dout_pcm2", + GATE_TOP_SCLK_PERIC, 16, CLK_SET_RATE_PARENT, 0), + GATE(sclk_i2s1, "sclk_i2s1", "dout_i2s1", + GATE_TOP_SCLK_PERIC, 17, CLK_SET_RATE_PARENT, 0), + GATE(sclk_i2s2, "sclk_i2s2", "dout_i2s2", + GATE_TOP_SCLK_PERIC, 18, CLK_SET_RATE_PARENT, 0), + + GATE(sclk_mmc0, "sclk_mmc0", "dout_mmc0", + GATE_TOP_SCLK_FSYS, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc1, "sclk_mmc1", "dout_mmc1", + GATE_TOP_SCLK_FSYS, 1, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mmc2, "sclk_mmc2", "dout_mmc2", + GATE_TOP_SCLK_FSYS, 2, CLK_SET_RATE_PARENT, 0), + GATE(sclk_usbphy301, "sclk_usbphy301", "dout_usbphy301", + GATE_TOP_SCLK_FSYS, 7, CLK_SET_RATE_PARENT, 0), + GATE(sclk_usbphy300, "sclk_usbphy300", "dout_usbphy300", + GATE_TOP_SCLK_FSYS, 8, CLK_SET_RATE_PARENT, 0), + GATE(sclk_usbd300, "sclk_usbd300", "dout_usbd300", + GATE_TOP_SCLK_FSYS, 9, CLK_SET_RATE_PARENT, 0), + GATE(sclk_usbd301, "sclk_usbd301", "dout_usbd301", + GATE_TOP_SCLK_FSYS, 10, CLK_SET_RATE_PARENT, 0), + + GATE(sclk_usbd301, "sclk_unipro", "dout_unipro", + SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), + + GATE(sclk_gscl_wa, "sclk_gscl_wa", "aclK333_432_gscl", + GATE_TOP_SCLK_GSCL, 6, CLK_SET_RATE_PARENT, 0), + GATE(sclk_gscl_wb, "sclk_gscl_wb", "aclk333_432_gscl", + GATE_TOP_SCLK_GSCL, 7, CLK_SET_RATE_PARENT, 0), + + /* Display */ + GATE(sclk_fimd1, "sclk_fimd1", "dout_fimd1", + GATE_TOP_SCLK_DISP1, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_mipi1, "sclk_mipi1", "dout_mipi1", + GATE_TOP_SCLK_DISP1, 3, CLK_SET_RATE_PARENT, 0), + GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi", + GATE_TOP_SCLK_DISP1, 9, CLK_SET_RATE_PARENT, 0), + GATE(sclk_pixel, "sclk_pixel", "dout_hdmi_pixel", + GATE_TOP_SCLK_DISP1, 10, CLK_SET_RATE_PARENT, 0), + GATE(sclk_dp1, "sclk_dp1", "dout_dp1", + GATE_TOP_SCLK_DISP1, 20, CLK_SET_RATE_PARENT, 0), + + /* Maudio Block */ + GATE(sclk_maudio0, "sclk_maudio0", "dout_maudio0", + GATE_TOP_SCLK_MAU, 0, CLK_SET_RATE_PARENT, 0), + GATE(sclk_maupcm0, "sclk_maupcm0", "dout_maupcm0", + GATE_TOP_SCLK_MAU, 1, CLK_SET_RATE_PARENT, 0), + /* FSYS */ + GATE(tsi, "tsi", "aclk200_fsys", GATE_BUS_FSYS0, 0, 0, 0), + GATE(pdma0, "pdma0", "aclk200_fsys", GATE_BUS_FSYS0, 1, 0, 0), + GATE(pdma1, "pdma1", "aclk200_fsys", GATE_BUS_FSYS0, 2, 0, 0), + GATE(ufs, "ufs", "aclk200_fsys2", GATE_BUS_FSYS0, 3, 0, 0), + GATE(rtic, "rtic", "aclk200_fsys", GATE_BUS_FSYS0, 5, 0, 0), + GATE(mmc0, "mmc0", "aclk200_fsys2", GATE_BUS_FSYS0, 12, 0, 0), + GATE(mmc1, "mmc1", "aclk200_fsys2", GATE_BUS_FSYS0, 13, 0, 0), + GATE(mmc2, "mmc2", "aclk200_fsys2", GATE_BUS_FSYS0, 14, 0, 0), + GATE(sromc, "sromc", "aclk200_fsys2", + GATE_BUS_FSYS0, 19, CLK_IGNORE_UNUSED, 0), + GATE(usbh20, "usbh20", "aclk200_fsys", GATE_BUS_FSYS0, 20, 0, 0), + GATE(usbd300, "usbd300", "aclk200_fsys", GATE_BUS_FSYS0, 21, 0, 0), + GATE(usbd301, "usbd301", "aclk200_fsys", GATE_BUS_FSYS0, 28, 0, 0), + + /* UART */ + GATE(uart0, "uart0", "aclk66_peric", GATE_BUS_PERIC, 4, 0, 0), + GATE(uart1, "uart1", "aclk66_peric", GATE_BUS_PERIC, 5, 0, 0), + GATE_A(uart2, "uart2", "aclk66_peric", + GATE_BUS_PERIC, 6, CLK_IGNORE_UNUSED, 0, "uart2"), + GATE(uart3, "uart3", "aclk66_peric", GATE_BUS_PERIC, 7, 0, 0), + /* I2C */ + GATE(i2c0, "i2c0", "aclk66_peric", GATE_BUS_PERIC, 9, 0, 0), + GATE(i2c1, "i2c1", "aclk66_peric", GATE_BUS_PERIC, 10, 0, 0), + GATE(i2c2, "i2c2", "aclk66_peric", GATE_BUS_PERIC, 11, 0, 0), + GATE(i2c3, "i2c3", "aclk66_peric", GATE_BUS_PERIC, 12, 0, 0), + GATE(i2c4, "i2c4", "aclk66_peric", GATE_BUS_PERIC, 13, 0, 0), + GATE(i2c5, "i2c5", "aclk66_peric", GATE_BUS_PERIC, 14, 0, 0), + GATE(i2c6, "i2c6", "aclk66_peric", GATE_BUS_PERIC, 15, 0, 0), + GATE(i2c7, "i2c7", "aclk66_peric", GATE_BUS_PERIC, 16, 0, 0), + GATE(i2c_hdmi, "i2c_hdmi", "aclk66_peric", GATE_BUS_PERIC, 17, 0, 0), + GATE(tsadc, "tsadc", "aclk66_peric", GATE_BUS_PERIC, 18, 0, 0), + /* SPI */ + GATE(spi0, "spi0", "aclk66_peric", GATE_BUS_PERIC, 19, 0, 0), + GATE(spi1, "spi1", "aclk66_peric", GATE_BUS_PERIC, 20, 0, 0), + GATE(spi2, "spi2", "aclk66_peric", GATE_BUS_PERIC, 21, 0, 0), + GATE(keyif, "keyif", "aclk66_peric", GATE_BUS_PERIC, 22, 0, 0), + /* I2S */ + GATE(i2s1, "i2s1", "aclk66_peric", GATE_BUS_PERIC, 23, 0, 0), + GATE(i2s2, "i2s2", "aclk66_peric", GATE_BUS_PERIC, 24, 0, 0), + /* PCM */ + GATE(pcm1, "pcm1", "aclk66_peric", GATE_BUS_PERIC, 25, 0, 0), + GATE(pcm2, "pcm2", "aclk66_peric", GATE_BUS_PERIC, 26, 0, 0), + /* PWM */ + GATE(pwm, "pwm", "aclk66_peric", GATE_BUS_PERIC, 27, 0, 0), + /* SPDIF */ + GATE(spdif, "spdif", "aclk66_peric", GATE_BUS_PERIC, 29, 0, 0), + + GATE(i2c8, "i2c8", "aclk66_peric", GATE_BUS_PERIC1, 0, 0, 0), + GATE(i2c9, "i2c9", "aclk66_peric", GATE_BUS_PERIC1, 1, 0, 0), + GATE(i2c10, "i2c10", "aclk66_peric", GATE_BUS_PERIC1, 2, 0, 0), + + GATE(chipid, "chipid", "aclk66_psgen", + GATE_BUS_PERIS0, 12, CLK_IGNORE_UNUSED, 0), + GATE(sysreg, "sysreg", "aclk66_psgen", + GATE_BUS_PERIS0, 13, CLK_IGNORE_UNUSED, 0), + GATE(tzpc0, "tzpc0", "aclk66_psgen", GATE_BUS_PERIS0, 18, 0, 0), + GATE(tzpc1, "tzpc1", "aclk66_psgen", GATE_BUS_PERIS0, 19, 0, 0), + GATE(tzpc2, "tzpc2", "aclk66_psgen", GATE_BUS_PERIS0, 20, 0, 0), + GATE(tzpc3, "tzpc3", "aclk66_psgen", GATE_BUS_PERIS0, 21, 0, 0), + GATE(tzpc4, "tzpc4", "aclk66_psgen", GATE_BUS_PERIS0, 22, 0, 0), + GATE(tzpc5, "tzpc5", "aclk66_psgen", GATE_BUS_PERIS0, 23, 0, 0), + GATE(tzpc6, "tzpc6", "aclk66_psgen", GATE_BUS_PERIS0, 24, 0, 0), + GATE(tzpc7, "tzpc7", "aclk66_psgen", GATE_BUS_PERIS0, 25, 0, 0), + GATE(tzpc8, "tzpc8", "aclk66_psgen", GATE_BUS_PERIS0, 26, 0, 0), + GATE(tzpc9, "tzpc9", "aclk66_psgen", GATE_BUS_PERIS0, 27, 0, 0), + + GATE(hdmi_cec, "hdmi_cec", "aclk66_psgen", GATE_BUS_PERIS1, 0, 0, 0), + GATE(seckey, "seckey", "aclk66_psgen", GATE_BUS_PERIS1, 1, 0, 0), + GATE(wdt, "wdt", "aclk66_psgen", GATE_BUS_PERIS1, 3, 0, 0), + GATE(rtc, "rtc", "aclk66_psgen", GATE_BUS_PERIS1, 4, 0, 0), + GATE(tmu, "tmu", "aclk66_psgen", GATE_BUS_PERIS1, 5, 0, 0), + GATE(tmu_gpu, "tmu_gpu", "aclk66_psgen", GATE_BUS_PERIS1, 6, 0, 0), + + GATE(gscl0, "gscl0", "aclk300_gscl", GATE_IP_GSCL0, 0, 0, 0), + GATE(gscl1, "gscl1", "aclk300_gscl", GATE_IP_GSCL0, 1, 0, 0), + GATE(clk_3aa, "clk_3aa", "aclk300_gscl", GATE_IP_GSCL0, 4, 0, 0), + + GATE(smmu_3aa, "smmu_3aa", "aclk333_432_gscl", GATE_IP_GSCL1, 2, 0, 0), + GATE(smmu_fimcl0, "smmu_fimcl0", "aclk333_432_gscl", + GATE_IP_GSCL1, 3, 0, 0), + GATE(smmu_fimcl1, "smmu_fimcl1", "aclk333_432_gscl", + GATE_IP_GSCL1, 4, 0, 0), + GATE(smmu_gscl0, "smmu_gscl0", "aclk300_gscl", GATE_IP_GSCL1, 6, 0, 0), + GATE(smmu_gscl1, "smmu_gscl1", "aclk300_gscl", GATE_IP_GSCL1, 7, 0, 0), + GATE(gscl_wa, "gscl_wa", "aclk300_gscl", GATE_IP_GSCL1, 12, 0, 0), + GATE(gscl_wb, "gscl_wb", "aclk300_gscl", GATE_IP_GSCL1, 13, 0, 0), + GATE(smmu_fimcl3, "smmu_fimcl3,", "aclk333_432_gscl", + GATE_IP_GSCL1, 16, 0, 0), + GATE(fimc_lite3, "fimc_lite3", "aclk333_432_gscl", + GATE_IP_GSCL1, 17, 0, 0), + + GATE(fimd1, "fimd1", "aclk300_disp1", GATE_IP_DISP1, 0, 0, 0), + GATE(dsim1, "dsim1", "aclk200_disp1", GATE_IP_DISP1, 3, 0, 0), + GATE(dp1, "dp1", "aclk200_disp1", GATE_IP_DISP1, 4, 0, 0), + GATE(mixer, "mixer", "aclk166", GATE_IP_DISP1, 5, 0, 0), + GATE(hdmi, "hdmi", "aclk200_disp1", GATE_IP_DISP1, 6, 0, 0), + GATE(smmu_fimd1, "smmu_fimd1", "aclk300_disp1", GATE_IP_DISP1, 8, 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(g3d, "g3d", "aclkg3d", GATE_IP_G3D, 9, 0, 0), + + GATE(rotator, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0), + GATE(jpeg, "jpeg", "aclk300_jpeg", GATE_IP_GEN, 2, 0, 0), + GATE(jpeg2, "jpeg2", "aclk300_jpeg", GATE_IP_GEN, 3, 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", "aclk300_jpeg", GATE_IP_GEN, 7, 0, 0), + GATE(smmu_mdma1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0), + + GATE(mscl0, "mscl0", "aclk400_mscl", GATE_IP_MSCL, 0, 0, 0), + GATE(mscl1, "mscl1", "aclk400_mscl", GATE_IP_MSCL, 1, 0, 0), + GATE(mscl2, "mscl2", "aclk400_mscl", GATE_IP_MSCL, 2, 0, 0), + GATE(smmu_mscl0, "smmu_mscl0", "aclk400_mscl", GATE_IP_MSCL, 8, 0, 0), + GATE(smmu_mscl1, "smmu_mscl1", "aclk400_mscl", GATE_IP_MSCL, 9, 0, 0), + GATE(smmu_mscl2, "smmu_mscl2", "aclk400_mscl", GATE_IP_MSCL, 10, 0, 0), + GATE(smmu_mixer, "smmu_mixer", "aclk200_disp1", GATE_IP_DISP1, 9, 0, 0), +}; + +static struct samsung_pll_clock exynos5420_plls[nr_plls] __initdata = { + [apll] = PLL(pll_2550, fout_apll, "fout_apll", "fin_pll", APLL_LOCK, + APLL_CON0, NULL), + [cpll] = PLL(pll_2550, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK, + MPLL_CON0, NULL), + [dpll] = PLL(pll_2550, fout_dpll, "fout_dpll", "fin_pll", DPLL_LOCK, + DPLL_CON0, NULL), + [epll] = PLL(pll_2650, fout_epll, "fout_epll", "fin_pll", EPLL_LOCK, + EPLL_CON0, NULL), + [rpll] = PLL(pll_2650, fout_rpll, "fout_rpll", "fin_pll", RPLL_LOCK, + RPLL_CON0, NULL), + [ipll] = PLL(pll_2550, fout_ipll, "fout_ipll", "fin_pll", IPLL_LOCK, + IPLL_CON0, NULL), + [spll] = PLL(pll_2550, fout_spll, "fout_spll", "fin_pll", SPLL_LOCK, + SPLL_CON0, NULL), + [vpll] = PLL(pll_2550, fout_vpll, "fout_vpll", "fin_pll", VPLL_LOCK, + VPLL_CON0, NULL), + [mpll] = PLL(pll_2550, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK, + MPLL_CON0, NULL), + [bpll] = PLL(pll_2550, fout_bpll, "fout_bpll", "fin_pll", BPLL_LOCK, + BPLL_CON0, NULL), + [kpll] = PLL(pll_2550, fout_kpll, "fout_kpll", "fin_pll", KPLL_LOCK, + KPLL_CON0, NULL), +}; + +static struct of_device_id ext_clk_match[] __initdata = { + { .compatible = "samsung,exynos5420-oscclk", .data = (void *)0, }, + { }, +}; + +/* register exynos5420 clocks */ +static void __init exynos5420_clk_init(struct device_node *np) +{ + void __iomem *reg_base; + + 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, + exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs), + NULL, 0); + samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks, + ARRAY_SIZE(exynos5420_fixed_rate_ext_clks), + ext_clk_match); + samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls), + reg_base); + samsung_clk_register_fixed_rate(exynos5420_fixed_rate_clks, + ARRAY_SIZE(exynos5420_fixed_rate_clks)); + samsung_clk_register_fixed_factor(exynos5420_fixed_factor_clks, + ARRAY_SIZE(exynos5420_fixed_factor_clks)); + samsung_clk_register_mux(exynos5420_mux_clks, + ARRAY_SIZE(exynos5420_mux_clks)); + samsung_clk_register_div(exynos5420_div_clks, + ARRAY_SIZE(exynos5420_div_clks)); + samsung_clk_register_gate(exynos5420_gate_clks, + ARRAY_SIZE(exynos5420_gate_clks)); +} +CLK_OF_DECLARE(exynos5420_clk, "samsung,exynos5420-clock", exynos5420_clk_init); diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c index 7d5434167a96..f8658945bfd2 100644 --- a/drivers/clk/samsung/clk-exynos5440.c +++ b/drivers/clk/samsung/clk-exynos5440.c @@ -41,12 +41,12 @@ 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 = { +static 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 = { +static 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), @@ -55,26 +55,26 @@ struct samsung_fixed_rate_clock exynos5440_fixed_rate_clks[] __initdata = { }; /* fixed factor clocks */ -struct samsung_fixed_factor_clock exynos5440_fixed_factor_clks[] __initdata = { +static 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 = { +static 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 = { +static 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 = { +static 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), @@ -97,13 +97,13 @@ struct samsung_gate_clock exynos5440_gate_clks[] __initdata = { GATE(cs250_o, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0), }; -static __initdata struct of_device_id ext_clk_match[] = { +static struct of_device_id ext_clk_match[] __initdata = { { .compatible = "samsung,clock-xtal", .data = (void *)0, }, {}, }; /* register exynos5440 clocks */ -void __init exynos5440_clk_init(struct device_node *np) +static void __init exynos5440_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -132,7 +132,7 @@ void __init exynos5440_clk_init(struct device_node *np) 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: arm_clk = %ldHz\n", _get_rate("arm_clk")); 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 index 89135f6be116..529e11dc2c6b 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -10,31 +10,73 @@ */ #include <linux/errno.h> +#include <linux/hrtimer.h> #include "clk.h" #include "clk-pll.h" +#define PLL_TIMEOUT_MS 10 + +struct samsung_clk_pll { + struct clk_hw hw; + void __iomem *lock_reg; + void __iomem *con_reg; + enum samsung_pll_type type; + unsigned int rate_count; + const struct samsung_pll_rate_table *rate_table; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct samsung_clk_pll, hw) + +static const struct samsung_pll_rate_table *samsung_get_pll_settings( + struct samsung_clk_pll *pll, unsigned long rate) +{ + const struct samsung_pll_rate_table *rate_table = pll->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) { + if (rate == rate_table[i].rate) + return &rate_table[i]; + } + + return NULL; +} + +static long samsung_pll_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate_table = pll->rate_table; + int i; + + /* Assumming rate_table is in descending order */ + for (i = 0; i < pll->rate_count; i++) { + if (drate >= rate_table[i].rate) + return rate_table[i].rate; + } + + /* return minimum supported value */ + return rate_table[i - 1].rate; +} + /* * PLL35xx Clock Type */ +/* Maximum lock time can be 270 * PDIV cycles */ +#define PLL35XX_LOCK_FACTOR (270) #define PLL35XX_MDIV_MASK (0x3FF) #define PLL35XX_PDIV_MASK (0x3F) #define PLL35XX_SDIV_MASK (0x7) +#define PLL35XX_LOCK_STAT_MASK (0x1) #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) +#define PLL35XX_LOCK_STAT_SHIFT (29) static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct samsung_clk_pll35xx *pll = to_clk_pll35xx(hw); + struct samsung_clk_pll *pll = to_clk_pll(hw); u32 mdiv, pdiv, sdiv, pll_con; u64 fvco = parent_rate; @@ -49,48 +91,80 @@ static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, 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) +static inline bool samsung_pll35xx_mp_change( + const struct samsung_pll_rate_table *rate, u32 pll_con) { - struct samsung_clk_pll35xx *pll; - struct clk *clk; - struct clk_init_data init; + u32 old_mdiv, old_pdiv; - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: could not allocate pll clk %s\n", __func__, name); - return NULL; + old_mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + old_pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; + + return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv); +} + +static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate; + u32 tmp; + + /* Get required rate settings from table */ + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; } - init.name = name; - init.ops = &samsung_pll35xx_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.parent_names = &pname; - init.num_parents = 1; + tmp = __raw_readl(pll->con_reg); - pll->hw.init = &init; - pll->con_reg = con_reg; + if (!(samsung_pll35xx_mp_change(rate, tmp))) { + /* If only s change, change just s value only*/ + tmp &= ~(PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT); + tmp |= rate->sdiv << PLL35XX_SDIV_SHIFT; + __raw_writel(tmp, pll->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); + return 0; } - if (clk_register_clkdev(clk, name, NULL)) - pr_err("%s: failed to register lookup for %s", __func__, name); - - return clk; + /* Set PLL lock time. */ + __raw_writel(rate->pdiv * PLL35XX_LOCK_FACTOR, + pll->lock_reg); + + /* Change PLL PMS values */ + tmp &= ~((PLL35XX_MDIV_MASK << PLL35XX_MDIV_SHIFT) | + (PLL35XX_PDIV_MASK << PLL35XX_PDIV_SHIFT) | + (PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT)); + tmp |= (rate->mdiv << PLL35XX_MDIV_SHIFT) | + (rate->pdiv << PLL35XX_PDIV_SHIFT) | + (rate->sdiv << PLL35XX_SDIV_SHIFT); + __raw_writel(tmp, pll->con_reg); + + /* wait_lock_time */ + do { + cpu_relax(); + tmp = __raw_readl(pll->con_reg); + } while (!(tmp & (PLL35XX_LOCK_STAT_MASK + << PLL35XX_LOCK_STAT_SHIFT))); + return 0; } +static const struct clk_ops samsung_pll35xx_clk_ops = { + .recalc_rate = samsung_pll35xx_recalc_rate, + .round_rate = samsung_pll_round_rate, + .set_rate = samsung_pll35xx_set_rate, +}; + +static const struct clk_ops samsung_pll35xx_clk_min_ops = { + .recalc_rate = samsung_pll35xx_recalc_rate, +}; + /* * PLL36xx Clock Type */ +/* Maximum lock time can be 3000 * PDIV cycles */ +#define PLL36XX_LOCK_FACTOR (3000) #define PLL36XX_KDIV_MASK (0xFFFF) #define PLL36XX_MDIV_MASK (0x1FF) @@ -99,19 +173,15 @@ struct clk * __init samsung_clk_register_pll35xx(const char *name, #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) +#define PLL36XX_KDIV_SHIFT (0) +#define PLL36XX_LOCK_STAT_SHIFT (29) 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; + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 mdiv, pdiv, sdiv, pll_con0, pll_con1; + s16 kdiv; u64 fvco = parent_rate; pll_con0 = __raw_readl(pll->con_reg); @@ -119,7 +189,7 @@ static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, 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; + kdiv = (s16)(pll_con1 & PLL36XX_KDIV_MASK); fvco *= (mdiv << 16) + kdiv; do_div(fvco, (pdiv << sdiv)); @@ -128,68 +198,102 @@ static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, 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) +static inline bool samsung_pll36xx_mpk_change( + const struct samsung_pll_rate_table *rate, u32 pll_con0, u32 pll_con1) { - struct samsung_clk_pll36xx *pll; - struct clk *clk; - struct clk_init_data init; + u32 old_mdiv, old_pdiv, old_kdiv; - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: could not allocate pll clk %s\n", __func__, name); - return NULL; + old_mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK; + old_pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK; + old_kdiv = (pll_con1 >> PLL36XX_KDIV_SHIFT) & PLL36XX_KDIV_MASK; + + return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || + rate->kdiv != old_kdiv); +} + +static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 tmp, pll_con0, pll_con1; + const struct samsung_pll_rate_table *rate; + + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; } - init.name = name; - init.ops = &samsung_pll36xx_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.parent_names = &pname; - init.num_parents = 1; + pll_con0 = __raw_readl(pll->con_reg); + pll_con1 = __raw_readl(pll->con_reg + 4); - pll->hw.init = &init; - pll->con_reg = con_reg; + if (!(samsung_pll36xx_mpk_change(rate, pll_con0, pll_con1))) { + /* If only s change, change just s value only*/ + pll_con0 &= ~(PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT); + pll_con0 |= (rate->sdiv << PLL36XX_SDIV_SHIFT); + __raw_writel(pll_con0, pll->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); + return 0; } - if (clk_register_clkdev(clk, name, NULL)) - pr_err("%s: failed to register lookup for %s", __func__, name); - - return clk; + /* Set PLL lock time. */ + __raw_writel(rate->pdiv * PLL36XX_LOCK_FACTOR, pll->lock_reg); + + /* Change PLL PMS values */ + pll_con0 &= ~((PLL36XX_MDIV_MASK << PLL36XX_MDIV_SHIFT) | + (PLL36XX_PDIV_MASK << PLL36XX_PDIV_SHIFT) | + (PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT)); + pll_con0 |= (rate->mdiv << PLL36XX_MDIV_SHIFT) | + (rate->pdiv << PLL36XX_PDIV_SHIFT) | + (rate->sdiv << PLL36XX_SDIV_SHIFT); + __raw_writel(pll_con0, pll->con_reg); + + pll_con1 &= ~(PLL36XX_KDIV_MASK << PLL36XX_KDIV_SHIFT); + pll_con1 |= rate->kdiv << PLL36XX_KDIV_SHIFT; + __raw_writel(pll_con1, pll->con_reg + 4); + + /* wait_lock_time */ + do { + cpu_relax(); + tmp = __raw_readl(pll->con_reg); + } while (!(tmp & (1 << PLL36XX_LOCK_STAT_SHIFT))); + + return 0; } +static const struct clk_ops samsung_pll36xx_clk_ops = { + .recalc_rate = samsung_pll36xx_recalc_rate, + .set_rate = samsung_pll36xx_set_rate, + .round_rate = samsung_pll_round_rate, +}; + +static const struct clk_ops samsung_pll36xx_clk_min_ops = { + .recalc_rate = samsung_pll36xx_recalc_rate, +}; + /* * PLL45xx Clock Type */ +#define PLL4502_LOCK_FACTOR 400 +#define PLL4508_LOCK_FACTOR 240 #define PLL45XX_MDIV_MASK (0x3FF) #define PLL45XX_PDIV_MASK (0x3F) #define PLL45XX_SDIV_MASK (0x7) +#define PLL45XX_AFC_MASK (0x1F) #define PLL45XX_MDIV_SHIFT (16) #define PLL45XX_PDIV_SHIFT (8) #define PLL45XX_SDIV_SHIFT (0) +#define PLL45XX_AFC_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) +#define PLL45XX_ENABLE BIT(31) +#define PLL45XX_LOCKED BIT(29) static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw); + struct samsung_clk_pll *pll = to_clk_pll(hw); u32 mdiv, pdiv, sdiv, pll_con; u64 fvco = parent_rate; @@ -207,54 +311,113 @@ static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, 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) +static bool samsung_pll45xx_mp_change(u32 pll_con0, u32 pll_con1, + const struct samsung_pll_rate_table *rate) { - struct samsung_clk_pll45xx *pll; - struct clk *clk; - struct clk_init_data init; + u32 old_mdiv, old_pdiv, old_afc; - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: could not allocate pll clk %s\n", __func__, name); - return NULL; + old_mdiv = (pll_con0 >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK; + old_pdiv = (pll_con0 >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK; + old_afc = (pll_con1 >> PLL45XX_AFC_SHIFT) & PLL45XX_AFC_MASK; + + return (old_mdiv != rate->mdiv || old_pdiv != rate->pdiv + || old_afc != rate->afc); +} + +static int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate; + u32 con0, con1; + ktime_t start; + + /* Get required rate settings from table */ + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; } - init.name = name; - init.ops = &samsung_pll45xx_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.parent_names = &pname; - init.num_parents = 1; + con0 = __raw_readl(pll->con_reg); + con1 = __raw_readl(pll->con_reg + 0x4); - pll->hw.init = &init; - pll->con_reg = con_reg; - pll->type = type; + if (!(samsung_pll45xx_mp_change(con0, con1, rate))) { + /* If only s change, change just s value only*/ + con0 &= ~(PLL45XX_SDIV_MASK << PLL45XX_SDIV_SHIFT); + con0 |= rate->sdiv << PLL45XX_SDIV_SHIFT; + __raw_writel(con0, pll->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); + return 0; } - if (clk_register_clkdev(clk, name, NULL)) - pr_err("%s: failed to register lookup for %s", __func__, name); + /* Set PLL PMS values. */ + con0 &= ~((PLL45XX_MDIV_MASK << PLL45XX_MDIV_SHIFT) | + (PLL45XX_PDIV_MASK << PLL45XX_PDIV_SHIFT) | + (PLL45XX_SDIV_MASK << PLL45XX_SDIV_SHIFT)); + con0 |= (rate->mdiv << PLL45XX_MDIV_SHIFT) | + (rate->pdiv << PLL45XX_PDIV_SHIFT) | + (rate->sdiv << PLL45XX_SDIV_SHIFT); + + /* Set PLL AFC value. */ + con1 = __raw_readl(pll->con_reg + 0x4); + con1 &= ~(PLL45XX_AFC_MASK << PLL45XX_AFC_SHIFT); + con1 |= (rate->afc << PLL45XX_AFC_SHIFT); + + /* Set PLL lock time. */ + switch (pll->type) { + case pll_4502: + __raw_writel(rate->pdiv * PLL4502_LOCK_FACTOR, pll->lock_reg); + break; + case pll_4508: + __raw_writel(rate->pdiv * PLL4508_LOCK_FACTOR, pll->lock_reg); + break; + default: + break; + }; + + /* Set new configuration. */ + __raw_writel(con1, pll->con_reg + 0x4); + __raw_writel(con0, pll->con_reg); + + /* Wait for locking. */ + start = ktime_get(); + while (!(__raw_readl(pll->con_reg) & PLL45XX_LOCKED)) { + ktime_t delta = ktime_sub(ktime_get(), start); + + if (ktime_to_ms(delta) > PLL_TIMEOUT_MS) { + pr_err("%s: could not lock PLL %s\n", + __func__, __clk_get_name(hw->clk)); + return -EFAULT; + } + + cpu_relax(); + } - return clk; + return 0; } +static const struct clk_ops samsung_pll45xx_clk_ops = { + .recalc_rate = samsung_pll45xx_recalc_rate, + .round_rate = samsung_pll_round_rate, + .set_rate = samsung_pll45xx_set_rate, +}; + +static const struct clk_ops samsung_pll45xx_clk_min_ops = { + .recalc_rate = samsung_pll45xx_recalc_rate, +}; + /* * PLL46xx Clock Type */ +#define PLL46XX_LOCK_FACTOR 3000 +#define PLL46XX_VSEL_MASK (1) #define PLL46XX_MDIV_MASK (0x1FF) #define PLL46XX_PDIV_MASK (0x3F) #define PLL46XX_SDIV_MASK (0x7) +#define PLL46XX_VSEL_SHIFT (27) #define PLL46XX_MDIV_SHIFT (16) #define PLL46XX_PDIV_SHIFT (8) #define PLL46XX_SDIV_SHIFT (0) @@ -262,19 +425,20 @@ struct clk * __init samsung_clk_register_pll45xx(const char *name, #define PLL46XX_KDIV_MASK (0xFFFF) #define PLL4650C_KDIV_MASK (0xFFF) #define PLL46XX_KDIV_SHIFT (0) +#define PLL46XX_MFR_MASK (0x3F) +#define PLL46XX_MRR_MASK (0x1F) +#define PLL46XX_KDIV_SHIFT (0) +#define PLL46XX_MFR_SHIFT (16) +#define PLL46XX_MRR_SHIFT (24) -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) +#define PLL46XX_ENABLE BIT(31) +#define PLL46XX_LOCKED BIT(29) +#define PLL46XX_VSEL BIT(27) static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw); + struct samsung_clk_pll *pll = to_clk_pll(hw); u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift; u64 fvco = parent_rate; @@ -294,47 +458,175 @@ static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } +static bool samsung_pll46xx_mpk_change(u32 pll_con0, u32 pll_con1, + const struct samsung_pll_rate_table *rate) +{ + u32 old_mdiv, old_pdiv, old_kdiv; + + old_mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK; + old_pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK; + old_kdiv = (pll_con1 >> PLL46XX_KDIV_SHIFT) & PLL46XX_KDIV_MASK; + + return (old_mdiv != rate->mdiv || old_pdiv != rate->pdiv + || old_kdiv != rate->kdiv); +} + +static int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate; + u32 con0, con1, lock; + ktime_t start; + + /* Get required rate settings from table */ + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; + } + + con0 = __raw_readl(pll->con_reg); + con1 = __raw_readl(pll->con_reg + 0x4); + + if (!(samsung_pll46xx_mpk_change(con0, con1, rate))) { + /* If only s change, change just s value only*/ + con0 &= ~(PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT); + con0 |= rate->sdiv << PLL46XX_SDIV_SHIFT; + __raw_writel(con0, pll->con_reg); + + return 0; + } + + /* Set PLL lock time. */ + lock = rate->pdiv * PLL46XX_LOCK_FACTOR; + if (lock > 0xffff) + /* Maximum lock time bitfield is 16-bit. */ + lock = 0xffff; + + /* Set PLL PMS and VSEL values. */ + con0 &= ~((PLL46XX_MDIV_MASK << PLL46XX_MDIV_SHIFT) | + (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT) | + (PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT) | + (PLL46XX_VSEL_MASK << PLL46XX_VSEL_SHIFT)); + con0 |= (rate->mdiv << PLL46XX_MDIV_SHIFT) | + (rate->pdiv << PLL46XX_PDIV_SHIFT) | + (rate->sdiv << PLL46XX_SDIV_SHIFT) | + (rate->vsel << PLL46XX_VSEL_SHIFT); + + /* Set PLL K, MFR and MRR values. */ + con1 = __raw_readl(pll->con_reg + 0x4); + con1 &= ~((PLL46XX_KDIV_MASK << PLL46XX_KDIV_SHIFT) | + (PLL46XX_MFR_MASK << PLL46XX_MFR_SHIFT) | + (PLL46XX_MRR_MASK << PLL46XX_MRR_SHIFT)); + con1 |= (rate->kdiv << PLL46XX_KDIV_SHIFT) | + (rate->mfr << PLL46XX_MFR_SHIFT) | + (rate->mrr << PLL46XX_MRR_SHIFT); + + /* Write configuration to PLL */ + __raw_writel(lock, pll->lock_reg); + __raw_writel(con0, pll->con_reg); + __raw_writel(con1, pll->con_reg + 0x4); + + /* Wait for locking. */ + start = ktime_get(); + while (!(__raw_readl(pll->con_reg) & PLL46XX_LOCKED)) { + ktime_t delta = ktime_sub(ktime_get(), start); + + if (ktime_to_ms(delta) > PLL_TIMEOUT_MS) { + pr_err("%s: could not lock PLL %s\n", + __func__, __clk_get_name(hw->clk)); + return -EFAULT; + } + + cpu_relax(); + } + + return 0; +} + static const struct clk_ops samsung_pll46xx_clk_ops = { .recalc_rate = samsung_pll46xx_recalc_rate, + .round_rate = samsung_pll_round_rate, + .set_rate = samsung_pll46xx_set_rate, +}; + +static const struct clk_ops samsung_pll46xx_clk_min_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) +/* + * PLL6552 Clock Type + */ + +#define PLL6552_MDIV_MASK 0x3ff +#define PLL6552_PDIV_MASK 0x3f +#define PLL6552_SDIV_MASK 0x7 +#define PLL6552_MDIV_SHIFT 16 +#define PLL6552_PDIV_SHIFT 8 +#define PLL6552_SDIV_SHIFT 0 + +static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) { - struct samsung_clk_pll46xx *pll; - struct clk *clk; - struct clk_init_data init; + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 mdiv, pdiv, sdiv, pll_con; + u64 fvco = parent_rate; - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: could not allocate pll clk %s\n", __func__, name); - return NULL; - } + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK; + pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK; + sdiv = (pll_con >> PLL6552_SDIV_SHIFT) & PLL6552_SDIV_MASK; - init.name = name; - init.ops = &samsung_pll46xx_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.parent_names = &pname; - init.num_parents = 1; + fvco *= mdiv; + do_div(fvco, (pdiv << sdiv)); - pll->hw.init = &init; - pll->con_reg = con_reg; - pll->type = type; + return (unsigned long)fvco; +} - clk = clk_register(NULL, &pll->hw); - if (IS_ERR(clk)) { - pr_err("%s: failed to register pll clock %s\n", __func__, - name); - kfree(pll); - } +static const struct clk_ops samsung_pll6552_clk_ops = { + .recalc_rate = samsung_pll6552_recalc_rate, +}; - if (clk_register_clkdev(clk, name, NULL)) - pr_err("%s: failed to register lookup for %s", __func__, name); +/* + * PLL6553 Clock Type + */ - return clk; +#define PLL6553_MDIV_MASK 0xff +#define PLL6553_PDIV_MASK 0x3f +#define PLL6553_SDIV_MASK 0x7 +#define PLL6553_KDIV_MASK 0xffff +#define PLL6553_MDIV_SHIFT 16 +#define PLL6553_PDIV_SHIFT 8 +#define PLL6553_SDIV_SHIFT 0 +#define PLL6553_KDIV_SHIFT 0 + +static unsigned long samsung_pll6553_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(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 + 0x4); + mdiv = (pll_con0 >> PLL6553_MDIV_SHIFT) & PLL6553_MDIV_MASK; + pdiv = (pll_con0 >> PLL6553_PDIV_SHIFT) & PLL6553_PDIV_MASK; + sdiv = (pll_con0 >> PLL6553_SDIV_SHIFT) & PLL6553_SDIV_MASK; + kdiv = (pll_con1 >> PLL6553_KDIV_SHIFT) & PLL6553_KDIV_MASK; + + fvco *= (mdiv << 16) + kdiv; + do_div(fvco, (pdiv << sdiv)); + fvco >>= 16; + + return (unsigned long)fvco; } +static const struct clk_ops samsung_pll6553_clk_ops = { + .recalc_rate = samsung_pll6553_recalc_rate, +}; + /* * PLL2550x Clock Type */ @@ -417,3 +709,117 @@ struct clk * __init samsung_clk_register_pll2550x(const char *name, return clk; } + +static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, + void __iomem *base) +{ + struct samsung_clk_pll *pll; + struct clk *clk; + struct clk_init_data init; + int ret, len; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", + __func__, pll_clk->name); + return; + } + + init.name = pll_clk->name; + init.flags = pll_clk->flags; + init.parent_names = &pll_clk->parent_name; + init.num_parents = 1; + + if (pll_clk->rate_table) { + /* find count of rates in rate_table */ + for (len = 0; pll_clk->rate_table[len].rate != 0; ) + len++; + + pll->rate_count = len; + pll->rate_table = kmemdup(pll_clk->rate_table, + pll->rate_count * + sizeof(struct samsung_pll_rate_table), + GFP_KERNEL); + WARN(!pll->rate_table, + "%s: could not allocate rate table for %s\n", + __func__, pll_clk->name); + } + + switch (pll_clk->type) { + /* clk_ops for 35xx and 2550 are similar */ + case pll_35xx: + case pll_2550: + if (!pll->rate_table) + init.ops = &samsung_pll35xx_clk_min_ops; + else + init.ops = &samsung_pll35xx_clk_ops; + break; + case pll_4500: + init.ops = &samsung_pll45xx_clk_min_ops; + break; + case pll_4502: + case pll_4508: + if (!pll->rate_table) + init.ops = &samsung_pll45xx_clk_min_ops; + else + init.ops = &samsung_pll45xx_clk_ops; + break; + /* clk_ops for 36xx and 2650 are similar */ + case pll_36xx: + case pll_2650: + if (!pll->rate_table) + init.ops = &samsung_pll36xx_clk_min_ops; + else + init.ops = &samsung_pll36xx_clk_ops; + break; + case pll_6552: + init.ops = &samsung_pll6552_clk_ops; + break; + case pll_6553: + init.ops = &samsung_pll6553_clk_ops; + break; + case pll_4600: + case pll_4650: + case pll_4650c: + if (!pll->rate_table) + init.ops = &samsung_pll46xx_clk_min_ops; + else + init.ops = &samsung_pll46xx_clk_ops; + break; + default: + pr_warn("%s: Unknown pll type for pll clk %s\n", + __func__, pll_clk->name); + } + + pll->hw.init = &init; + pll->type = pll_clk->type; + pll->lock_reg = base + pll_clk->lock_offset; + pll->con_reg = base + pll_clk->con_offset; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s : %ld\n", + __func__, pll_clk->name, PTR_ERR(clk)); + kfree(pll); + return; + } + + samsung_clk_add_lookup(clk, pll_clk->id); + + if (!pll_clk->alias) + return; + + ret = clk_register_clkdev(clk, pll_clk->alias, pll_clk->dev_name); + if (ret) + pr_err("%s: failed to register lookup for %s : %d", + __func__, pll_clk->name, ret); +} + +void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list, + unsigned int nr_pll, void __iomem *base) +{ + int cnt; + + for (cnt = 0; cnt < nr_pll; cnt++) + _samsung_clk_register_pll(&pll_list[cnt], base); +} diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index f33786e9a78b..6c39030080fb 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h @@ -12,28 +12,83 @@ #ifndef __SAMSUNG_CLK_PLL_H #define __SAMSUNG_CLK_PLL_H -enum pll45xx_type { +enum samsung_pll_type { + pll_35xx, + pll_36xx, + pll_2550, + pll_2650, pll_4500, pll_4502, - pll_4508 -}; - -enum pll46xx_type { + pll_4508, pll_4600, pll_4650, pll_4650c, + pll_6552, + pll_6553, +}; + +#define PLL_35XX_RATE(_rate, _m, _p, _s) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + } + +#define PLL_36XX_RATE(_rate, _m, _p, _s, _k) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .kdiv = (_k), \ + } + +#define PLL_45XX_RATE(_rate, _m, _p, _s, _afc) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .afc = (_afc), \ + } + +#define PLL_4600_RATE(_rate, _m, _p, _s, _k, _vsel) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .kdiv = (_k), \ + .vsel = (_vsel), \ + } + +#define PLL_4650_RATE(_rate, _m, _p, _s, _k, _mfr, _mrr, _vsel) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .kdiv = (_k), \ + .mfr = (_mfr), \ + .mrr = (_mrr), \ + .vsel = (_vsel), \ + } + +/* NOTE: Rate table should be kept sorted in descending order. */ + +struct samsung_pll_rate_table { + unsigned int rate; + unsigned int pdiv; + unsigned int mdiv; + unsigned int sdiv; + unsigned int kdiv; + unsigned int afc; + unsigned int mfr; + unsigned int mrr; + unsigned int vsel; }; -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); diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c new file mode 100644 index 000000000000..7d2c84265947 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c64xx.c @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2013 Tomasz Figa <tomasz.figa at gmail.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 S3C64xx SoCs. +*/ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include <dt-bindings/clock/samsung,s3c64xx-clock.h> + +#include "clk.h" +#include "clk-pll.h" + +/* S3C64xx clock controller register offsets. */ +#define APLL_LOCK 0x000 +#define MPLL_LOCK 0x004 +#define EPLL_LOCK 0x008 +#define APLL_CON 0x00c +#define MPLL_CON 0x010 +#define EPLL_CON0 0x014 +#define EPLL_CON1 0x018 +#define CLK_SRC 0x01c +#define CLK_DIV0 0x020 +#define CLK_DIV1 0x024 +#define CLK_DIV2 0x028 +#define HCLK_GATE 0x030 +#define PCLK_GATE 0x034 +#define SCLK_GATE 0x038 +#define MEM0_GATE 0x03c +#define CLK_SRC2 0x10c +#define OTHERS 0x900 + +/* Helper macros to define clock arrays. */ +#define FIXED_RATE_CLOCKS(name) \ + static struct samsung_fixed_rate_clock name[] +#define MUX_CLOCKS(name) \ + static struct samsung_mux_clock name[] +#define DIV_CLOCKS(name) \ + static struct samsung_div_clock name[] +#define GATE_CLOCKS(name) \ + static struct samsung_gate_clock name[] + +/* Helper macros for gate types present on S3C64xx. */ +#define GATE_BUS(_id, cname, pname, o, b) \ + GATE(_id, cname, pname, o, b, 0, 0) +#define GATE_SCLK(_id, cname, pname, o, b) \ + GATE(_id, cname, pname, o, b, CLK_SET_RATE_PARENT, 0) +#define GATE_ON(_id, cname, pname, o, b) \ + GATE(_id, cname, pname, o, b, CLK_IGNORE_UNUSED, 0) + +/* list of PLLs to be registered */ +enum s3c64xx_plls { + apll, mpll, epll, +}; + +/* + * List of controller registers to be saved and restored during + * a suspend/resume cycle. + */ +static unsigned long s3c64xx_clk_regs[] __initdata = { + APLL_LOCK, + MPLL_LOCK, + EPLL_LOCK, + APLL_CON, + MPLL_CON, + EPLL_CON0, + EPLL_CON1, + CLK_SRC, + CLK_DIV0, + CLK_DIV1, + CLK_DIV2, + HCLK_GATE, + PCLK_GATE, + SCLK_GATE, +}; + +static unsigned long s3c6410_clk_regs[] __initdata = { + CLK_SRC2, + MEM0_GATE, +}; + +/* List of parent clocks common for all S3C64xx SoCs. */ +PNAME(spi_mmc_p) = { "mout_epll", "dout_mpll", "fin_pll", "clk27m" }; +PNAME(uart_p) = { "mout_epll", "dout_mpll" }; +PNAME(audio0_p) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk0", + "pcmcdclk0", "none", "none", "none" }; +PNAME(audio1_p) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk1", + "pcmcdclk0", "none", "none", "none" }; +PNAME(mfc_p) = { "hclkx2", "mout_epll" }; +PNAME(apll_p) = { "fin_pll", "fout_apll" }; +PNAME(mpll_p) = { "fin_pll", "fout_mpll" }; +PNAME(epll_p) = { "fin_pll", "fout_epll" }; +PNAME(hclkx2_p) = { "mout_mpll", "mout_apll" }; + +/* S3C6400-specific parent clocks. */ +PNAME(scaler_lcd_p6400) = { "mout_epll", "dout_mpll", "none", "none" }; +PNAME(irda_p6400) = { "mout_epll", "dout_mpll", "none", "clk48m" }; +PNAME(uhost_p6400) = { "clk48m", "mout_epll", "dout_mpll", "none" }; + +/* S3C6410-specific parent clocks. */ +PNAME(clk27_p6410) = { "clk27m", "fin_pll" }; +PNAME(scaler_lcd_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "none" }; +PNAME(irda_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "clk48m" }; +PNAME(uhost_p6410) = { "clk48m", "mout_epll", "dout_mpll", "fin_pll" }; +PNAME(audio2_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk2", + "pcmcdclk1", "none", "none", "none" }; + +/* Fixed rate clocks generated outside the SoC. */ +FIXED_RATE_CLOCKS(s3c64xx_fixed_rate_ext_clks) __initdata = { + FRATE(0, "fin_pll", NULL, CLK_IS_ROOT, 0), + FRATE(0, "xusbxti", NULL, CLK_IS_ROOT, 0), +}; + +/* Fixed rate clocks generated inside the SoC. */ +FIXED_RATE_CLOCKS(s3c64xx_fixed_rate_clks) __initdata = { + FRATE(CLK27M, "clk27m", NULL, CLK_IS_ROOT, 27000000), + FRATE(CLK48M, "clk48m", NULL, CLK_IS_ROOT, 48000000), +}; + +/* List of clock muxes present on all S3C64xx SoCs. */ +MUX_CLOCKS(s3c64xx_mux_clks) __initdata = { + MUX_F(0, "mout_syncmux", hclkx2_p, OTHERS, 6, 1, 0, CLK_MUX_READ_ONLY), + MUX(MOUT_APLL, "mout_apll", apll_p, CLK_SRC, 0, 1), + MUX(MOUT_MPLL, "mout_mpll", mpll_p, CLK_SRC, 1, 1), + MUX(MOUT_EPLL, "mout_epll", epll_p, CLK_SRC, 2, 1), + MUX(MOUT_MFC, "mout_mfc", mfc_p, CLK_SRC, 4, 1), + MUX(MOUT_AUDIO0, "mout_audio0", audio0_p, CLK_SRC, 7, 3), + MUX(MOUT_AUDIO1, "mout_audio1", audio1_p, CLK_SRC, 10, 3), + MUX(MOUT_UART, "mout_uart", uart_p, CLK_SRC, 13, 1), + MUX(MOUT_SPI0, "mout_spi0", spi_mmc_p, CLK_SRC, 14, 2), + MUX(MOUT_SPI1, "mout_spi1", spi_mmc_p, CLK_SRC, 16, 2), + MUX(MOUT_MMC0, "mout_mmc0", spi_mmc_p, CLK_SRC, 18, 2), + MUX(MOUT_MMC1, "mout_mmc1", spi_mmc_p, CLK_SRC, 20, 2), + MUX(MOUT_MMC2, "mout_mmc2", spi_mmc_p, CLK_SRC, 22, 2), +}; + +/* List of clock muxes present on S3C6400. */ +MUX_CLOCKS(s3c6400_mux_clks) __initdata = { + MUX(MOUT_UHOST, "mout_uhost", uhost_p6400, CLK_SRC, 5, 2), + MUX(MOUT_IRDA, "mout_irda", irda_p6400, CLK_SRC, 24, 2), + MUX(MOUT_LCD, "mout_lcd", scaler_lcd_p6400, CLK_SRC, 26, 2), + MUX(MOUT_SCALER, "mout_scaler", scaler_lcd_p6400, CLK_SRC, 28, 2), +}; + +/* List of clock muxes present on S3C6410. */ +MUX_CLOCKS(s3c6410_mux_clks) __initdata = { + MUX(MOUT_UHOST, "mout_uhost", uhost_p6410, CLK_SRC, 5, 2), + MUX(MOUT_IRDA, "mout_irda", irda_p6410, CLK_SRC, 24, 2), + MUX(MOUT_LCD, "mout_lcd", scaler_lcd_p6410, CLK_SRC, 26, 2), + MUX(MOUT_SCALER, "mout_scaler", scaler_lcd_p6410, CLK_SRC, 28, 2), + MUX(MOUT_DAC27, "mout_dac27", clk27_p6410, CLK_SRC, 30, 1), + MUX(MOUT_TV27, "mout_tv27", clk27_p6410, CLK_SRC, 31, 1), + MUX(MOUT_AUDIO2, "mout_audio2", audio2_p6410, CLK_SRC2, 0, 3), +}; + +/* List of clock dividers present on all S3C64xx SoCs. */ +DIV_CLOCKS(s3c64xx_div_clks) __initdata = { + DIV(DOUT_MPLL, "dout_mpll", "mout_mpll", CLK_DIV0, 4, 1), + DIV(HCLKX2, "hclkx2", "mout_syncmux", CLK_DIV0, 9, 3), + DIV(HCLK, "hclk", "hclkx2", CLK_DIV0, 8, 1), + DIV(PCLK, "pclk", "hclkx2", CLK_DIV0, 12, 4), + DIV(DOUT_SECUR, "dout_secur", "hclkx2", CLK_DIV0, 18, 2), + DIV(DOUT_CAM, "dout_cam", "hclkx2", CLK_DIV0, 20, 4), + DIV(DOUT_JPEG, "dout_jpeg", "hclkx2", CLK_DIV0, 24, 4), + DIV(DOUT_MFC, "dout_mfc", "mout_mfc", CLK_DIV0, 28, 4), + DIV(DOUT_MMC0, "dout_mmc0", "mout_mmc0", CLK_DIV1, 0, 4), + DIV(DOUT_MMC1, "dout_mmc1", "mout_mmc1", CLK_DIV1, 4, 4), + DIV(DOUT_MMC2, "dout_mmc2", "mout_mmc2", CLK_DIV1, 8, 4), + DIV(DOUT_LCD, "dout_lcd", "mout_lcd", CLK_DIV1, 12, 4), + DIV(DOUT_SCALER, "dout_scaler", "mout_scaler", CLK_DIV1, 16, 4), + DIV(DOUT_UHOST, "dout_uhost", "mout_uhost", CLK_DIV1, 20, 4), + DIV(DOUT_SPI0, "dout_spi0", "mout_spi0", CLK_DIV2, 0, 4), + DIV(DOUT_SPI1, "dout_spi1", "mout_spi1", CLK_DIV2, 4, 4), + DIV(DOUT_AUDIO0, "dout_audio0", "mout_audio0", CLK_DIV2, 8, 4), + DIV(DOUT_AUDIO1, "dout_audio1", "mout_audio1", CLK_DIV2, 12, 4), + DIV(DOUT_UART, "dout_uart", "mout_uart", CLK_DIV2, 16, 4), + DIV(DOUT_IRDA, "dout_irda", "mout_irda", CLK_DIV2, 20, 4), +}; + +/* List of clock dividers present on S3C6400. */ +DIV_CLOCKS(s3c6400_div_clks) __initdata = { + DIV(ARMCLK, "armclk", "mout_apll", CLK_DIV0, 0, 3), +}; + +/* List of clock dividers present on S3C6410. */ +DIV_CLOCKS(s3c6410_div_clks) __initdata = { + DIV(ARMCLK, "armclk", "mout_apll", CLK_DIV0, 0, 4), + DIV(DOUT_FIMC, "dout_fimc", "hclk", CLK_DIV1, 24, 4), + DIV(DOUT_AUDIO2, "dout_audio2", "mout_audio2", CLK_DIV2, 24, 4), +}; + +/* List of clock gates present on all S3C64xx SoCs. */ +GATE_CLOCKS(s3c64xx_gate_clks) __initdata = { + GATE_BUS(HCLK_UHOST, "hclk_uhost", "hclk", HCLK_GATE, 29), + GATE_BUS(HCLK_SECUR, "hclk_secur", "hclk", HCLK_GATE, 28), + GATE_BUS(HCLK_SDMA1, "hclk_sdma1", "hclk", HCLK_GATE, 27), + GATE_BUS(HCLK_SDMA0, "hclk_sdma0", "hclk", HCLK_GATE, 26), + GATE_ON(HCLK_DDR1, "hclk_ddr1", "hclk", HCLK_GATE, 24), + GATE_BUS(HCLK_USB, "hclk_usb", "hclk", HCLK_GATE, 20), + GATE_BUS(HCLK_HSMMC2, "hclk_hsmmc2", "hclk", HCLK_GATE, 19), + GATE_BUS(HCLK_HSMMC1, "hclk_hsmmc1", "hclk", HCLK_GATE, 18), + GATE_BUS(HCLK_HSMMC0, "hclk_hsmmc0", "hclk", HCLK_GATE, 17), + GATE_BUS(HCLK_MDP, "hclk_mdp", "hclk", HCLK_GATE, 16), + GATE_BUS(HCLK_DHOST, "hclk_dhost", "hclk", HCLK_GATE, 15), + GATE_BUS(HCLK_IHOST, "hclk_ihost", "hclk", HCLK_GATE, 14), + GATE_BUS(HCLK_DMA1, "hclk_dma1", "hclk", HCLK_GATE, 13), + GATE_BUS(HCLK_DMA0, "hclk_dma0", "hclk", HCLK_GATE, 12), + GATE_BUS(HCLK_JPEG, "hclk_jpeg", "hclk", HCLK_GATE, 11), + GATE_BUS(HCLK_CAMIF, "hclk_camif", "hclk", HCLK_GATE, 10), + GATE_BUS(HCLK_SCALER, "hclk_scaler", "hclk", HCLK_GATE, 9), + GATE_BUS(HCLK_2D, "hclk_2d", "hclk", HCLK_GATE, 8), + GATE_BUS(HCLK_TV, "hclk_tv", "hclk", HCLK_GATE, 7), + GATE_BUS(HCLK_POST0, "hclk_post0", "hclk", HCLK_GATE, 5), + GATE_BUS(HCLK_ROT, "hclk_rot", "hclk", HCLK_GATE, 4), + GATE_BUS(HCLK_LCD, "hclk_lcd", "hclk", HCLK_GATE, 3), + GATE_BUS(HCLK_TZIC, "hclk_tzic", "hclk", HCLK_GATE, 2), + GATE_ON(HCLK_INTC, "hclk_intc", "hclk", HCLK_GATE, 1), + GATE_ON(PCLK_SKEY, "pclk_skey", "pclk", PCLK_GATE, 24), + GATE_ON(PCLK_CHIPID, "pclk_chipid", "pclk", PCLK_GATE, 23), + GATE_BUS(PCLK_SPI1, "pclk_spi1", "pclk", PCLK_GATE, 22), + GATE_BUS(PCLK_SPI0, "pclk_spi0", "pclk", PCLK_GATE, 21), + GATE_BUS(PCLK_HSIRX, "pclk_hsirx", "pclk", PCLK_GATE, 20), + GATE_BUS(PCLK_HSITX, "pclk_hsitx", "pclk", PCLK_GATE, 19), + GATE_ON(PCLK_GPIO, "pclk_gpio", "pclk", PCLK_GATE, 18), + GATE_BUS(PCLK_IIC0, "pclk_iic0", "pclk", PCLK_GATE, 17), + GATE_BUS(PCLK_IIS1, "pclk_iis1", "pclk", PCLK_GATE, 16), + GATE_BUS(PCLK_IIS0, "pclk_iis0", "pclk", PCLK_GATE, 15), + GATE_BUS(PCLK_AC97, "pclk_ac97", "pclk", PCLK_GATE, 14), + GATE_BUS(PCLK_TZPC, "pclk_tzpc", "pclk", PCLK_GATE, 13), + GATE_BUS(PCLK_TSADC, "pclk_tsadc", "pclk", PCLK_GATE, 12), + GATE_BUS(PCLK_KEYPAD, "pclk_keypad", "pclk", PCLK_GATE, 11), + GATE_BUS(PCLK_IRDA, "pclk_irda", "pclk", PCLK_GATE, 10), + GATE_BUS(PCLK_PCM1, "pclk_pcm1", "pclk", PCLK_GATE, 9), + GATE_BUS(PCLK_PCM0, "pclk_pcm0", "pclk", PCLK_GATE, 8), + GATE_BUS(PCLK_PWM, "pclk_pwm", "pclk", PCLK_GATE, 7), + GATE_BUS(PCLK_RTC, "pclk_rtc", "pclk", PCLK_GATE, 6), + GATE_BUS(PCLK_WDT, "pclk_wdt", "pclk", PCLK_GATE, 5), + GATE_BUS(PCLK_UART3, "pclk_uart3", "pclk", PCLK_GATE, 4), + GATE_BUS(PCLK_UART2, "pclk_uart2", "pclk", PCLK_GATE, 3), + GATE_BUS(PCLK_UART1, "pclk_uart1", "pclk", PCLK_GATE, 2), + GATE_BUS(PCLK_UART0, "pclk_uart0", "pclk", PCLK_GATE, 1), + GATE_BUS(PCLK_MFC, "pclk_mfc", "pclk", PCLK_GATE, 0), + GATE_SCLK(SCLK_UHOST, "sclk_uhost", "dout_uhost", SCLK_GATE, 30), + GATE_SCLK(SCLK_MMC2_48, "sclk_mmc2_48", "clk48m", SCLK_GATE, 29), + GATE_SCLK(SCLK_MMC1_48, "sclk_mmc1_48", "clk48m", SCLK_GATE, 28), + GATE_SCLK(SCLK_MMC0_48, "sclk_mmc0_48", "clk48m", SCLK_GATE, 27), + GATE_SCLK(SCLK_MMC2, "sclk_mmc2", "dout_mmc2", SCLK_GATE, 26), + GATE_SCLK(SCLK_MMC1, "sclk_mmc1", "dout_mmc1", SCLK_GATE, 25), + GATE_SCLK(SCLK_MMC0, "sclk_mmc0", "dout_mmc0", SCLK_GATE, 24), + GATE_SCLK(SCLK_SPI1_48, "sclk_spi1_48", "clk48m", SCLK_GATE, 23), + GATE_SCLK(SCLK_SPI0_48, "sclk_spi0_48", "clk48m", SCLK_GATE, 22), + GATE_SCLK(SCLK_SPI1, "sclk_spi1", "dout_spi1", SCLK_GATE, 21), + GATE_SCLK(SCLK_SPI0, "sclk_spi0", "dout_spi0", SCLK_GATE, 20), + GATE_SCLK(SCLK_DAC27, "sclk_dac27", "mout_dac27", SCLK_GATE, 19), + GATE_SCLK(SCLK_TV27, "sclk_tv27", "mout_tv27", SCLK_GATE, 18), + GATE_SCLK(SCLK_SCALER27, "sclk_scaler27", "clk27m", SCLK_GATE, 17), + GATE_SCLK(SCLK_SCALER, "sclk_scaler", "dout_scaler", SCLK_GATE, 16), + GATE_SCLK(SCLK_LCD27, "sclk_lcd27", "clk27m", SCLK_GATE, 15), + GATE_SCLK(SCLK_LCD, "sclk_lcd", "dout_lcd", SCLK_GATE, 14), + GATE_SCLK(SCLK_POST0_27, "sclk_post0_27", "clk27m", SCLK_GATE, 12), + GATE_SCLK(SCLK_POST0, "sclk_post0", "dout_lcd", SCLK_GATE, 10), + GATE_SCLK(SCLK_AUDIO1, "sclk_audio1", "dout_audio1", SCLK_GATE, 9), + GATE_SCLK(SCLK_AUDIO0, "sclk_audio0", "dout_audio0", SCLK_GATE, 8), + GATE_SCLK(SCLK_SECUR, "sclk_secur", "dout_secur", SCLK_GATE, 7), + GATE_SCLK(SCLK_IRDA, "sclk_irda", "dout_irda", SCLK_GATE, 6), + GATE_SCLK(SCLK_UART, "sclk_uart", "dout_uart", SCLK_GATE, 5), + GATE_SCLK(SCLK_MFC, "sclk_mfc", "dout_mfc", SCLK_GATE, 3), + GATE_SCLK(SCLK_CAM, "sclk_cam", "dout_cam", SCLK_GATE, 2), + GATE_SCLK(SCLK_JPEG, "sclk_jpeg", "dout_jpeg", SCLK_GATE, 1), +}; + +/* List of clock gates present on S3C6400. */ +GATE_CLOCKS(s3c6400_gate_clks) __initdata = { + GATE_ON(HCLK_DDR0, "hclk_ddr0", "hclk", HCLK_GATE, 23), + GATE_SCLK(SCLK_ONENAND, "sclk_onenand", "parent", SCLK_GATE, 4), +}; + +/* List of clock gates present on S3C6410. */ +GATE_CLOCKS(s3c6410_gate_clks) __initdata = { + GATE_BUS(HCLK_3DSE, "hclk_3dse", "hclk", HCLK_GATE, 31), + GATE_ON(HCLK_IROM, "hclk_irom", "hclk", HCLK_GATE, 25), + GATE_ON(HCLK_MEM1, "hclk_mem1", "hclk", HCLK_GATE, 22), + GATE_ON(HCLK_MEM0, "hclk_mem0", "hclk", HCLK_GATE, 21), + GATE_BUS(HCLK_MFC, "hclk_mfc", "hclk", HCLK_GATE, 0), + GATE_BUS(PCLK_IIC1, "pclk_iic1", "pclk", PCLK_GATE, 27), + GATE_BUS(PCLK_IIS2, "pclk_iis2", "pclk", PCLK_GATE, 26), + GATE_SCLK(SCLK_FIMC, "sclk_fimc", "dout_fimc", SCLK_GATE, 13), + GATE_SCLK(SCLK_AUDIO2, "sclk_audio2", "dout_audio2", SCLK_GATE, 11), + GATE_BUS(MEM0_CFCON, "mem0_cfcon", "hclk_mem0", MEM0_GATE, 5), + GATE_BUS(MEM0_ONENAND1, "mem0_onenand1", "hclk_mem0", MEM0_GATE, 4), + GATE_BUS(MEM0_ONENAND0, "mem0_onenand0", "hclk_mem0", MEM0_GATE, 3), + GATE_BUS(MEM0_NFCON, "mem0_nfcon", "hclk_mem0", MEM0_GATE, 2), + GATE_ON(MEM0_SROM, "mem0_srom", "hclk_mem0", MEM0_GATE, 1), +}; + +/* List of PLL clocks. */ +static struct samsung_pll_clock s3c64xx_pll_clks[] __initdata = { + [apll] = PLL(pll_6552, FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON, NULL), + [mpll] = PLL(pll_6552, FOUT_MPLL, "fout_mpll", "fin_pll", + MPLL_LOCK, MPLL_CON, NULL), + [epll] = PLL(pll_6553, FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), +}; + +/* Aliases for common s3c64xx clocks. */ +static struct samsung_clock_alias s3c64xx_clock_aliases[] = { + ALIAS(FOUT_APLL, NULL, "fout_apll"), + ALIAS(FOUT_MPLL, NULL, "fout_mpll"), + ALIAS(FOUT_EPLL, NULL, "fout_epll"), + ALIAS(MOUT_EPLL, NULL, "mout_epll"), + ALIAS(DOUT_MPLL, NULL, "dout_mpll"), + ALIAS(HCLKX2, NULL, "hclk2"), + ALIAS(HCLK, NULL, "hclk"), + ALIAS(PCLK, NULL, "pclk"), + ALIAS(PCLK, NULL, "clk_uart_baud2"), + ALIAS(ARMCLK, NULL, "armclk"), + ALIAS(HCLK_UHOST, "s3c2410-ohci", "usb-host"), + ALIAS(HCLK_USB, "s3c-hsotg", "otg"), + ALIAS(HCLK_HSMMC2, "s3c-sdhci.2", "hsmmc"), + ALIAS(HCLK_HSMMC2, "s3c-sdhci.2", "mmc_busclk.0"), + ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "hsmmc"), + ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"), + ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"), + ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"), + ALIAS(HCLK_DMA1, NULL, "dma1"), + ALIAS(HCLK_DMA0, NULL, "dma0"), + ALIAS(HCLK_CAMIF, "s3c-camif", "camif"), + ALIAS(HCLK_LCD, "s3c-fb", "lcd"), + ALIAS(PCLK_SPI1, "s3c6410-spi.1", "spi"), + ALIAS(PCLK_SPI0, "s3c6410-spi.0", "spi"), + ALIAS(PCLK_IIC0, "s3c2440-i2c.0", "i2c"), + ALIAS(PCLK_IIS1, "samsung-i2s.1", "iis"), + ALIAS(PCLK_IIS0, "samsung-i2s.0", "iis"), + ALIAS(PCLK_AC97, "samsung-ac97", "ac97"), + ALIAS(PCLK_TSADC, "s3c64xx-adc", "adc"), + ALIAS(PCLK_KEYPAD, "samsung-keypad", "keypad"), + ALIAS(PCLK_PCM1, "samsung-pcm.1", "pcm"), + ALIAS(PCLK_PCM0, "samsung-pcm.0", "pcm"), + ALIAS(PCLK_PWM, NULL, "timers"), + ALIAS(PCLK_RTC, "s3c64xx-rtc", "rtc"), + ALIAS(PCLK_WDT, NULL, "watchdog"), + ALIAS(PCLK_UART3, "s3c6400-uart.3", "uart"), + ALIAS(PCLK_UART2, "s3c6400-uart.2", "uart"), + ALIAS(PCLK_UART1, "s3c6400-uart.1", "uart"), + ALIAS(PCLK_UART0, "s3c6400-uart.0", "uart"), + ALIAS(SCLK_UHOST, "s3c2410-ohci", "usb-bus-host"), + ALIAS(SCLK_MMC2, "s3c-sdhci.2", "mmc_busclk.2"), + ALIAS(SCLK_MMC1, "s3c-sdhci.1", "mmc_busclk.2"), + ALIAS(SCLK_MMC0, "s3c-sdhci.0", "mmc_busclk.2"), + ALIAS(SCLK_SPI1, "s3c6410-spi.1", "spi-bus"), + ALIAS(SCLK_SPI0, "s3c6410-spi.0", "spi-bus"), + ALIAS(SCLK_AUDIO1, "samsung-pcm.1", "audio-bus"), + ALIAS(SCLK_AUDIO1, "samsung-i2s.1", "audio-bus"), + ALIAS(SCLK_AUDIO0, "samsung-pcm.0", "audio-bus"), + ALIAS(SCLK_AUDIO0, "samsung-i2s.0", "audio-bus"), + ALIAS(SCLK_UART, NULL, "clk_uart_baud3"), + ALIAS(SCLK_CAM, "s3c-camif", "camera"), +}; + +/* Aliases for s3c6400-specific clocks. */ +static struct samsung_clock_alias s3c6400_clock_aliases[] = { + /* Nothing to place here yet. */ +}; + +/* Aliases for s3c6410-specific clocks. */ +static struct samsung_clock_alias s3c6410_clock_aliases[] = { + ALIAS(PCLK_IIC1, "s3c2440-i2c.1", "i2c"), + ALIAS(PCLK_IIS2, "samsung-i2s.2", "iis"), + ALIAS(SCLK_FIMC, "s3c-camif", "fimc"), + ALIAS(SCLK_AUDIO2, "samsung-i2s.2", "audio-bus"), + ALIAS(MEM0_SROM, NULL, "srom"), +}; + +static void __init s3c64xx_clk_register_fixed_ext(unsigned long fin_pll_f, + unsigned long xusbxti_f) +{ + s3c64xx_fixed_rate_ext_clks[0].fixed_rate = fin_pll_f; + s3c64xx_fixed_rate_ext_clks[1].fixed_rate = xusbxti_f; + samsung_clk_register_fixed_rate(s3c64xx_fixed_rate_ext_clks, + ARRAY_SIZE(s3c64xx_fixed_rate_ext_clks)); +} + +/* Register s3c64xx clocks. */ +void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f, + unsigned long xusbxti_f, bool is_s3c6400, + void __iomem *reg_base) +{ + unsigned long *soc_regs = NULL; + unsigned long nr_soc_regs = 0; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + if (!is_s3c6400) { + soc_regs = s3c6410_clk_regs; + nr_soc_regs = ARRAY_SIZE(s3c6410_clk_regs); + } + + samsung_clk_init(np, reg_base, NR_CLKS, s3c64xx_clk_regs, + ARRAY_SIZE(s3c64xx_clk_regs), soc_regs, nr_soc_regs); + + /* Register external clocks. */ + if (!np) + s3c64xx_clk_register_fixed_ext(xtal_f, xusbxti_f); + + /* Register PLLs. */ + samsung_clk_register_pll(s3c64xx_pll_clks, + ARRAY_SIZE(s3c64xx_pll_clks), reg_base); + + /* Register common internal clocks. */ + samsung_clk_register_fixed_rate(s3c64xx_fixed_rate_clks, + ARRAY_SIZE(s3c64xx_fixed_rate_clks)); + samsung_clk_register_mux(s3c64xx_mux_clks, + ARRAY_SIZE(s3c64xx_mux_clks)); + samsung_clk_register_div(s3c64xx_div_clks, + ARRAY_SIZE(s3c64xx_div_clks)); + samsung_clk_register_gate(s3c64xx_gate_clks, + ARRAY_SIZE(s3c64xx_gate_clks)); + + /* Register SoC-specific clocks. */ + if (is_s3c6400) { + samsung_clk_register_mux(s3c6400_mux_clks, + ARRAY_SIZE(s3c6400_mux_clks)); + samsung_clk_register_div(s3c6400_div_clks, + ARRAY_SIZE(s3c6400_div_clks)); + samsung_clk_register_gate(s3c6400_gate_clks, + ARRAY_SIZE(s3c6400_gate_clks)); + samsung_clk_register_alias(s3c6400_clock_aliases, + ARRAY_SIZE(s3c6400_clock_aliases)); + } else { + samsung_clk_register_mux(s3c6410_mux_clks, + ARRAY_SIZE(s3c6410_mux_clks)); + samsung_clk_register_div(s3c6410_div_clks, + ARRAY_SIZE(s3c6410_div_clks)); + samsung_clk_register_gate(s3c6410_gate_clks, + ARRAY_SIZE(s3c6410_gate_clks)); + samsung_clk_register_alias(s3c6410_clock_aliases, + ARRAY_SIZE(s3c6410_clock_aliases)); + } + + samsung_clk_register_alias(s3c64xx_clock_aliases, + ARRAY_SIZE(s3c64xx_clock_aliases)); + + pr_info("%s clocks: apll = %lu, mpll = %lu\n" + "\tepll = %lu, arm_clk = %lu\n", + is_s3c6400 ? "S3C6400" : "S3C6410", + _get_rate("fout_apll"), _get_rate("fout_mpll"), + _get_rate("fout_epll"), _get_rate("armclk")); +} + +static void __init s3c6400_clk_init(struct device_node *np) +{ + s3c64xx_clk_init(np, 0, 0, true, NULL); +} +CLK_OF_DECLARE(s3c6400_clk, "samsung,s3c6400-clock", s3c6400_clk_init); + +static void __init s3c6410_clk_init(struct device_node *np) +{ + s3c64xx_clk_init(np, 0, 0, false, NULL); +} +CLK_OF_DECLARE(s3c6410_clk, "samsung,s3c6410-clock", s3c6410_clk_init); diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index cd3c40ab50f3..f503f32e2f80 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -307,14 +307,12 @@ void __init samsung_clk_of_register_fixed_ext( unsigned long _get_rate(const char *clk_name) { struct clk *clk; - unsigned long rate; - clk = clk_get(NULL, clk_name); - if (IS_ERR(clk)) { + clk = __clk_lookup(clk_name); + if (!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; + + return clk_get_rate(clk); } diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index e4ad6ea9aa76..31b4174e7a5b 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -19,6 +19,7 @@ #include <linux/clk-provider.h> #include <linux/of.h> #include <linux/of_address.h> +#include "clk-pll.h" /** * struct samsung_clock_alias: information about mux clock @@ -39,6 +40,8 @@ struct samsung_clock_alias { .alias = a, \ } +#define MHZ (1000 * 1000) + /** * struct samsung_fixed_rate_clock: information about fixed-rate clock * @id: platform specific id of the clock. @@ -127,7 +130,7 @@ struct samsung_mux_clock { .name = cname, \ .parent_names = pnames, \ .num_parents = ARRAY_SIZE(pnames), \ - .flags = f, \ + .flags = (f) | CLK_SET_RATE_NO_REPARENT, \ .offset = o, \ .shift = s, \ .width = w, \ @@ -144,6 +147,9 @@ struct samsung_mux_clock { #define MUX_F(_id, cname, pnames, o, s, w, f, mf) \ __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, NULL) +#define MUX_FA(_id, cname, pnames, o, s, w, f, mf, a) \ + __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, a) + /** * @id: platform specific id of the clock. * struct samsung_div_clock: information about div clock @@ -258,6 +264,54 @@ struct samsung_clk_reg_dump { u32 value; }; +/** + * struct samsung_pll_clock: information about pll clock + * @id: platform specific id of the clock. + * @dev_name: name of the device to which this clock belongs. + * @name: name of this pll clock. + * @parent_name: name of the parent clock. + * @flags: optional flags for basic clock. + * @con_offset: offset of the register for configuring the PLL. + * @lock_offset: offset of the register for locking the PLL. + * @type: Type of PLL to be registered. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_pll_clock { + unsigned int id; + const char *dev_name; + const char *name; + const char *parent_name; + unsigned long flags; + int con_offset; + int lock_offset; + enum samsung_pll_type type; + const struct samsung_pll_rate_table *rate_table; + const char *alias; +}; + +#define __PLL(_typ, _id, _dname, _name, _pname, _flags, _lock, _con, \ + _rtable, _alias) \ + { \ + .id = _id, \ + .type = _typ, \ + .dev_name = _dname, \ + .name = _name, \ + .parent_name = _pname, \ + .flags = CLK_GET_RATE_NOCACHE, \ + .con_offset = _con, \ + .lock_offset = _lock, \ + .rate_table = _rtable, \ + .alias = _alias, \ + } + +#define PLL(_typ, _id, _name, _pname, _lock, _con, _rtable) \ + __PLL(_typ, _id, NULL, _name, _pname, CLK_GET_RATE_NOCACHE, \ + _lock, _con, _rtable, _name) + +#define PLL_A(_typ, _id, _name, _pname, _lock, _con, _alias, _rtable) \ + __PLL(_typ, _id, NULL, _name, _pname, CLK_GET_RATE_NOCACHE, \ + _lock, _con, _rtable, _alias) + 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, @@ -281,6 +335,8 @@ 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 void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list, + unsigned int nr_clk, void __iomem *base); extern unsigned long _get_rate(const char *clk_name); diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index bd11315cf5ab..5bb848cac6ec 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -24,15 +24,17 @@ #include <linux/of.h> /* Clock Manager offsets */ -#define CLKMGR_CTRL 0x0 -#define CLKMGR_BYPASS 0x4 +#define CLKMGR_CTRL 0x0 +#define CLKMGR_BYPASS 0x4 +#define CLKMGR_L4SRC 0x70 +#define CLKMGR_PERPLL_SRC 0xAC /* 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 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 @@ -41,6 +43,17 @@ #define SOCFPGA_PLL_DIVF_SHIFT 3 #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 #define SOCFPGA_PLL_DIVQ_SHIFT 16 +#define SOCFGPA_MAX_PARENTS 3 + +#define SOCFPGA_L4_MP_CLK "l4_mp_clk" +#define SOCFPGA_L4_SP_CLK "l4_sp_clk" +#define SOCFPGA_NAND_CLK "nand_clk" +#define SOCFPGA_NAND_X_CLK "nand_x_clk" +#define SOCFPGA_MMC_CLK "mmc_clk" +#define SOCFPGA_DB_CLK "gpio_db_clk" + +#define div_mask(width) ((1 << (width)) - 1) +#define streq(a, b) (strcmp((a), (b)) == 0) extern void __iomem *clk_mgr_base_addr; @@ -49,6 +62,9 @@ struct socfpga_clk { char *parent_name; char *clk_name; u32 fixed_div; + void __iomem *div_reg; + u32 width; /* only valid if div_reg != 0 */ + u32 shift; /* only valid if div_reg != 0 */ }; #define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw) @@ -132,8 +148,9 @@ static __init struct clk *socfpga_clk_init(struct device_node *node, socfpga_clk->hw.hw.init = &init; - if (strcmp(clk_name, "main_pll") || strcmp(clk_name, "periph_pll") || - strcmp(clk_name, "sdram_pll")) { + if (streq(clk_name, "main_pll") || + streq(clk_name, "periph_pll") || + streq(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; @@ -148,6 +165,159 @@ static __init struct clk *socfpga_clk_init(struct device_node *node, return clk; } +static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) +{ + u32 l4_src; + u32 perpll_src; + + if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { + l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + return l4_src &= 0x1; + } + if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { + l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + return !!(l4_src & 2); + } + + perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) + return perpll_src &= 0x3; + if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || + streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) + return (perpll_src >> 2) & 3; + + /* QSPI clock */ + return (perpll_src >> 4) & 3; + +} + +static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) +{ + u32 src_reg; + + if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { + src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + src_reg &= ~0x1; + src_reg |= parent; + writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); + } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { + src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + src_reg &= ~0x2; + src_reg |= (parent << 1); + writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); + } else { + src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) { + src_reg &= ~0x3; + src_reg |= parent; + } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || + streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) { + src_reg &= ~0xC; + src_reg |= (parent << 2); + } else {/* QSPI clock */ + src_reg &= ~0x30; + src_reg |= (parent << 4); + } + writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + } + + return 0; +} + +static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); + u32 div = 1, val; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else if (socfpgaclk->div_reg) { + val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + val &= div_mask(socfpgaclk->width); + if (streq(hwclk->init->name, SOCFPGA_DB_CLK)) + div = val + 1; + else + div = (1 << val); + } + + return parent_rate / div; +} + +static struct clk_ops gateclk_ops = { + .recalc_rate = socfpga_clk_recalc_rate, + .get_parent = socfpga_clk_get_parent, + .set_parent = socfpga_clk_set_parent, +}; + +static void __init socfpga_gate_clk_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 clk_gate[2]; + u32 div_reg[3]; + u32 fixed_div; + struct clk *clk; + struct socfpga_clk *socfpga_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFGPA_MAX_PARENTS]; + struct clk_init_data init; + int rc; + int i = 0; + + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); + if (WARN_ON(!socfpga_clk)) + return; + + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); + if (rc) + clk_gate[0] = 0; + + if (clk_gate[0]) { + socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; + socfpga_clk->hw.bit_idx = clk_gate[1]; + + gateclk_ops.enable = clk_gate_ops.enable; + gateclk_ops.disable = clk_gate_ops.disable; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + socfpga_clk->fixed_div = 0; + else + socfpga_clk->fixed_div = fixed_div; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; + socfpga_clk->shift = div_reg[1]; + socfpga_clk->width = div_reg[2]; + } else { + socfpga_clk->div_reg = 0; + } + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.parent_names = parent_name; + init.num_parents = i; + socfpga_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &socfpga_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(socfpga_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(rc)) + return; +} + static void __init socfpga_pll_init(struct device_node *node) { socfpga_clk_init(node, &clk_pll_ops); @@ -160,6 +330,12 @@ static void __init socfpga_periph_init(struct device_node *node) } CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init); +static void __init socfpga_gate_init(struct device_node *node) +{ + socfpga_gate_clk_init(node, &gateclk_ops); +} +CLK_OF_DECLARE(socfpga_gate, "altr,socfpga-gate-clk", socfpga_gate_init); + void __init socfpga_init_clocks(void) { struct clk *clk; diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c index aedbbe12f321..65894f7687ed 100644 --- a/drivers/clk/spear/spear1310_clock.c +++ b/drivers/clk/spear/spear1310_clock.c @@ -416,9 +416,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* clock derived from 24 or 25 MHz osc clk */ /* vco-pll */ clk = clk_register_mux(NULL, "vco1_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL1_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL1_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco1_mclk", NULL); clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk", 0, SPEAR1310_PLL1_CTR, SPEAR1310_PLL1_FRQ, pll_rtbl, @@ -427,9 +427,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "pll1_clk", NULL); clk = clk_register_mux(NULL, "vco2_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL2_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL2_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco2_mclk", NULL); clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk", 0, SPEAR1310_PLL2_CTR, SPEAR1310_PLL2_FRQ, pll_rtbl, @@ -438,9 +438,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "pll2_clk", NULL); clk = clk_register_mux(NULL, "vco3_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL3_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL3_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco3_mclk", NULL); clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk", 0, SPEAR1310_PLL3_CTR, SPEAR1310_PLL3_FRQ, pll_rtbl, @@ -515,9 +515,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* gpt clocks */ clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT0_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT0_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt0_mclk", NULL); clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0, SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT0_CLK_ENB, 0, @@ -525,9 +525,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT1_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT1_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT1_CLK_ENB, 0, @@ -535,9 +535,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt1"); clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT2_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT2_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT2_CLK_ENB, 0, @@ -545,9 +545,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt2"); clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT3_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT3_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT3_CLK_ENB, 0, @@ -562,7 +562,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_UART_CLK_SHIFT, SPEAR1310_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -602,7 +603,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "c3_syn_gclk", NULL); clk = clk_register_mux(NULL, "c3_mclk", c3_parents, - ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(c3_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_C3_CLK_SHIFT, SPEAR1310_C3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "c3_mclk", NULL); @@ -614,8 +616,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* gmac */ clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents, - ARRAY_SIZE(gmac_phy_input_parents), 0, - SPEAR1310_GMAC_CLK_CFG, + ARRAY_SIZE(gmac_phy_input_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_GMAC_CLK_CFG, SPEAR1310_GMAC_PHY_INPUT_CLK_SHIFT, SPEAR1310_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "phy_input_mclk", NULL); @@ -627,15 +629,16 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "phy_syn_gclk", NULL); clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents, - ARRAY_SIZE(gmac_phy_parents), 0, + ARRAY_SIZE(gmac_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GMAC_PHY_CLK_SHIFT, SPEAR1310_GMAC_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.0", NULL); /* clcd */ clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents, - ARRAY_SIZE(clcd_synth_parents), 0, - SPEAR1310_CLCD_CLK_SYNT, SPEAR1310_CLCD_SYNT_CLK_SHIFT, + ARRAY_SIZE(clcd_synth_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_CLCD_CLK_SYNT, + SPEAR1310_CLCD_SYNT_CLK_SHIFT, SPEAR1310_CLCD_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_syn_mclk", NULL); @@ -645,7 +648,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "clcd_syn_clk", NULL); clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents, - ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(clcd_pixel_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_CLCD_CLK_SHIFT, SPEAR1310_CLCD_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_pixel_mclk", NULL); @@ -657,9 +661,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* i2s */ clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents, - ARRAY_SIZE(i2s_src_parents), 0, SPEAR1310_I2S_CLK_CFG, - SPEAR1310_I2S_SRC_CLK_SHIFT, SPEAR1310_I2S_SRC_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(i2s_src_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_SRC_CLK_SHIFT, + SPEAR1310_I2S_SRC_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_src_mclk", NULL); clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk", 0, @@ -668,7 +672,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "i2s_prs1_clk", NULL); clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_REF_SHIFT, SPEAR1310_I2S_REF_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_mclk", NULL); @@ -806,13 +811,15 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* RAS clks */ clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents, - ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1310_PLL_CFG, + ARRAY_SIZE(gen_synth0_1_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_PLL_CFG, SPEAR1310_RAS_SYNT0_1_CLK_SHIFT, SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn0_1_clk", NULL); clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents, - ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1310_PLL_CFG, + ARRAY_SIZE(gen_synth2_3_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_PLL_CFG, SPEAR1310_RAS_SYNT2_3_CLK_SHIFT, SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn2_3_clk", NULL); @@ -929,8 +936,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk = clk_register_mux(NULL, "smii_rgmii_phy_mclk", smii_rgmii_phy_parents, - ARRAY_SIZE(smii_rgmii_phy_parents), 0, - SPEAR1310_RAS_CTRL_REG1, + ARRAY_SIZE(smii_rgmii_phy_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_RAS_CTRL_REG1, SPEAR1310_SMII_RGMII_PHY_CLK_SHIFT, SPEAR1310_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.1", NULL); @@ -938,15 +945,15 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "stmmacphy.4", NULL); clk = clk_register_mux(NULL, "rmii_phy_mclk", rmii_phy_parents, - ARRAY_SIZE(rmii_phy_parents), 0, + ARRAY_SIZE(rmii_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1310_RAS_CTRL_REG1, SPEAR1310_RMII_PHY_CLK_SHIFT, SPEAR1310_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.3", NULL); clk = clk_register_mux(NULL, "uart1_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART1_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART1_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart1_mclk", NULL); clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0, @@ -955,9 +962,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5c800000.serial"); clk = clk_register_mux(NULL, "uart2_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART2_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART2_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart2_mclk", NULL); clk = clk_register_gate(NULL, "uart2_clk", "uart2_mclk", 0, @@ -966,9 +973,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5c900000.serial"); clk = clk_register_mux(NULL, "uart3_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART3_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART3_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart3_mclk", NULL); clk = clk_register_gate(NULL, "uart3_clk", "uart3_mclk", 0, @@ -977,9 +984,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5ca00000.serial"); clk = clk_register_mux(NULL, "uart4_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART4_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART4_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart4_mclk", NULL); clk = clk_register_gate(NULL, "uart4_clk", "uart4_mclk", 0, @@ -988,9 +995,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cb00000.serial"); clk = clk_register_mux(NULL, "uart5_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART5_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART5_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart5_mclk", NULL); clk = clk_register_gate(NULL, "uart5_clk", "uart5_mclk", 0, @@ -999,9 +1006,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cc00000.serial"); clk = clk_register_mux(NULL, "i2c1_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C1_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C1_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c1_mclk", NULL); clk = clk_register_gate(NULL, "i2c1_clk", "i2c1_mclk", 0, @@ -1010,9 +1017,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cd00000.i2c"); clk = clk_register_mux(NULL, "i2c2_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C2_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C2_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c2_mclk", NULL); clk = clk_register_gate(NULL, "i2c2_clk", "i2c2_mclk", 0, @@ -1021,9 +1028,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5ce00000.i2c"); clk = clk_register_mux(NULL, "i2c3_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C3_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C3_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c3_mclk", NULL); clk = clk_register_gate(NULL, "i2c3_clk", "i2c3_mclk", 0, @@ -1032,9 +1039,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cf00000.i2c"); clk = clk_register_mux(NULL, "i2c4_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C4_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C4_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c4_mclk", NULL); clk = clk_register_gate(NULL, "i2c4_clk", "i2c4_mclk", 0, @@ -1043,9 +1050,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d000000.i2c"); clk = clk_register_mux(NULL, "i2c5_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C5_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C5_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c5_mclk", NULL); clk = clk_register_gate(NULL, "i2c5_clk", "i2c5_mclk", 0, @@ -1054,9 +1061,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d100000.i2c"); clk = clk_register_mux(NULL, "i2c6_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C6_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C6_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c6_mclk", NULL); clk = clk_register_gate(NULL, "i2c6_clk", "i2c6_mclk", 0, @@ -1065,9 +1072,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d200000.i2c"); clk = clk_register_mux(NULL, "i2c7_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C7_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C7_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c7_mclk", NULL); clk = clk_register_gate(NULL, "i2c7_clk", "i2c7_mclk", 0, @@ -1076,9 +1083,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d300000.i2c"); clk = clk_register_mux(NULL, "ssp1_mclk", ssp1_parents, - ARRAY_SIZE(ssp1_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_SSP1_CLK_SHIFT, SPEAR1310_SSP1_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(ssp1_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_SSP1_CLK_SHIFT, + SPEAR1310_SSP1_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ssp1_mclk", NULL); clk = clk_register_gate(NULL, "ssp1_clk", "ssp1_mclk", 0, @@ -1087,9 +1094,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d400000.spi"); clk = clk_register_mux(NULL, "pci_mclk", pci_parents, - ARRAY_SIZE(pci_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_PCI_CLK_SHIFT, SPEAR1310_PCI_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(pci_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_PCI_CLK_SHIFT, + SPEAR1310_PCI_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "pci_mclk", NULL); clk = clk_register_gate(NULL, "pci_clk", "pci_mclk", 0, @@ -1098,9 +1105,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "pci"); clk = clk_register_mux(NULL, "tdm1_mclk", tdm_parents, - ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_TDM1_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(tdm_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_TDM1_CLK_SHIFT, + SPEAR1310_TDM_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "tdm1_mclk", NULL); clk = clk_register_gate(NULL, "tdm1_clk", "tdm1_mclk", 0, @@ -1109,9 +1116,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "tdm_hdlc.0"); clk = clk_register_mux(NULL, "tdm2_mclk", tdm_parents, - ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_TDM2_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(tdm_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_TDM2_CLK_SHIFT, + SPEAR1310_TDM_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "tdm2_mclk", NULL); clk = clk_register_gate(NULL, "tdm2_clk", "tdm2_mclk", 0, diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 9d0b3949db30..fe835c1845fe 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -473,9 +473,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* clock derived from 24 or 25 MHz osc clk */ /* vco-pll */ clk = clk_register_mux(NULL, "vco1_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL1_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL1_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco1_mclk", NULL); clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk", 0, SPEAR1340_PLL1_CTR, SPEAR1340_PLL1_FRQ, pll_rtbl, @@ -484,9 +484,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "pll1_clk", NULL); clk = clk_register_mux(NULL, "vco2_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL2_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL2_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco2_mclk", NULL); clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk", 0, SPEAR1340_PLL2_CTR, SPEAR1340_PLL2_FRQ, pll_rtbl, @@ -495,9 +495,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "pll2_clk", NULL); clk = clk_register_mux(NULL, "vco3_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL3_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL3_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco3_mclk", NULL); clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk", 0, SPEAR1340_PLL3_CTR, SPEAR1340_PLL3_FRQ, pll_rtbl, @@ -561,8 +561,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "amba_syn_clk", NULL); clk = clk_register_mux(NULL, "sys_mclk", sys_parents, - ARRAY_SIZE(sys_parents), 0, SPEAR1340_SYS_CLK_CTRL, - SPEAR1340_SCLK_SRC_SEL_SHIFT, + ARRAY_SIZE(sys_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_SYS_CLK_CTRL, SPEAR1340_SCLK_SRC_SEL_SHIFT, SPEAR1340_SCLK_SRC_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "sys_mclk", NULL); @@ -583,8 +583,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "smp_twd"); clk = clk_register_mux(NULL, "ahb_clk", ahb_parents, - ARRAY_SIZE(ahb_parents), 0, SPEAR1340_SYS_CLK_CTRL, - SPEAR1340_HCLK_SRC_SEL_SHIFT, + ARRAY_SIZE(ahb_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_SYS_CLK_CTRL, SPEAR1340_HCLK_SRC_SEL_SHIFT, SPEAR1340_HCLK_SRC_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "ahb_clk", NULL); @@ -594,9 +594,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* gpt clocks */ clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT0_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT0_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt0_mclk", NULL); clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0, SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT0_CLK_ENB, 0, @@ -604,9 +604,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT1_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT1_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT1_CLK_ENB, 0, @@ -614,9 +614,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt1"); clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT2_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT2_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT2_CLK_ENB, 0, @@ -624,9 +624,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt2"); clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT3_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT3_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT3_CLK_ENB, 0, @@ -641,7 +641,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart0_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART0_CLK_SHIFT, SPEAR1340_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -658,9 +659,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart1_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart1_mclk", uart1_parents, - ARRAY_SIZE(uart1_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_UART1_CLK_SHIFT, SPEAR1340_UART_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(uart1_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART1_CLK_SHIFT, + SPEAR1340_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart1_mclk", NULL); clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0, @@ -698,7 +699,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "c3_syn_gclk", NULL); clk = clk_register_mux(NULL, "c3_mclk", c3_parents, - ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(c3_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_C3_CLK_SHIFT, SPEAR1340_C3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "c3_mclk", NULL); @@ -710,8 +712,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* gmac */ clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents, - ARRAY_SIZE(gmac_phy_input_parents), 0, - SPEAR1340_GMAC_CLK_CFG, + ARRAY_SIZE(gmac_phy_input_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_GMAC_CLK_CFG, SPEAR1340_GMAC_PHY_INPUT_CLK_SHIFT, SPEAR1340_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "phy_input_mclk", NULL); @@ -723,15 +725,16 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "phy_syn_gclk", NULL); clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents, - ARRAY_SIZE(gmac_phy_parents), 0, + ARRAY_SIZE(gmac_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GMAC_PHY_CLK_SHIFT, SPEAR1340_GMAC_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.0", NULL); /* clcd */ clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents, - ARRAY_SIZE(clcd_synth_parents), 0, - SPEAR1340_CLCD_CLK_SYNT, SPEAR1340_CLCD_SYNT_CLK_SHIFT, + ARRAY_SIZE(clcd_synth_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_CLCD_CLK_SYNT, + SPEAR1340_CLCD_SYNT_CLK_SHIFT, SPEAR1340_CLCD_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_syn_mclk", NULL); @@ -741,7 +744,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "clcd_syn_clk", NULL); clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents, - ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(clcd_pixel_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_CLCD_CLK_SHIFT, SPEAR1340_CLCD_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_pixel_mclk", NULL); @@ -753,9 +757,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* i2s */ clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents, - ARRAY_SIZE(i2s_src_parents), 0, SPEAR1340_I2S_CLK_CFG, - SPEAR1340_I2S_SRC_CLK_SHIFT, SPEAR1340_I2S_SRC_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(i2s_src_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_SRC_CLK_SHIFT, + SPEAR1340_I2S_SRC_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_src_mclk", NULL); clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk", @@ -765,7 +769,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "i2s_prs1_clk", NULL); clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_REF_SHIFT, SPEAR1340_I2S_REF_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_mclk", NULL); @@ -891,13 +896,15 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* RAS clks */ clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents, - ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1340_PLL_CFG, + ARRAY_SIZE(gen_synth0_1_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_PLL_CFG, SPEAR1340_GEN_SYNT0_1_CLK_SHIFT, SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn0_1_mclk", NULL); clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents, - ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1340_PLL_CFG, + ARRAY_SIZE(gen_synth2_3_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_PLL_CFG, SPEAR1340_GEN_SYNT2_3_CLK_SHIFT, SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn2_3_mclk", NULL); @@ -938,7 +945,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "spear_cec.1"); clk = clk_register_mux(NULL, "spdif_out_mclk", spdif_out_parents, - ARRAY_SIZE(spdif_out_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(spdif_out_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_OUT_CLK_SHIFT, SPEAR1340_SPDIF_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "spdif_out_mclk", NULL); @@ -949,7 +957,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "d0000000.spdif-out"); clk = clk_register_mux(NULL, "spdif_in_mclk", spdif_in_parents, - ARRAY_SIZE(spdif_in_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(spdif_in_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_IN_CLK_SHIFT, SPEAR1340_SPDIF_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "spdif_in_mclk", NULL); diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c index f9ec43fd1320..c2d204315546 100644 --- a/drivers/clk/spear/spear3xx_clock.c +++ b/drivers/clk/spear/spear3xx_clock.c @@ -294,7 +294,8 @@ static void __init spear320_clk_init(void __iomem *soc_config_base) clk_register_clkdev(clk, NULL, "a9400000.i2s"); clk = clk_register_mux(NULL, "i2s_ref_clk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, I2S_REF_PCLK_SHIFT, I2S_REF_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_clk", NULL); @@ -313,63 +314,72 @@ static void __init spear320_clk_init(void __iomem *soc_config_base) clk_register_clkdev(clk, "hclk", "ab000000.eth"); clk = clk_register_mux(NULL, "rs485_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_RS485_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9300000.serial"); clk = clk_register_mux(NULL, "sdhci_clk", sdhci_parents, - ARRAY_SIZE(sdhci_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdhci_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, SDHCI_PCLK_SHIFT, SDHCI_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "70000000.sdhci"); clk = clk_register_mux(NULL, "smii_pclk", smii0_parents, - ARRAY_SIZE(smii0_parents), 0, SPEAR320_CONTROL_REG, - SMII_PCLK_SHIFT, SMII_PCLK_MASK, 0, &_lock); + ARRAY_SIZE(smii0_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR320_CONTROL_REG, SMII_PCLK_SHIFT, SMII_PCLK_MASK, + 0, &_lock); clk_register_clkdev(clk, NULL, "smii_pclk"); clk = clk_register_fixed_factor(NULL, "smii_clk", "smii_pclk", 0, 1, 1); clk_register_clkdev(clk, NULL, "smii"); clk = clk_register_mux(NULL, "uart1_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, UART1_PCLK_SHIFT, UART1_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a3000000.serial"); clk = clk_register_mux(NULL, "uart2_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART2_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a4000000.serial"); clk = clk_register_mux(NULL, "uart3_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART3_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9100000.serial"); clk = clk_register_mux(NULL, "uart4_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART4_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9200000.serial"); clk = clk_register_mux(NULL, "uart5_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART5_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "60000000.serial"); clk = clk_register_mux(NULL, "uart6_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART6_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "60100000.serial"); } #else -static inline void spear320_clk_init(void) { } +static inline void spear320_clk_init(void __iomem *soc_config_base) { } #endif void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_base) @@ -427,7 +437,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -444,7 +455,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "firda_syn_gclk", NULL); clk = clk_register_mux(NULL, "firda_mclk", firda_parents, - ARRAY_SIZE(firda_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(firda_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "firda_mclk", NULL); @@ -458,14 +470,16 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_gpt("gpt0_syn_clk", "pll1_clk", 0, PRSC0_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt0_clk", gpt0_parents, - ARRAY_SIZE(gpt0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "gpt0"); clk_register_gpt("gpt1_syn_clk", "pll1_clk", 0, PRSC1_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt1_mclk", gpt1_parents, - ARRAY_SIZE(gpt1_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt1_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", @@ -476,7 +490,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_gpt("gpt2_syn_clk", "pll1_clk", 0, PRSC2_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents, - ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt2_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", @@ -498,9 +513,9 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "gen1_syn_gclk", NULL); clk = clk_register_mux(NULL, "gen2_3_par_clk", gen2_3_parents, - ARRAY_SIZE(gen2_3_parents), 0, CORE_CLK_CFG, - GEN_SYNTH2_3_CLK_SHIFT, GEN_SYNTH2_3_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gen2_3_parents), CLK_SET_RATE_NO_REPARENT, + CORE_CLK_CFG, GEN_SYNTH2_3_CLK_SHIFT, + GEN_SYNTH2_3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen2_3_par_clk", NULL); clk = clk_register_aux("gen2_syn_clk", "gen2_syn_gclk", @@ -540,8 +555,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk, "ahbmult2_clk", NULL); clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, - ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, - MCTR_CLK_MASK, 0, &_lock); + ARRAY_SIZE(ddr_parents), CLK_SET_RATE_NO_REPARENT, + PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ddr_clk", NULL); clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c index 9406f2426d64..4f649c9cb094 100644 --- a/drivers/clk/spear/spear6xx_clock.c +++ b/drivers/clk/spear/spear6xx_clock.c @@ -169,8 +169,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, PERIP_CLK_CFG, - UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "uart_mclk", NULL); clk = clk_register_gate(NULL, "uart0", "uart_mclk", 0, PERIP1_CLK_ENB, @@ -188,8 +189,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "firda_syn_gclk", NULL); clk = clk_register_mux(NULL, "firda_mclk", firda_parents, - ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, - FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + ARRAY_SIZE(firda_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "firda_mclk", NULL); clk = clk_register_gate(NULL, "firda_clk", "firda_mclk", 0, @@ -203,8 +205,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "clcd_syn_gclk", NULL); clk = clk_register_mux(NULL, "clcd_mclk", clcd_parents, - ARRAY_SIZE(clcd_parents), 0, PERIP_CLK_CFG, - CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, &_lock); + ARRAY_SIZE(clcd_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "clcd_mclk", NULL); clk = clk_register_gate(NULL, "clcd_clk", "clcd_mclk", 0, @@ -217,13 +220,13 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt0_1_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt0_mclk", gpt0_1_parents, - ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, - GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt0_1_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt0_1_parents, - ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, - GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt0_1_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, @@ -235,8 +238,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt2_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents, - ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, - GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, @@ -248,8 +251,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt3_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt3_mclk", gpt3_parents, - ARRAY_SIZE(gpt3_parents), 0, PERIP_CLK_CFG, - GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt3_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, @@ -277,8 +280,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "ahbmult2_clk", NULL); clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, - ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, - MCTR_CLK_MASK, 0, &_lock); + ARRAY_SIZE(ddr_parents), CLK_SET_RATE_NO_REPARENT, + PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ddr_clk", NULL); clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 8492ad1d5360..34ee69f4d50c 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -25,12 +25,12 @@ static DEFINE_SPINLOCK(clk_lock); /** - * sunxi_osc_clk_setup() - Setup function for gatable oscillator + * sun4i_osc_clk_setup() - Setup function for gatable oscillator */ #define SUNXI_OSC24M_GATE 0 -static void __init sunxi_osc_clk_setup(struct device_node *node) +static void __init sun4i_osc_clk_setup(struct device_node *node) { struct clk *clk; struct clk_fixed_rate *fixed; @@ -64,22 +64,23 @@ static void __init sunxi_osc_clk_setup(struct device_node *node) &gate->hw, &clk_gate_ops, CLK_IS_ROOT); - if (clk) { + if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); clk_register_clkdev(clk, clk_name, NULL); } } +CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup); /** - * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1 + * sun4i_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, +static void sun4i_get_pll1_factors(u32 *freq, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p) { u8 div; @@ -124,15 +125,97 @@ static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate, *n = div / 4; } +/** + * sun6i_a31_get_pll1_factors() - calculates n, k and m factors for PLL1 + * PLL1 rate is calculated as follows + * rate = parent_rate * (n + 1) * (k + 1) / (m + 1); + * parent_rate should always be 24MHz + */ +static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + /* + * We can operate only on MHz, this will make our life easier + * later. + */ + u32 freq_mhz = *freq / 1000000; + u32 parent_freq_mhz = parent_rate / 1000000; + + /* + * Round down the frequency to the closest multiple of either + * 6 or 16 + */ + u32 round_freq_6 = round_down(freq_mhz, 6); + u32 round_freq_16 = round_down(freq_mhz, 16); + + if (round_freq_6 > round_freq_16) + freq_mhz = round_freq_6; + else + freq_mhz = round_freq_16; + + *freq = freq_mhz * 1000000; + /* + * If the factors pointer are null, we were just called to + * round down the frequency. + * Exit. + */ + if (n == NULL) + return; + + /* If the frequency is a multiple of 32 MHz, k is always 3 */ + if (!(freq_mhz % 32)) + *k = 3; + /* If the frequency is a multiple of 9 MHz, k is always 2 */ + else if (!(freq_mhz % 9)) + *k = 2; + /* If the frequency is a multiple of 8 MHz, k is always 1 */ + else if (!(freq_mhz % 8)) + *k = 1; + /* Otherwise, we don't use the k factor */ + else + *k = 0; + + /* + * If the frequency is a multiple of 2 but not a multiple of + * 3, m is 3. This is the first time we use 6 here, yet we + * will use it on several other places. + * We use this number because it's the lowest frequency we can + * generate (with n = 0, k = 0, m = 3), so every other frequency + * somehow relates to this frequency. + */ + if ((freq_mhz % 6) == 2 || (freq_mhz % 6) == 4) + *m = 2; + /* + * If the frequency is a multiple of 6MHz, but the factor is + * odd, m will be 3 + */ + else if ((freq_mhz / 6) & 1) + *m = 3; + /* Otherwise, we end up with m = 1 */ + else + *m = 1; + + /* Calculate n thanks to the above factors we already got */ + *n = freq_mhz * (*m + 1) / ((*k + 1) * parent_freq_mhz) - 1; + + /* + * If n end up being outbound, and that we can still decrease + * m, do it. + */ + if ((*n + 1) > 31 && (*m + 1) > 1) { + *n = (*n + 1) / 2 - 1; + *m = (*m + 1) / 2 - 1; + } +} /** - * sunxi_get_apb1_factors() - calculates m, p factors for APB1 + * sun4i_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, +static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p) { u8 calcm, calcp; @@ -178,7 +261,7 @@ struct factors_data { void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); }; -static struct clk_factors_config pll1_config = { +static struct clk_factors_config sun4i_pll1_config = { .nshift = 8, .nwidth = 5, .kshift = 4, @@ -189,21 +272,35 @@ static struct clk_factors_config pll1_config = { .pwidth = 2, }; -static struct clk_factors_config apb1_config = { +static struct clk_factors_config sun6i_a31_pll1_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, + .mshift = 0, + .mwidth = 2, +}; + +static struct clk_factors_config sun4i_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 struct factors_data sun4i_pll1_data __initconst = { + .table = &sun4i_pll1_config, + .getter = sun4i_get_pll1_factors, +}; + +static const struct factors_data sun6i_a31_pll1_data __initconst = { + .table = &sun6i_a31_pll1_config, + .getter = sun6i_a31_get_pll1_factors, }; -static const __initconst struct factors_data apb1_data = { - .table = &apb1_config, - .getter = sunxi_get_apb1_factors, +static const struct factors_data sun4i_apb1_data __initconst = { + .table = &sun4i_apb1_config, + .getter = sun4i_get_apb1_factors, }; static void __init sunxi_factors_clk_setup(struct device_node *node, @@ -221,7 +318,7 @@ static void __init sunxi_factors_clk_setup(struct device_node *node, clk = clk_register_factors(NULL, clk_name, parent, 0, reg, data->table, data->getter, &clk_lock); - if (clk) { + if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); clk_register_clkdev(clk, clk_name, NULL); } @@ -239,11 +336,15 @@ struct mux_data { u8 shift; }; -static const __initconst struct mux_data cpu_data = { +static const struct mux_data sun4i_cpu_mux_data __initconst = { .shift = 16, }; -static const __initconst struct mux_data apb1_mux_data = { +static const struct mux_data sun6i_a31_ahb1_mux_data __initconst = { + .shift = 12, +}; + +static const struct mux_data sun4i_apb1_mux_data __initconst = { .shift = 24, }; @@ -261,7 +362,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, 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, + clk = clk_register_mux(NULL, clk_name, parents, i, + CLK_SET_RATE_NO_REPARENT, reg, data->shift, SUNXI_MUX_GATE_WIDTH, 0, &clk_lock); @@ -277,26 +379,34 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, * sunxi_divider_clk_setup() - Setup function for simple divider clocks */ -#define SUNXI_DIVISOR_WIDTH 2 - struct div_data { - u8 shift; - u8 pow; + u8 shift; + u8 pow; + u8 width; +}; + +static const struct div_data sun4i_axi_data __initconst = { + .shift = 0, + .pow = 0, + .width = 2, }; -static const __initconst struct div_data axi_data = { - .shift = 0, - .pow = 0, +static const struct div_data sun4i_ahb_data __initconst = { + .shift = 4, + .pow = 1, + .width = 2, }; -static const __initconst struct div_data ahb_data = { - .shift = 4, - .pow = 1, +static const struct div_data sun4i_apb0_data __initconst = { + .shift = 8, + .pow = 1, + .width = 2, }; -static const __initconst struct div_data apb0_data = { - .shift = 8, - .pow = 1, +static const struct div_data sun6i_a31_apb2_div_data __initconst = { + .shift = 0, + .pow = 0, + .width = 4, }; static void __init sunxi_divider_clk_setup(struct device_node *node, @@ -312,7 +422,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, 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, + reg, data->shift, data->width, data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, &clk_lock); if (clk) { @@ -333,22 +443,70 @@ struct gates_data { DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); }; -static const __initconst struct gates_data axi_gates_data = { +static const struct gates_data sun4i_axi_gates_data __initconst = { .mask = {1}, }; -static const __initconst struct gates_data ahb_gates_data = { +static const struct gates_data sun4i_ahb_gates_data __initconst = { .mask = {0x7F77FFF, 0x14FB3F}, }; -static const __initconst struct gates_data apb0_gates_data = { +static const struct gates_data sun5i_a10s_ahb_gates_data __initconst = { + .mask = {0x147667e7, 0x185915}, +}; + +static const struct gates_data sun5i_a13_ahb_gates_data __initconst = { + .mask = {0x107067e7, 0x185111}, +}; + +static const struct gates_data sun6i_a31_ahb1_gates_data __initconst = { + .mask = {0xEDFE7F62, 0x794F931}, +}; + +static const struct gates_data sun7i_a20_ahb_gates_data __initconst = { + .mask = { 0x12f77fff, 0x16ff3f }, +}; + +static const struct gates_data sun4i_apb0_gates_data __initconst = { .mask = {0x4EF}, }; -static const __initconst struct gates_data apb1_gates_data = { +static const struct gates_data sun5i_a10s_apb0_gates_data __initconst = { + .mask = {0x469}, +}; + +static const struct gates_data sun5i_a13_apb0_gates_data __initconst = { + .mask = {0x61}, +}; + +static const struct gates_data sun7i_a20_apb0_gates_data __initconst = { + .mask = { 0x4ff }, +}; + +static const struct gates_data sun4i_apb1_gates_data __initconst = { .mask = {0xFF00F7}, }; +static const struct gates_data sun5i_a10s_apb1_gates_data __initconst = { + .mask = {0xf0007}, +}; + +static const struct gates_data sun5i_a13_apb1_gates_data __initconst = { + .mask = {0xa0007}, +}; + +static const struct gates_data sun6i_a31_apb1_gates_data __initconst = { + .mask = {0x3031}, +}; + +static const struct gates_data sun6i_a31_apb2_gates_data __initconst = { + .mask = {0x3F000F}, +}; + +static const struct gates_data sun7i_a20_apb1_gates_data __initconst = { + .mask = { 0xff80ff }, +}; + static void __init sunxi_gates_clk_setup(struct device_node *node, struct gates_data *data) { @@ -398,40 +556,49 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, 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,}, +static const struct of_device_id clk_factors_match[] __initconst = { + {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,}, + {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, + {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_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,}, +static const struct of_device_id clk_div_match[] __initconst = { + {.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,}, + {.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,}, + {.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,}, + {.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_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,}, +static const struct of_device_id clk_mux_match[] __initconst = { + {.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,}, + {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,}, + {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_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 const struct of_device_id clk_gates_match[] __initconst = { + {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,}, + {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, + {.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,}, + {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,}, + {.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,}, + {.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,}, + {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, + {.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,}, + {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,}, + {.compatible = "allwinner,sun7i-a20-apb0-gates-clk", .data = &sun7i_a20_apb0_gates_data,}, + {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, + {.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,}, + {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,}, + {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,}, + {.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,}, + {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, {} }; @@ -452,8 +619,8 @@ static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_mat void __init sunxi_init_clocks(void) { - /* Register all the simple sunxi clocks on DT */ - of_clk_init(clk_match); + /* Register all the simple and basic clocks on DT */ + of_clk_init(NULL); /* Register factor clocks */ of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 17c2cc086eb4..197074a57754 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -117,10 +117,6 @@ #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) @@ -128,38 +124,31 @@ #define pll_readl(offset, p) readl_relaxed(p->clk_base + offset) #define pll_readl_base(p) pll_readl(p->params->base_reg, p) #define pll_readl_misc(p) pll_readl(p->params->misc_reg, p) +#define pll_override_readl(offset, p) readl_relaxed(p->pmc + offset) #define pll_writel(val, offset, p) writel_relaxed(val, p->clk_base + offset) #define pll_writel_base(val, p) pll_writel(val, p->params->base_reg, p) #define pll_writel_misc(val, p) pll_writel(val, p->params->misc_reg, p) +#define pll_override_writel(val, offset, p) writel(val, p->pmc + offset) #define mask(w) ((1 << (w)) - 1) -#define divm_mask(p) mask(p->divm_width) -#define divn_mask(p) mask(p->divn_width) +#define divm_mask(p) mask(p->params->div_nmp->divm_width) +#define divn_mask(p) mask(p->params->div_nmp->divn_width) #define divp_mask(p) (p->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK : \ - mask(p->divp_width)) + mask(p->params->div_nmp->divp_width)) #define divm_max(p) (divm_mask(p)) #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 +static struct div_nmp default_nmp = { + .divn_shift = PLL_BASE_DIVN_SHIFT, + .divn_width = PLL_BASE_DIVN_WIDTH, + .divm_shift = PLL_BASE_DIVM_SHIFT, + .divm_width = PLL_BASE_DIVM_WIDTH, + .divp_shift = PLL_BASE_DIVP_SHIFT, + .divp_width = PLL_BASE_DIVP_WIDTH, }; -#endif static void clk_pll_enable_lock(struct tegra_clk_pll *pll) { @@ -297,6 +286,39 @@ static void clk_pll_disable(struct clk_hw *hw) spin_unlock_irqrestore(pll->lock, flags); } +static int _p_div_to_hw(struct clk_hw *hw, u8 p_div) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + + if (p_tohw) { + while (p_tohw->pdiv) { + if (p_div <= p_tohw->pdiv) + return p_tohw->hw_val; + p_tohw++; + } + return -EINVAL; + } + return -EINVAL; +} + +static int _hw_to_p_div(struct clk_hw *hw, u8 p_div_hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + + if (p_tohw) { + while (p_tohw->pdiv) { + if (p_div_hw == p_tohw->hw_val) + return p_tohw->pdiv; + p_tohw++; + } + return -EINVAL; + } + + return 1 << p_div_hw; +} + static int _get_table_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) @@ -326,9 +348,9 @@ 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; + int ret; switch (parent_rate) { case 12000000: @@ -369,20 +391,16 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, || cfg->output_rate > pll->params->vco_max) { pr_err("%s: Failed to set %s rate %lu\n", __func__, __clk_get_name(hw->clk), rate); + WARN_ON(1); 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; + if (pll->params->pdiv_tohw) { + ret = _p_div_to_hw(hw, 1 << p_div); + if (ret < 0) + return ret; + else + cfg->p = ret; } else cfg->p = p_div; @@ -393,29 +411,61 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_freq_table *cfg) { u32 val; + struct tegra_clk_pll_params *params = pll->params; + struct div_nmp *div_nmp = params->div_nmp; + + if ((pll->flags & TEGRA_PLLM) && + (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & + PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { + val = pll_override_readl(params->pmc_divp_reg, pll); + val &= ~(divp_mask(pll) << div_nmp->override_divp_shift); + val |= cfg->p << div_nmp->override_divp_shift; + pll_override_writel(val, params->pmc_divp_reg, pll); + + val = pll_override_readl(params->pmc_divnm_reg, pll); + val &= ~(divm_mask(pll) << div_nmp->override_divm_shift) | + ~(divn_mask(pll) << div_nmp->override_divn_shift); + val |= (cfg->m << div_nmp->override_divm_shift) | + (cfg->n << div_nmp->override_divn_shift); + pll_override_writel(val, params->pmc_divnm_reg, pll); + } else { + val = pll_readl_base(pll); - val = pll_readl_base(pll); + val &= ~((divm_mask(pll) << div_nmp->divm_shift) | + (divn_mask(pll) << div_nmp->divn_shift) | + (divp_mask(pll) << div_nmp->divp_shift)); - 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) | - (cfg->p << pll->divp_shift)); + val |= ((cfg->m << div_nmp->divm_shift) | + (cfg->n << div_nmp->divn_shift) | + (cfg->p << div_nmp->divp_shift)); - pll_writel_base(val, pll); + pll_writel_base(val, pll); + } } static void _get_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_freq_table *cfg) { u32 val; + struct tegra_clk_pll_params *params = pll->params; + struct div_nmp *div_nmp = params->div_nmp; + + if ((pll->flags & TEGRA_PLLM) && + (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & + PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { + val = pll_override_readl(params->pmc_divp_reg, pll); + cfg->p = (val >> div_nmp->override_divp_shift) & divp_mask(pll); + + val = pll_override_readl(params->pmc_divnm_reg, pll); + cfg->m = (val >> div_nmp->override_divm_shift) & divm_mask(pll); + cfg->n = (val >> div_nmp->override_divn_shift) & divn_mask(pll); + } else { + val = pll_readl_base(pll); - 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)); + cfg->m = (val >> div_nmp->divm_shift) & divm_mask(pll); + cfg->n = (val >> div_nmp->divn_shift) & divn_mask(pll); + cfg->p = (val >> div_nmp->divp_shift) & divp_mask(pll); + } } static void _update_pll_cpcon(struct tegra_clk_pll *pll, @@ -485,9 +535,10 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, } if (_get_table_rate(hw, &cfg, rate, parent_rate) && - _calc_rate(hw, &cfg, rate, parent_rate)) + _calc_rate(hw, &cfg, rate, parent_rate)) { + WARN_ON(1); return -EINVAL; - + } if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -507,7 +558,6 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; - u64 output_rate = *prate; if (pll->flags & TEGRA_PLL_FIXED) return pll->fixed_rate; @@ -517,13 +567,12 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, return __clk_get_rate(hw->clk); if (_get_table_rate(hw, &cfg, rate, *prate) && - _calc_rate(hw, &cfg, rate, *prate)) + _calc_rate(hw, &cfg, rate, *prate)) { + WARN_ON(1); return -EINVAL; + } - output_rate *= cfg.n; - do_div(output_rate, cfg.m * (1 << cfg.p)); - - return output_rate; + return cfg.output_rate; } static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, @@ -531,7 +580,6 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; - struct pdiv_map *p_tohw = pll->params->pdiv_tohw; u32 val; u64 rate = parent_rate; int pdiv; @@ -553,21 +601,11 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, _get_pll_mnp(pll, &cfg); - 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; + pdiv = _hw_to_p_div(hw, cfg.p); + if (pdiv < 0) { + WARN_ON(1); + pdiv = 1; + } cfg.m *= pdiv; @@ -647,9 +685,9 @@ static int clk_plle_enable(struct clk_hw *hw) 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.p << pll->divp_shift; + val |= sel.m << pll->params->div_nmp->divm_shift; + val |= sel.n << pll->params->div_nmp->divn_shift; + val |= sel.p << pll->params->div_nmp->divp_shift; val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; pll_writel_base(val, pll); } @@ -680,9 +718,9 @@ static unsigned long clk_plle_recalc_rate(struct clk_hw *hw, u32 divn = 0, divm = 0, divp = 0; u64 rate = parent_rate; - divp = (val >> pll->divp_shift) & (divp_mask(pll)); - divn = (val >> pll->divn_shift) & (divn_mask(pll)); - divm = (val >> pll->divm_shift) & (divm_mask(pll)); + divp = (val >> pll->params->div_nmp->divp_shift) & (divp_mask(pll)); + divn = (val >> pll->params->div_nmp->divn_shift) & (divn_mask(pll)); + divm = (val >> pll->params->div_nmp->divm_shift) & (divm_mask(pll)); divm *= divp; rate *= divn; @@ -769,16 +807,22 @@ static int _calc_dynamic_ramp_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); unsigned int p; + int p_div; 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->output_rate = rate * p; cfg->n = cfg->output_rate * cfg->m / parent_rate; + p_div = _p_div_to_hw(hw, p); + if (p_div < 0) + return p_div; + else + cfg->p = p_div; + if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) return -EINVAL; @@ -790,18 +834,25 @@ static int _pll_ramp_calc_pll(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - int err = 0; + int err = 0, p_div; 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)) { + else { + if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { WARN_ON(1); err = -EINVAL; goto out; + } + p_div = _p_div_to_hw(hw, cfg->p); + if (p_div < 0) + return p_div; + else + cfg->p = p_div; } - if (!cfg->p || (cfg->p > pll->params->max_p)) + if (cfg->p > pll->params->max_p) err = -EINVAL; out: @@ -815,7 +866,6 @@ static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, 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) @@ -826,11 +876,8 @@ static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, _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; + 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); @@ -842,15 +889,19 @@ 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; + int ret = 0, p_div; u64 output_rate = *prate; ret = _pll_ramp_calc_pll(hw, &cfg, rate, *prate); if (ret < 0) return ret; + p_div = _hw_to_p_div(hw, cfg.p); + if (p_div < 0) + return p_div; + output_rate *= cfg.n; - do_div(output_rate, cfg.m * cfg.p); + do_div(output_rate, cfg.m * p_div); return output_rate; } @@ -862,7 +913,6 @@ static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, 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); @@ -881,22 +931,7 @@ static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long 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); - + _update_pll_mnp(pll, &cfg); out: if (pll->lock) @@ -1010,13 +1045,10 @@ static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll, 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_freq_table cfg, old_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); @@ -1025,21 +1057,16 @@ static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long 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))]; + _get_pll_mnp(pll, &old_cfg); - if (cfg.m != old_m) { + if (cfg.m != old_cfg.m) { WARN_ON(1); goto out; } - if (old_n == cfg.n && old_p == cfg.p) + if (old_cfg.n == cfg.n && old_cfg.p == cfg.p) goto out; - cfg.p -= 1; - state = clk_pll_is_enabled(hw); if (state) _clk_pllc_disable(hw); @@ -1178,8 +1205,8 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw) 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.m << pll->params->div_nmp->divm_shift; + val |= sel.n << pll->params->div_nmp->divn_shift; val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; pll_writel_base(val, pll); udelay(1); @@ -1240,12 +1267,8 @@ static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, pll->flags = pll_flags; pll->lock = lock; - pll->divp_shift = PLL_BASE_DIVP_SHIFT; - pll->divp_width = PLL_BASE_DIVP_WIDTH; - pll->divn_shift = PLL_BASE_DIVN_SHIFT; - pll->divn_width = PLL_BASE_DIVN_WIDTH; - pll->divm_shift = PLL_BASE_DIVM_SHIFT; - pll->divm_width = PLL_BASE_DIVM_WIDTH; + if (!pll_params->div_nmp) + pll_params->div_nmp = &default_nmp; return pll; } @@ -1401,7 +1424,7 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, struct tegra_clk_pll *pll; struct clk *clk; - pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_LOCK_MISC; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) @@ -1428,7 +1451,6 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, 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)) @@ -1453,6 +1475,7 @@ struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, pll_flags |= TEGRA_PLL_BYPASS; pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll_flags |= TEGRA_PLLM; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index d78e16ee161c..9467da7dee49 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -21,6 +21,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/clk/tegra.h> #include "clk.h" @@ -28,6 +29,7 @@ #define RST_DEVICES_L 0x004 #define RST_DEVICES_H 0x008 #define RST_DEVICES_U 0x00C +#define RST_DFLL_DVCO 0x2F4 #define RST_DEVICES_V 0x358 #define RST_DEVICES_W 0x35C #define RST_DEVICES_X 0x28C @@ -41,8 +43,36 @@ #define RST_DEVICES_CLR_V 0x434 #define RST_DEVICES_SET_W 0x438 #define RST_DEVICES_CLR_W 0x43c +#define CPU_FINETRIM_SELECT 0x4d4 /* override default prop dlys */ +#define CPU_FINETRIM_DR 0x4d8 /* rise->rise prop dly A */ +#define CPU_FINETRIM_R 0x4e4 /* rise->rise prop dly inc A */ #define RST_DEVICES_NUM 5 +/* RST_DFLL_DVCO bitfields */ +#define DVFS_DFLL_RESET_SHIFT 0 + +/* CPU_FINETRIM_SELECT and CPU_FINETRIM_DR bitfields */ +#define CPU_FINETRIM_1_FCPU_1 BIT(0) /* fcpu0 */ +#define CPU_FINETRIM_1_FCPU_2 BIT(1) /* fcpu1 */ +#define CPU_FINETRIM_1_FCPU_3 BIT(2) /* fcpu2 */ +#define CPU_FINETRIM_1_FCPU_4 BIT(3) /* fcpu3 */ +#define CPU_FINETRIM_1_FCPU_5 BIT(4) /* fl2 */ +#define CPU_FINETRIM_1_FCPU_6 BIT(5) /* ftop */ + +/* CPU_FINETRIM_R bitfields */ +#define CPU_FINETRIM_R_FCPU_1_SHIFT 0 /* fcpu0 */ +#define CPU_FINETRIM_R_FCPU_1_MASK (0x3 << CPU_FINETRIM_R_FCPU_1_SHIFT) +#define CPU_FINETRIM_R_FCPU_2_SHIFT 2 /* fcpu1 */ +#define CPU_FINETRIM_R_FCPU_2_MASK (0x3 << CPU_FINETRIM_R_FCPU_2_SHIFT) +#define CPU_FINETRIM_R_FCPU_3_SHIFT 4 /* fcpu2 */ +#define CPU_FINETRIM_R_FCPU_3_MASK (0x3 << CPU_FINETRIM_R_FCPU_3_SHIFT) +#define CPU_FINETRIM_R_FCPU_4_SHIFT 6 /* fcpu3 */ +#define CPU_FINETRIM_R_FCPU_4_MASK (0x3 << CPU_FINETRIM_R_FCPU_4_SHIFT) +#define CPU_FINETRIM_R_FCPU_5_SHIFT 8 /* fl2 */ +#define CPU_FINETRIM_R_FCPU_5_MASK (0x3 << CPU_FINETRIM_R_FCPU_5_SHIFT) +#define CPU_FINETRIM_R_FCPU_6_SHIFT 10 /* ftop */ +#define CPU_FINETRIM_R_FCPU_6_MASK (0x3 << CPU_FINETRIM_R_FCPU_6_SHIFT) + #define CLK_OUT_ENB_L 0x010 #define CLK_OUT_ENB_H 0x014 #define CLK_OUT_ENB_U 0x018 @@ -127,6 +157,7 @@ #define PMC_DPD_PADS_ORIDE_BLINK_ENB 20 #define PMC_CTRL 0 #define PMC_CTRL_BLINK_ENB 7 +#define PMC_BLINK_TIMER 0x40 #define OSC_CTRL 0x50 #define OSC_CTRL_OSC_FREQ_SHIFT 28 @@ -242,6 +273,8 @@ #define CLK_SOURCE_I2CSLOW 0x3fc #define CLK_SOURCE_SE 0x42c #define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_DFLL_REF 0x62c +#define CLK_SOURCE_DFLL_SOC 0x630 #define CLK_SOURCE_SOC_THERM 0x644 #define CLK_SOURCE_XUSB_HOST_SRC 0x600 #define CLK_SOURCE_XUSB_FALCON_SRC 0x604 @@ -250,6 +283,21 @@ #define CLK_SOURCE_XUSB_DEV_SRC 0x60c #define CLK_SOURCE_EMC 0x19c +/* PLLM override registers */ +#define PMC_PLLM_WB0_OVERRIDE 0x1dc +#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 + +/* Tegra CPU clock and reset control regs */ +#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470 + +#ifdef CONFIG_PM_SLEEP +static struct cpu_clk_suspend_context { + u32 clk_csite_src; + u32 cclkg_burst; + u32 cclkg_divider; +} tegra114_cpu_clk_sctx; +#endif + static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; static void __iomem *clk_base; @@ -264,6 +312,15 @@ static DEFINE_SPINLOCK(clk_doubler_lock); static DEFINE_SPINLOCK(clk_out_lock); static DEFINE_SPINLOCK(sysrate_lock); +static struct div_nmp pllxc_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 20, + .divp_width = 4, +}; + static struct pdiv_map pllxc_p[] = { { .pdiv = 1, .hw_val = 0 }, { .pdiv = 2, .hw_val = 1 }, @@ -312,6 +369,16 @@ static struct tegra_clk_pll_params pll_c_params = { .stepa_shift = 17, .stepb_shift = 9, .pdiv_tohw = pllxc_p, + .div_nmp = &pllxc_nmp, +}; + +static struct div_nmp pllcx_nmp = { + .divm_shift = 0, + .divm_width = 2, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 20, + .divp_width = 3, }; static struct pdiv_map pllc_p[] = { @@ -345,6 +412,8 @@ static struct tegra_clk_pll_params pll_c2_params = { .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, .pdiv_tohw = pllc_p, + .div_nmp = &pllcx_nmp, + .max_p = 7, .ext_misc_reg[0] = 0x4f0, .ext_misc_reg[1] = 0x4f4, .ext_misc_reg[2] = 0x4f8, @@ -363,11 +432,25 @@ static struct tegra_clk_pll_params pll_c3_params = { .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, .pdiv_tohw = pllc_p, + .div_nmp = &pllcx_nmp, + .max_p = 7, .ext_misc_reg[0] = 0x504, .ext_misc_reg[1] = 0x508, .ext_misc_reg[2] = 0x50c, }; +static struct div_nmp pllm_nmp = { + .divm_shift = 0, + .divm_width = 8, + .override_divm_shift = 0, + .divn_shift = 8, + .divn_width = 8, + .override_divn_shift = 8, + .divp_shift = 20, + .divp_width = 1, + .override_divp_shift = 27, +}; + static struct pdiv_map pllm_p[] = { { .pdiv = 1, .hw_val = 0 }, { .pdiv = 2, .hw_val = 1 }, @@ -397,6 +480,18 @@ static struct tegra_clk_pll_params pll_m_params = { .lock_delay = 300, .max_p = 2, .pdiv_tohw = pllm_p, + .div_nmp = &pllm_nmp, + .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, + .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2, +}; + +static struct div_nmp pllp_nmp = { + .divm_shift = 0, + .divm_width = 5, + .divn_shift = 8, + .divn_width = 10, + .divp_shift = 20, + .divp_width = 3, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { @@ -420,6 +515,7 @@ static struct tegra_clk_pll_params pll_p_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { @@ -446,6 +542,7 @@ static struct tegra_clk_pll_params pll_a_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { @@ -481,6 +578,7 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_params pll_d2_params = { @@ -495,6 +593,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .div_nmp = &pllp_nmp, }; static struct pdiv_map pllu_p[] = { @@ -503,6 +602,15 @@ static struct pdiv_map pllu_p[] = { { .pdiv = 0, .hw_val = 0 }, }; +static struct div_nmp pllu_nmp = { + .divm_shift = 0, + .divm_width = 5, + .divn_shift = 8, + .divn_width = 10, + .divp_shift = 20, + .divp_width = 1, +}; + static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { {12000000, 480000000, 960, 12, 0, 12}, {13000000, 480000000, 960, 13, 0, 12}, @@ -525,6 +633,7 @@ static struct tegra_clk_pll_params pll_u_params = { .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, .pdiv_tohw = pllu_p, + .div_nmp = &pllu_nmp, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { @@ -557,6 +666,7 @@ static struct tegra_clk_pll_params pll_x_params = { .stepa_shift = 16, .stepb_shift = 24, .pdiv_tohw = pllxc_p, + .div_nmp = &pllxc_nmp, }; static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { @@ -566,6 +676,15 @@ static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { {0, 0, 0, 0, 0, 0}, }; +static struct div_nmp plle_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 24, + .divp_width = 4, +}; + static struct tegra_clk_pll_params pll_e_params = { .input_min = 12000000, .input_max = 1000000000, @@ -579,6 +698,16 @@ static struct tegra_clk_pll_params pll_e_params = { .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &plle_nmp, +}; + +static struct div_nmp pllre_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 16, + .divp_width = 4, }; static struct tegra_clk_pll_params pll_re_vco_params = { @@ -595,6 +724,7 @@ static struct tegra_clk_pll_params pll_re_vco_params = { .lock_delay = 300, .iddq_reg = PLLRE_MISC, .iddq_bit_idx = PLLRE_IDDQ_BIT, + .div_nmp = &pllre_nmp, }; /* Peripheral clock registers */ @@ -762,6 +892,7 @@ enum tegra114_clk { 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, + dfll_ref = 264, dfll_soc, /* Mux clocks */ @@ -1199,8 +1330,8 @@ static void __init tegra114_pll_init(void __iomem *clk_base, /* 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); + TEGRA_DIVIDER_ROUND_UP | TEGRA_DIVIDER_INT, 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, @@ -1435,7 +1566,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio0 */ clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, NULL); clks[audio0_mux] = clk; @@ -1447,7 +1579,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio1 */ clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, NULL); clks[audio1_mux] = clk; @@ -1459,7 +1592,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio2 */ clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, NULL); clks[audio2_mux] = clk; @@ -1471,7 +1605,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio3 */ clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, NULL); clks[audio3_mux] = clk; @@ -1483,7 +1618,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio4 */ clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, NULL); clks[audio4_mux] = clk; @@ -1495,7 +1631,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* spdif */ clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, NULL); clks[spdif_mux] = clk; @@ -1590,7 +1727,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) /* clk_out_1 */ clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out1_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, &clk_out_lock); clks[clk_out_1_mux] = clk; @@ -1602,7 +1740,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) /* clk_out_2 */ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out2_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, &clk_out_lock); clks[clk_out_2_mux] = clk; @@ -1614,7 +1753,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) /* clk_out_3 */ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out3_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, &clk_out_lock); clks[clk_out_3_mux] = clk; @@ -1625,6 +1765,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) clks[clk_out_3] = clk; /* blink */ + /* clear the blink timer register to directly output clk_32k */ + writel_relaxed(0, pmc_base + PMC_BLINK_TIMER); clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0, pmc_base + PMC_DPD_PADS_ORIDE, PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL); @@ -1637,7 +1779,7 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) } static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", - "pll_p_out3", "pll_p_out2", "unused", + "pll_p", "pll_p_out2", "unused", "clk_32k", "pll_m_out1" }; static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", @@ -1747,7 +1889,7 @@ static struct tegra_periph_init_data tegra_periph_clk_list[] = { 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("msenc", NULL, "msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, &periph_u_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), @@ -1764,6 +1906,8 @@ static struct tegra_periph_init_data tegra_periph_clk_list[] = { 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_MUX("dfll_ref", "ref", "t114_dfll", mux_pllp_clkm, CLK_SOURCE_DFLL_REF, 155, &periph_w_regs, TEGRA_PERIPH_ON_APB, dfll_ref), + TEGRA_INIT_DATA_MUX("dfll_soc", "soc", "t114_dfll", mux_pllp_clkm, CLK_SOURCE_DFLL_SOC, 155, &periph_w_regs, TEGRA_PERIPH_ON_APB, dfll_soc), 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), @@ -1928,7 +2072,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base) /* dsia */ clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, - ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), + CLK_SET_RATE_NO_REPARENT, 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, @@ -1938,7 +2083,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base) /* dsib */ clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0, - ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), + CLK_SET_RATE_NO_REPARENT, 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, @@ -1975,7 +2121,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base) /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, + ARRAY_SIZE(mux_pllmcp_clkm), + CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, 29, 3, 0, NULL); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, @@ -2000,14 +2147,66 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base) } } -static struct tegra_cpu_car_ops tegra114_cpu_car_ops; +/* Tegra114 CPU clock and reset control functions */ +static void tegra114_wait_cpu_in_reset(u32 cpu) +{ + unsigned int reg; + + do { + reg = readl(clk_base + CLK_RST_CONTROLLER_CPU_CMPLX_STATUS); + cpu_relax(); + } while (!(reg & (1 << cpu))); /* check CPU been reset or not */ +} +static void tegra114_disable_cpu_clock(u32 cpu) +{ + /* flow controller would take care in the power sequence. */ +} + +#ifdef CONFIG_PM_SLEEP +static void tegra114_cpu_clock_suspend(void) +{ + /* switch coresite to clk_m, save off original source */ + tegra114_cpu_clk_sctx.clk_csite_src = + readl(clk_base + CLK_SOURCE_CSITE); + writel(3 << 30, clk_base + CLK_SOURCE_CSITE); + + tegra114_cpu_clk_sctx.cclkg_burst = + readl(clk_base + CCLKG_BURST_POLICY); + tegra114_cpu_clk_sctx.cclkg_divider = + readl(clk_base + CCLKG_BURST_POLICY + 4); +} + +static void tegra114_cpu_clock_resume(void) +{ + writel(tegra114_cpu_clk_sctx.clk_csite_src, + clk_base + CLK_SOURCE_CSITE); + + writel(tegra114_cpu_clk_sctx.cclkg_burst, + clk_base + CCLKG_BURST_POLICY); + writel(tegra114_cpu_clk_sctx.cclkg_divider, + clk_base + CCLKG_BURST_POLICY + 4); +} +#endif + +static struct tegra_cpu_car_ops tegra114_cpu_car_ops = { + .wait_for_reset = tegra114_wait_cpu_in_reset, + .disable_clock = tegra114_disable_cpu_clock, +#ifdef CONFIG_PM_SLEEP + .suspend = tegra114_cpu_clock_suspend, + .resume = tegra114_cpu_clock_resume, +#endif +}; static const struct of_device_id pmc_match[] __initconst = { { .compatible = "nvidia,tegra114-pmc" }, {}, }; -static __initdata struct tegra_clk_init_table init_table[] = { +/* + * dfll_soc/dfll_ref apparently must be kept enabled, otherwise I2C5 + * breaks + */ +static struct tegra_clk_init_table init_table[] __initdata = { {uarta, pll_p, 408000000, 0}, {uartb, pll_p, 408000000, 0}, {uartc, pll_p, 408000000, 0}, @@ -2022,6 +2221,8 @@ static __initdata struct tegra_clk_init_table init_table[] = { {i2s2, pll_a_out0, 11289600, 0}, {i2s3, pll_a_out0, 11289600, 0}, {i2s4, pll_a_out0, 11289600, 0}, + {dfll_soc, pll_p, 51000000, 1}, + {dfll_ref, pll_p, 51000000, 1}, {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ }; @@ -2030,7 +2231,132 @@ 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) + +/** + * tegra114_car_barrier - wait for pending writes to the CAR to complete + * + * Wait for any outstanding writes to the CAR MMIO space from this CPU + * to complete before continuing execution. No return value. + */ +static void tegra114_car_barrier(void) +{ + wmb(); /* probably unnecessary */ + readl_relaxed(clk_base + CPU_FINETRIM_SELECT); +} + +/** + * tegra114_clock_tune_cpu_trimmers_high - use high-voltage propagation delays + * + * When the CPU rail voltage is in the high-voltage range, use the + * built-in hardwired clock propagation delays in the CPU clock + * shaper. No return value. + */ +void tegra114_clock_tune_cpu_trimmers_high(void) +{ + u32 select = 0; + + /* Use hardwired rise->rise & fall->fall clock propagation delays */ + select |= ~(CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 | + CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 | + CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6); + writel_relaxed(select, clk_base + CPU_FINETRIM_SELECT); + + tegra114_car_barrier(); +} +EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_high); + +/** + * tegra114_clock_tune_cpu_trimmers_low - use low-voltage propagation delays + * + * When the CPU rail voltage is in the low-voltage range, use the + * extended clock propagation delays set by + * tegra114_clock_tune_cpu_trimmers_init(). The intention is to + * maintain the input clock duty cycle that the FCPU subsystem + * expects. No return value. + */ +void tegra114_clock_tune_cpu_trimmers_low(void) +{ + u32 select = 0; + + /* + * Use software-specified rise->rise & fall->fall clock + * propagation delays (from + * tegra114_clock_tune_cpu_trimmers_init() + */ + select |= (CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 | + CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 | + CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6); + writel_relaxed(select, clk_base + CPU_FINETRIM_SELECT); + + tegra114_car_barrier(); +} +EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_low); + +/** + * tegra114_clock_tune_cpu_trimmers_init - set up and enable clk prop delays + * + * Program extended clock propagation delays into the FCPU clock + * shaper and enable them. XXX Define the purpose - peak current + * reduction? No return value. + */ +/* XXX Initial voltage rail state assumption issues? */ +void tegra114_clock_tune_cpu_trimmers_init(void) +{ + u32 dr = 0, r = 0; + + /* Increment the rise->rise clock delay by four steps */ + r |= (CPU_FINETRIM_R_FCPU_1_MASK | CPU_FINETRIM_R_FCPU_2_MASK | + CPU_FINETRIM_R_FCPU_3_MASK | CPU_FINETRIM_R_FCPU_4_MASK | + CPU_FINETRIM_R_FCPU_5_MASK | CPU_FINETRIM_R_FCPU_6_MASK); + writel_relaxed(r, clk_base + CPU_FINETRIM_R); + + /* + * Use the rise->rise clock propagation delay specified in the + * r field + */ + dr |= (CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 | + CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 | + CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6); + writel_relaxed(dr, clk_base + CPU_FINETRIM_DR); + + tegra114_clock_tune_cpu_trimmers_low(); +} +EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_init); + +/** + * tegra114_clock_assert_dfll_dvco_reset - assert the DFLL's DVCO reset + * + * Assert the reset line of the DFLL's DVCO. No return value. + */ +void tegra114_clock_assert_dfll_dvco_reset(void) +{ + u32 v; + + v = readl_relaxed(clk_base + RST_DFLL_DVCO); + v |= (1 << DVFS_DFLL_RESET_SHIFT); + writel_relaxed(v, clk_base + RST_DFLL_DVCO); + tegra114_car_barrier(); +} +EXPORT_SYMBOL(tegra114_clock_assert_dfll_dvco_reset); + +/** + * tegra114_clock_deassert_dfll_dvco_reset - deassert the DFLL's DVCO reset + * + * Deassert the reset line of the DFLL's DVCO, allowing the DVCO to + * operate. No return value. + */ +void tegra114_clock_deassert_dfll_dvco_reset(void) +{ + u32 v; + + v = readl_relaxed(clk_base + RST_DFLL_DVCO); + v &= ~(1 << DVFS_DFLL_RESET_SHIFT); + writel_relaxed(v, clk_base + RST_DFLL_DVCO); + tegra114_car_barrier(); +} +EXPORT_SYMBOL(tegra114_clock_deassert_dfll_dvco_reset); + +static void __init tegra114_clock_init(struct device_node *np) { struct device_node *node; int i; @@ -2083,3 +2409,4 @@ void __init tegra114_clock_init(struct device_node *np) tegra_cpu_car_ops = &tegra114_cpu_car_ops; } +CLK_OF_DECLARE(tegra114, "nvidia,tegra114-car", tegra114_clock_init); diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 075db0c99edb..056f649d0d89 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -778,7 +778,8 @@ static void __init tegra20_audio_clk_init(void) /* audio */ clk = clk_register_mux(NULL, "audio_mux", audio_parents, - ARRAY_SIZE(audio_parents), 0, + ARRAY_SIZE(audio_parents), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio", "audio_mux", 0, clk_base + AUDIO_SYNC_CLK, 4, @@ -941,7 +942,8 @@ static void __init tegra20_periph_clk_init(void) /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, + ARRAY_SIZE(mux_pllmcp_clkm), + CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, 30, 2, 0, NULL); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, @@ -1223,7 +1225,7 @@ static struct tegra_cpu_car_ops tegra20_cpu_car_ops = { #endif }; -static __initdata struct tegra_clk_init_table init_table[] = { +static struct tegra_clk_init_table init_table[] __initdata = { {pll_p, clk_max, 216000000, 1}, {pll_p_out1, clk_max, 28800000, 1}, {pll_p_out2, clk_max, 48000000, 1}, @@ -1287,7 +1289,7 @@ static const struct of_device_id pmc_match[] __initconst = { {}, }; -void __init tegra20_clock_init(struct device_node *np) +static void __init tegra20_clock_init(struct device_node *np) { int i; struct device_node *node; @@ -1339,3 +1341,4 @@ void __init tegra20_clock_init(struct device_node *np) tegra_cpu_car_ops = &tegra20_cpu_car_ops; } +CLK_OF_DECLARE(tegra20, "nvidia,tegra20-car", tegra20_clock_init); diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index c6921f538e28..dbe7c8003c5c 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -252,6 +252,9 @@ #define CLK_RESET_CCLK_RUN_POLICY 2 #define CLK_RESET_CCLK_BURST_POLICY_PLLX 8 +/* PLLM override registers */ +#define PMC_PLLM_WB0_OVERRIDE 0x1dc + #ifdef CONFIG_PM_SLEEP static struct cpu_clk_suspend_context { u32 pllx_misc; @@ -563,6 +566,18 @@ static struct tegra_clk_pll_params pll_c_params = { .lock_delay = 300, }; +static struct div_nmp pllm_nmp = { + .divn_shift = 8, + .divn_width = 10, + .override_divn_shift = 5, + .divm_shift = 0, + .divm_width = 5, + .override_divm_shift = 0, + .divp_shift = 20, + .divp_width = 3, + .override_divp_shift = 15, +}; + static struct tegra_clk_pll_params pll_m_params = { .input_min = 2000000, .input_max = 31000000, @@ -575,6 +590,9 @@ static struct tegra_clk_pll_params pll_m_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllm_nmp, + .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, + .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE, }; static struct tegra_clk_pll_params pll_p_params = { @@ -953,7 +971,7 @@ static void __init tegra30_pll_init(void) /* PLLU */ clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, pmc_base, 0, 0, &pll_u_params, TEGRA_PLLU | TEGRA_PLL_HAS_CPCON | - TEGRA_PLL_SET_LFCON | TEGRA_PLL_USE_LOCK, + TEGRA_PLL_SET_LFCON, pll_u_freq_table, NULL); clk_register_clkdev(clk, "pll_u", NULL); @@ -1008,7 +1026,8 @@ static void __init tegra30_pll_init(void) /* PLLE */ clk = clk_register_mux(NULL, "pll_e_mux", pll_e_parents, - ARRAY_SIZE(pll_e_parents), 0, + ARRAY_SIZE(pll_e_parents), + CLK_SET_RATE_NO_REPARENT, clk_base + PLLE_AUX, 2, 1, 0, NULL); clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base, CLK_GET_RATE_NOCACHE, 100000000, &pll_e_params, @@ -1068,7 +1087,8 @@ static void __init tegra30_audio_clk_init(void) /* audio0 */ clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S0, 4, @@ -1078,7 +1098,8 @@ static void __init tegra30_audio_clk_init(void) /* audio1 */ clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S1, 4, @@ -1088,7 +1109,8 @@ static void __init tegra30_audio_clk_init(void) /* audio2 */ clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S2, 4, @@ -1098,7 +1120,8 @@ static void __init tegra30_audio_clk_init(void) /* audio3 */ clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S3, 4, @@ -1108,7 +1131,8 @@ static void __init tegra30_audio_clk_init(void) /* audio4 */ clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S4, 4, @@ -1118,7 +1142,8 @@ static void __init tegra30_audio_clk_init(void) /* spdif */ clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0, clk_base + AUDIO_SYNC_CLK_SPDIF, 4, @@ -1211,7 +1236,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_1 */ clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out1_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, &clk_out_lock); clks[clk_out_1_mux] = clk; @@ -1223,7 +1249,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_2 */ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out2_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, &clk_out_lock); clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, @@ -1234,7 +1261,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_3 */ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out3_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, &clk_out_lock); clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, @@ -1598,6 +1626,12 @@ static void __init tegra30_periph_clk_init(void) clk_register_clkdev(clk, "afi", "tegra-pcie"); clks[afi] = clk; + /* pciex */ + clk = tegra_clk_register_periph_gate("pciex", "pll_e", 0, clk_base, 0, + 74, &periph_u_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "pciex", "tegra-pcie"); + clks[pciex] = clk; + /* kfuse */ clk = tegra_clk_register_periph_gate("kfuse", "clk_m", TEGRA_PERIPH_ON_APB, @@ -1655,7 +1689,8 @@ static void __init tegra30_periph_clk_init(void) /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, + ARRAY_SIZE(mux_pllmcp_clkm), + CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, 30, 2, 0, NULL); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, @@ -1716,11 +1751,6 @@ static void __init tegra30_fixed_clk_init(void) 1, 0, &cml_lock); clk_register_clkdev(clk, "cml1", NULL); clks[cml1] = clk; - - /* pciex */ - clk = clk_register_fixed_rate(NULL, "pciex", "pll_e", 0, 100000000); - clk_register_clkdev(clk, "pciex", NULL); - clks[pciex] = clk; } static void __init tegra30_osc_clk_init(void) @@ -1882,7 +1912,7 @@ static struct tegra_cpu_car_ops tegra30_cpu_car_ops = { #endif }; -static __initdata struct tegra_clk_init_table init_table[] = { +static struct tegra_clk_init_table init_table[] __initdata = { {uarta, pll_p, 408000000, 0}, {uartb, pll_p, 408000000, 0}, {uartc, pll_p, 408000000, 0}, @@ -1953,7 +1983,7 @@ static const struct of_device_id pmc_match[] __initconst = { {}, }; -void __init tegra30_clock_init(struct device_node *np) +static void __init tegra30_clock_init(struct device_node *np) { struct device_node *node; int i; @@ -2004,3 +2034,4 @@ void __init tegra30_clock_init(struct device_node *np) tegra_cpu_car_ops = &tegra30_cpu_car_ops; } +CLK_OF_DECLARE(tegra30, "nvidia,tegra30-car", tegra30_clock_init); diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 923ca7ee4694..86581ac1fd69 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -74,18 +74,6 @@ 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 }, - { } -}; - -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) diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index e0565620d68e..07cfacd91686 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -128,6 +128,31 @@ struct pdiv_map { }; /** + * struct div_nmp - offset and width of m,n and p fields + * + * @divn_shift: shift to the feedback divider bit field + * @divn_width: width of the feedback divider bit field + * @divm_shift: shift to the input divider bit field + * @divm_width: width of the input divider bit field + * @divp_shift: shift to the post divider bit field + * @divp_width: width of the post divider bit field + * @override_divn_shift: shift to the feedback divider bitfield in override reg + * @override_divm_shift: shift to the input divider bitfield in override reg + * @override_divp_shift: shift to the post divider bitfield in override reg + */ +struct div_nmp { + u8 divn_shift; + u8 divn_width; + u8 divm_shift; + u8 divm_width; + u8 divp_shift; + u8 divp_width; + u8 override_divn_shift; + u8 override_divm_shift; + u8 override_divp_shift; +}; + +/** * struct clk_pll_params - PLL parameters * * @input_min: Minimum input frequency @@ -161,11 +186,14 @@ struct tegra_clk_pll_params { u32 aux_reg; u32 dyn_ramp_reg; u32 ext_misc_reg[3]; + u32 pmc_divnm_reg; + u32 pmc_divp_reg; int stepa_shift; int stepb_shift; int lock_delay; int max_p; struct pdiv_map *pdiv_tohw; + struct div_nmp *div_nmp; }; /** @@ -179,12 +207,6 @@ struct tegra_clk_pll_params { * @flags: PLL flags * @fixed_rate: PLL rate if it is fixed * @lock: register lock - * @divn_shift: shift to the feedback divider bit field - * @divn_width: width of the feedback divider bit field - * @divm_shift: shift to the input divider bit field - * @divm_width: width of the input divider bit field - * @divp_shift: shift to the post divider bit field - * @divp_width: width of the post divider bit field * * Flags: * TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for @@ -214,12 +236,6 @@ struct tegra_clk_pll { u32 flags; unsigned long fixed_rate; spinlock_t *lock; - u8 divn_shift; - u8 divn_width; - u8 divm_shift; - u8 divm_width; - u8 divp_shift; - u8 divp_width; struct tegra_clk_pll_freq_table *freq_table; struct tegra_clk_pll_params *params; }; @@ -571,23 +587,11 @@ void tegra_init_from_table(struct tegra_clk_init_table *tbl, void tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, struct clk *clks[], int clk_max); -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -void tegra20_clock_init(struct device_node *np); -#else -static inline void tegra20_clock_init(struct device_node *np) {} -#endif /* CONFIG_ARCH_TEGRA_2x_SOC */ - -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -void tegra30_clock_init(struct device_node *np); -#else -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 */ +void tegra114_clock_tune_cpu_trimmers_high(void); +void tegra114_clock_tune_cpu_trimmers_low(void); +void tegra114_clock_tune_cpu_trimmers_init(void); +void tegra114_clock_assert_dfll_dvco_reset(void); +void tegra114_clock_deassert_dfll_dvco_reset(void); typedef void (*tegra_clk_apply_init_table_func)(void); extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c index a0fca004abc1..e7bd62cf60b3 100644 --- a/drivers/clk/ux500/abx500-clk.c +++ b/drivers/clk/ux500/abx500-clk.c @@ -45,7 +45,7 @@ static int ab8500_reg_clks(struct device *dev) 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", "snd-soc-mop500.0"); clk_register_clkdev(clk, "sysclk", "shrm_bus"); /* ab8500_sysclk2 */ @@ -70,19 +70,19 @@ static int ab8500_reg_clks(struct device *dev) AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, 38400000, 9000, CLK_IS_ROOT); - clk_register_clkdev(clk, "ulpclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "ulpclk", "snd-soc-mop500.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, "intclk", "snd-soc-mop500.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"); + clk_register_clkdev(clk, "audioclk", "ab8500-codec.0"); return 0; } diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index 10adfd2ead21..f26258869deb 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c @@ -12,10 +12,568 @@ #include <linux/clk-provider.h> #include <linux/mfd/dbx500-prcmu.h> #include <linux/platform_data/clk-ux500.h> - #include "clk.h" -void u8540_clk_init(void) +void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { - /* register clocks here */ + struct clk *clk; + + /* Clock sources. */ + /* Fixed ClockGen */ + clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "soc0_pll", NULL); + + clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "soc1_pll", NULL); + + clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "ddr_pll", NULL); + + clk = clk_register_fixed_rate(NULL, "rtc32k", NULL, + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 32768); + clk_register_clkdev(clk, "clk32k", NULL); + clk_register_clkdev(clk, "apb_pclk", "rtc-pl031"); + + clk = clk_register_fixed_rate(NULL, "ulp38m4", NULL, + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 38400000); + + clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "UART"); + + /* msp02clk needs a abx500 clk as parent. Handle by abx500 clk driver */ + clk = clk_reg_prcmu_gate("msp02clk", "ab9540_sysclk12_b1", + PRCMU_MSP02CLK, 0); + clk_register_clkdev(clk, NULL, "MSP02"); + + clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "MSP1"); + + clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "I2C"); + + clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "slim"); + + clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH1"); + + clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH2"); + + clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH3"); + + clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH5"); + + clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH6"); + + clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH7"); + + clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "lcd"); + clk_register_clkdev(clk, "lcd", "mcde"); + + clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BML8580CLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "bml"); + + clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + + clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + + clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "hdmi"); + clk_register_clkdev(clk, "hdmi", "mcde"); + + clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "apeat"); + + clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "apetrace"); + + clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mcde"); + clk_register_clkdev(clk, "mcde", "mcde"); + clk_register_clkdev(clk, NULL, "dsilink.0"); + clk_register_clkdev(clk, NULL, "dsilink.1"); + clk_register_clkdev(clk, NULL, "dsilink.2"); + + clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "ipi2"); + + clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "dsialt"); + + clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "dma40.0"); + + clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "b2r2"); + clk_register_clkdev(clk, NULL, "b2r2_core"); + clk_register_clkdev(clk, NULL, "U8500-B2R2.0"); + clk_register_clkdev(clk, NULL, "b2r2_1_core"); + + clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "tv"); + clk_register_clkdev(clk, "tv", "mcde"); + + clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "SSP"); + + clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "rngclk"); + + clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "uicc"); + + clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mtu0"); + clk_register_clkdev(clk, NULL, "mtu1"); + + clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, + PRCMU_SDMMCCLK, 100000000, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdmmc"); + + clk = clk_reg_prcmu_opp_volt_scalable("sdmmchclk", NULL, + PRCMU_SDMMCHCLK, 400000000, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdmmchclk"); + + clk = clk_reg_prcmu_gate("hvaclk", NULL, PRCMU_HVACLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "hva"); + + clk = clk_reg_prcmu_gate("g1clk", NULL, PRCMU_G1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "g1"); + + clk = clk_reg_prcmu_scalable("spare1clk", NULL, PRCMU_SPARE1CLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilcd", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk", + PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs2", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.2"); + + clk = clk_reg_prcmu_scalable("dsilcd_pll", "spare1clk", + PRCMU_PLLDSI_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilcd_pll", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll", + PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs0", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi0lcdclk", "dsilcd_pll", + PRCMU_DSI0CLK_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs0", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.0"); + + clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll", + PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs1", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi1lcdclk", "dsilcd_pll", + PRCMU_DSI1CLK_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs1", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.1"); + + clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk", + PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.0"); + clk_register_clkdev(clk, "dsilp0", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk", + PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.1"); + clk_register_clkdev(clk, "dsilp1", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk", + PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.2"); + clk_register_clkdev(clk, "dsilp2", "mcde"); + + clk = clk_reg_prcmu_scalable_rate("armss", NULL, + PRCMU_ARMSS, 0, CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "armss", NULL); + + clk = clk_register_fixed_factor(NULL, "smp_twd", "armss", + CLK_IGNORE_UNUSED, 1, 2); + clk_register_clkdev(clk, NULL, "smp_twd"); + + /* PRCC P-clocks */ + /* Peripheral 1 : PRCC P-clocks */ + 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", clkrst1_base, + BIT(1), 0); + clk_register_clkdev(clk, "apb_pclk", "uart1"); + + 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", clkrst1_base, + BIT(3), 0); + clk_register_clkdev(clk, "apb_pclk", "msp0"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.0"); + + 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", "dbx5x0-msp-i2s.1"); + + 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", clkrst1_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.2"); + + 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", clkrst1_base, + BIT(8), 0); + clk_register_clkdev(clk, "apb_pclk", "slimbus0"); + + 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_register_clkdev(clk, "apb_pclk", "ab85xx-codec.0"); + + 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", clkrst1_base, + BIT(11), 0); + clk_register_clkdev(clk, "apb_pclk", "msp3"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.3"); + + /* Peripheral 2 : PRCC P-clocks */ + 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", clkrst2_base, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "spi2"); + + 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", clkrst2_base, + BIT(3), 0); + clk_register_clkdev(clk, NULL, "pwl"); + + 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", clkrst2_base, + BIT(5), 0); + clk_register_clkdev(clk, "apb_pclk", "msp2"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.2"); + + 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", clkrst2_base, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi3"); + + 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", clkrst2_base, + BIT(9), 0); + clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0"); + + 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", 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", clkrst2_base, + BIT(12), 0); + clk_register_clkdev(clk, "msp4-pclk", "ab85xx-codec.0"); + + /* Peripheral 3 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", clkrst3_base, + BIT(0), 0); + clk_register_clkdev(clk, NULL, "fsmc"); + + 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", clkrst3_base, + BIT(2), 0); + clk_register_clkdev(clk, "apb_pclk", "ssp1"); + + 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", clkrst3_base, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi2"); + + 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", clkrst3_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "uart2"); + + 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", clkrst3_base, + BIT(8), 0); + clk_register_clkdev(clk, NULL, "gpio.2"); + clk_register_clkdev(clk, NULL, "gpio.3"); + clk_register_clkdev(clk, NULL, "gpio.4"); + clk_register_clkdev(clk, NULL, "gpio.5"); + clk_register_clkdev(clk, NULL, "gpioblock2"); + + clk = clk_reg_prcc_pclk("p3_pclk9", "per3clk", clkrst3_base, + BIT(9), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.5"); + + clk = clk_reg_prcc_pclk("p3_pclk10", "per3clk", clkrst3_base, + BIT(10), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.6"); + + clk = clk_reg_prcc_pclk("p3_pclk11", "per3clk", clkrst3_base, + BIT(11), 0); + clk_register_clkdev(clk, "apb_pclk", "uart3"); + + clk = clk_reg_prcc_pclk("p3_pclk12", "per3clk", clkrst3_base, + BIT(12), 0); + clk_register_clkdev(clk, "apb_pclk", "uart4"); + + /* Peripheral 5 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", clkrst5_base, + BIT(0), 0); + clk_register_clkdev(clk, "usb", "musb-ux500.0"); + clk_register_clkdev(clk, "usbclk", "ab-iddet.0"); + + 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"); + + /* Peripheral 6 : PRCC P-clocks */ + 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", 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", clkrst6_base, + BIT(2), 0); + clk_register_clkdev(clk, NULL, "hash0"); + + 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", clkrst6_base, + BIT(4), 0); + clk_register_clkdev(clk, NULL, "db8540-hash1"); + + 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", clkrst6_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "mtu0"); + + clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", clkrst6_base, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "mtu1"); + + /* + * PRCC K-clocks ==> see table PRCC_PCKEN/PRCC_KCKEN + * This differs from the internal implementation: + * We don't use the PERPIH[n| clock as parent, since those _should_ + * only be used as parents for the P-clocks. + * TODO: "parentjoin" with corresponding P-clocks for all K-clocks. + */ + + /* Peripheral 1 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk", + clkrst1_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart0"); + + clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk", + clkrst1_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart1"); + + clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk", + 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", + clkrst1_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp0"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.0"); + + clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk", + clkrst1_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp1"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.1"); + + clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmchclk", + clkrst1_base, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi0"); + + clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk", + 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", + clkrst1_base, BIT(8), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "slimbus0"); + + clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk", + 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", + clkrst1_base, BIT(10), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp3"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.3"); + + /* Peripheral 2 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk", + clkrst2_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.3"); + + clk = clk_reg_prcc_kclk("p2_pwl_kclk", "rtc32k", + clkrst2_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "pwl"); + + clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmchclk", + clkrst2_base, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi4"); + + clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk", + clkrst2_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp2"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.2"); + + clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmchclk", + clkrst2_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi1"); + + clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk", + clkrst2_base, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi3"); + + clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk", + clkrst2_base, BIT(6), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + clk_register_clkdev(clk, "hsir_hsirxclk", "ste_hsi.0"); + + clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk", + clkrst2_base, BIT(7), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + clk_register_clkdev(clk, "hsit_hsitxclk", "ste_hsi.0"); + + /* Should only be 9540, but might be added for 85xx as well */ + clk = clk_reg_prcc_kclk("p2_msp4_kclk", "msp02clk", + clkrst2_base, BIT(9), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp4"); + clk_register_clkdev(clk, "msp4", "ab85xx-codec.0"); + + /* Peripheral 3 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk", + clkrst3_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "ssp0"); + + clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk", + clkrst3_base, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "ssp1"); + + clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk", + clkrst3_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.0"); + + clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmchclk", + clkrst3_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi2"); + + clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k", + 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", + clkrst3_base, BIT(6), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart2"); + + clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk", + clkrst3_base, BIT(7), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi5"); + + clk = clk_reg_prcc_kclk("p3_i2c5_kclk", "i2cclk", + clkrst3_base, BIT(8), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.5"); + + clk = clk_reg_prcc_kclk("p3_i2c6_kclk", "i2cclk", + clkrst3_base, BIT(9), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.6"); + + clk = clk_reg_prcc_kclk("p3_uart3_kclk", "uartclk", + clkrst3_base, BIT(10), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart3"); + + clk = clk_reg_prcc_kclk("p3_uart4_kclk", "uartclk", + clkrst3_base, BIT(11), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart4"); + + /* Peripheral 6 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p6_rng_kclk", "rngclk", + clkrst6_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "rng"); } diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c index dbc0191e16c8..44794782e7e0 100644 --- a/drivers/clk/ux500/u9540_clk.c +++ b/drivers/clk/ux500/u9540_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 "clk.h" -void u9540_clk_init(void) +void u9540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { /* register clocks here */ } diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c index 256c8be74df8..2dc8b41a339d 100644 --- a/drivers/clk/versatile/clk-vexpress-osc.c +++ b/drivers/clk/versatile/clk-vexpress-osc.c @@ -107,7 +107,7 @@ void __init vexpress_osc_of_setup(struct device_node *node) osc->func = vexpress_config_func_get_by_node(node); if (!osc->func) { pr_err("Failed to obtain config func for node '%s'!\n", - node->name); + node->full_name); goto error; } @@ -119,7 +119,7 @@ void __init vexpress_osc_of_setup(struct device_node *node) of_property_read_string(node, "clock-output-names", &init.name); if (!init.name) - init.name = node->name; + init.name = node->full_name; init.ops = &vexpress_osc_ops; init.flags = CLK_IS_ROOT; diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c index a4a728d05092..2d5e1b4820e0 100644 --- a/drivers/clk/versatile/clk-vexpress.c +++ b/drivers/clk/versatile/clk-vexpress.c @@ -37,8 +37,8 @@ static void __init vexpress_sp810_init(void __iomem *base) snprintf(name, ARRAY_SIZE(name), "timerclken%d", i); vexpress_sp810_timerclken[i] = clk_register_mux(NULL, name, - parents, 2, 0, base + SCCTRL, - SCCTRL_TIMERENnSEL_SHIFT(i), 1, + parents, 2, CLK_SET_RATE_NO_REPARENT, + base + SCCTRL, SCCTRL_TIMERENnSEL_SHIFT(i), 1, 0, &vexpress_sp810_lock); if (WARN_ON(IS_ERR(vexpress_sp810_timerclken[i]))) diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c index 4f45eee9e33b..812f83f8b0c6 100644 --- a/drivers/clk/x86/clk-lpt.c +++ b/drivers/clk/x86/clk-lpt.c @@ -1,5 +1,5 @@ /* - * Intel Lynxpoint LPSS clocks. + * Intel Low Power Subsystem clocks. * * Copyright (C) 2013, Intel Corporation * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> @@ -18,8 +18,6 @@ #include <linux/platform_data/clk-lpss.h> #include <linux/platform_device.h> -#define PRV_CLOCK_PARAMS 0x800 - static int lpt_clk_probe(struct platform_device *pdev) { struct lpss_clk_data *drvdata; diff --git a/drivers/clk/zynq/Makefile b/drivers/clk/zynq/Makefile new file mode 100644 index 000000000000..156d923f4fa9 --- /dev/null +++ b/drivers/clk/zynq/Makefile @@ -0,0 +1,3 @@ +# Zynq clock specific Makefile + +obj-$(CONFIG_ARCH_ZYNQ) += clkc.o pll.o diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c new file mode 100644 index 000000000000..cc40fe64f2dc --- /dev/null +++ b/drivers/clk/zynq/clkc.c @@ -0,0 +1,550 @@ +/* + * Zynq clock controller + * + * Copyright (C) 2012 - 2013 Xilinx + * + * Sören Brinkmann <soren.brinkmann@xilinx.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 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. + * + * 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/zynq.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/io.h> + +static void __iomem *zynq_slcr_base_priv; + +#define SLCR_ARMPLL_CTRL (zynq_slcr_base_priv + 0x100) +#define SLCR_DDRPLL_CTRL (zynq_slcr_base_priv + 0x104) +#define SLCR_IOPLL_CTRL (zynq_slcr_base_priv + 0x108) +#define SLCR_PLL_STATUS (zynq_slcr_base_priv + 0x10c) +#define SLCR_ARM_CLK_CTRL (zynq_slcr_base_priv + 0x120) +#define SLCR_DDR_CLK_CTRL (zynq_slcr_base_priv + 0x124) +#define SLCR_DCI_CLK_CTRL (zynq_slcr_base_priv + 0x128) +#define SLCR_APER_CLK_CTRL (zynq_slcr_base_priv + 0x12c) +#define SLCR_GEM0_CLK_CTRL (zynq_slcr_base_priv + 0x140) +#define SLCR_GEM1_CLK_CTRL (zynq_slcr_base_priv + 0x144) +#define SLCR_SMC_CLK_CTRL (zynq_slcr_base_priv + 0x148) +#define SLCR_LQSPI_CLK_CTRL (zynq_slcr_base_priv + 0x14c) +#define SLCR_SDIO_CLK_CTRL (zynq_slcr_base_priv + 0x150) +#define SLCR_UART_CLK_CTRL (zynq_slcr_base_priv + 0x154) +#define SLCR_SPI_CLK_CTRL (zynq_slcr_base_priv + 0x158) +#define SLCR_CAN_CLK_CTRL (zynq_slcr_base_priv + 0x15c) +#define SLCR_CAN_MIOCLK_CTRL (zynq_slcr_base_priv + 0x160) +#define SLCR_DBG_CLK_CTRL (zynq_slcr_base_priv + 0x164) +#define SLCR_PCAP_CLK_CTRL (zynq_slcr_base_priv + 0x168) +#define SLCR_FPGA0_CLK_CTRL (zynq_slcr_base_priv + 0x170) +#define SLCR_621_TRUE (zynq_slcr_base_priv + 0x1c4) +#define SLCR_SWDT_CLK_SEL (zynq_slcr_base_priv + 0x304) + +#define NUM_MIO_PINS 54 + +enum zynq_clk { + armpll, ddrpll, iopll, + cpu_6or4x, cpu_3or2x, cpu_2x, cpu_1x, + ddr2x, ddr3x, dci, + lqspi, smc, pcap, gem0, gem1, fclk0, fclk1, fclk2, fclk3, can0, can1, + sdio0, sdio1, uart0, uart1, spi0, spi1, dma, + usb0_aper, usb1_aper, gem0_aper, gem1_aper, + sdio0_aper, sdio1_aper, spi0_aper, spi1_aper, can0_aper, can1_aper, + i2c0_aper, i2c1_aper, uart0_aper, uart1_aper, gpio_aper, lqspi_aper, + smc_aper, swdt, dbg_trc, dbg_apb, clk_max}; + +static struct clk *ps_clk; +static struct clk *clks[clk_max]; +static struct clk_onecell_data clk_data; + +static DEFINE_SPINLOCK(armpll_lock); +static DEFINE_SPINLOCK(ddrpll_lock); +static DEFINE_SPINLOCK(iopll_lock); +static DEFINE_SPINLOCK(armclk_lock); +static DEFINE_SPINLOCK(swdtclk_lock); +static DEFINE_SPINLOCK(ddrclk_lock); +static DEFINE_SPINLOCK(dciclk_lock); +static DEFINE_SPINLOCK(gem0clk_lock); +static DEFINE_SPINLOCK(gem1clk_lock); +static DEFINE_SPINLOCK(canclk_lock); +static DEFINE_SPINLOCK(canmioclk_lock); +static DEFINE_SPINLOCK(dbgclk_lock); +static DEFINE_SPINLOCK(aperclk_lock); + +static const char dummy_nm[] __initconst = "dummy_name"; + +static const char *armpll_parents[] __initdata = {"armpll_int", "ps_clk"}; +static const char *ddrpll_parents[] __initdata = {"ddrpll_int", "ps_clk"}; +static const char *iopll_parents[] __initdata = {"iopll_int", "ps_clk"}; +static const char *gem0_mux_parents[] __initdata = {"gem0_div1", dummy_nm}; +static const char *gem1_mux_parents[] __initdata = {"gem1_div1", dummy_nm}; +static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate", + "can0_mio_mux"}; +static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate", + "can1_mio_mux"}; +static const char *dbg_emio_mux_parents[] __initdata = {"dbg_div", + dummy_nm}; + +static const char *dbgtrc_emio_input_names[] __initdata = {"trace_emio_clk"}; +static const char *gem0_emio_input_names[] __initdata = {"gem0_emio_clk"}; +static const char *gem1_emio_input_names[] __initdata = {"gem1_emio_clk"}; +static const char *swdt_ext_clk_input_names[] __initdata = {"swdt_ext_clk"}; + +static void __init zynq_clk_register_fclk(enum zynq_clk fclk, + const char *clk_name, void __iomem *fclk_ctrl_reg, + const char **parents) +{ + struct clk *clk; + char *mux_name; + char *div0_name; + char *div1_name; + spinlock_t *fclk_lock; + spinlock_t *fclk_gate_lock; + void __iomem *fclk_gate_reg = fclk_ctrl_reg + 8; + + fclk_lock = kmalloc(sizeof(*fclk_lock), GFP_KERNEL); + if (!fclk_lock) + goto err; + fclk_gate_lock = kmalloc(sizeof(*fclk_gate_lock), GFP_KERNEL); + if (!fclk_gate_lock) + goto err; + spin_lock_init(fclk_lock); + spin_lock_init(fclk_gate_lock); + + mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name); + div0_name = kasprintf(GFP_KERNEL, "%s_div0", clk_name); + div1_name = kasprintf(GFP_KERNEL, "%s_div1", clk_name); + + clk = clk_register_mux(NULL, mux_name, parents, 4, + CLK_SET_RATE_NO_REPARENT, fclk_ctrl_reg, 4, 2, 0, + fclk_lock); + + clk = clk_register_divider(NULL, div0_name, mux_name, + 0, fclk_ctrl_reg, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, fclk_lock); + + clk = clk_register_divider(NULL, div1_name, div0_name, + CLK_SET_RATE_PARENT, fclk_ctrl_reg, 20, 6, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + fclk_lock); + + clks[fclk] = clk_register_gate(NULL, clk_name, + div1_name, CLK_SET_RATE_PARENT, fclk_gate_reg, + 0, CLK_GATE_SET_TO_DISABLE, fclk_gate_lock); + kfree(mux_name); + kfree(div0_name); + kfree(div1_name); + + return; + +err: + clks[fclk] = ERR_PTR(-ENOMEM); +} + +static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0, + enum zynq_clk clk1, const char *clk_name0, + const char *clk_name1, void __iomem *clk_ctrl, + const char **parents, unsigned int two_gates) +{ + struct clk *clk; + char *mux_name; + char *div_name; + spinlock_t *lock; + + lock = kmalloc(sizeof(*lock), GFP_KERNEL); + if (!lock) + goto err; + spin_lock_init(lock); + + mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name0); + div_name = kasprintf(GFP_KERNEL, "%s_div", clk_name0); + + clk = clk_register_mux(NULL, mux_name, parents, 4, + CLK_SET_RATE_NO_REPARENT, clk_ctrl, 4, 2, 0, lock); + + clk = clk_register_divider(NULL, div_name, mux_name, 0, clk_ctrl, 8, 6, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, lock); + + clks[clk0] = clk_register_gate(NULL, clk_name0, div_name, + CLK_SET_RATE_PARENT, clk_ctrl, 0, 0, lock); + if (two_gates) + clks[clk1] = clk_register_gate(NULL, clk_name1, div_name, + CLK_SET_RATE_PARENT, clk_ctrl, 1, 0, lock); + + kfree(mux_name); + kfree(div_name); + + return; + +err: + clks[clk0] = ERR_PTR(-ENOMEM); + if (two_gates) + clks[clk1] = ERR_PTR(-ENOMEM); +} + +static void __init zynq_clk_setup(struct device_node *np) +{ + int i; + u32 tmp; + int ret; + struct clk *clk; + char *clk_name; + const char *clk_output_name[clk_max]; + const char *cpu_parents[4]; + const char *periph_parents[4]; + const char *swdt_ext_clk_mux_parents[2]; + const char *can_mio_mux_parents[NUM_MIO_PINS]; + + pr_info("Zynq clock init\n"); + + /* get clock output names from DT */ + for (i = 0; i < clk_max; i++) { + if (of_property_read_string_index(np, "clock-output-names", + i, &clk_output_name[i])) { + pr_err("%s: clock output name not in DT\n", __func__); + BUG(); + } + } + cpu_parents[0] = clk_output_name[armpll]; + cpu_parents[1] = clk_output_name[armpll]; + cpu_parents[2] = clk_output_name[ddrpll]; + cpu_parents[3] = clk_output_name[iopll]; + periph_parents[0] = clk_output_name[iopll]; + periph_parents[1] = clk_output_name[iopll]; + periph_parents[2] = clk_output_name[armpll]; + periph_parents[3] = clk_output_name[ddrpll]; + + /* ps_clk */ + ret = of_property_read_u32(np, "ps-clk-frequency", &tmp); + if (ret) { + pr_warn("ps_clk frequency not specified, using 33 MHz.\n"); + tmp = 33333333; + } + ps_clk = clk_register_fixed_rate(NULL, "ps_clk", NULL, CLK_IS_ROOT, + tmp); + + /* PLLs */ + clk = clk_register_zynq_pll("armpll_int", "ps_clk", SLCR_ARMPLL_CTRL, + SLCR_PLL_STATUS, 0, &armpll_lock); + clks[armpll] = clk_register_mux(NULL, clk_output_name[armpll], + armpll_parents, 2, CLK_SET_RATE_NO_REPARENT, + SLCR_ARMPLL_CTRL, 4, 1, 0, &armpll_lock); + + clk = clk_register_zynq_pll("ddrpll_int", "ps_clk", SLCR_DDRPLL_CTRL, + SLCR_PLL_STATUS, 1, &ddrpll_lock); + clks[ddrpll] = clk_register_mux(NULL, clk_output_name[ddrpll], + ddrpll_parents, 2, CLK_SET_RATE_NO_REPARENT, + SLCR_DDRPLL_CTRL, 4, 1, 0, &ddrpll_lock); + + clk = clk_register_zynq_pll("iopll_int", "ps_clk", SLCR_IOPLL_CTRL, + SLCR_PLL_STATUS, 2, &iopll_lock); + clks[iopll] = clk_register_mux(NULL, clk_output_name[iopll], + iopll_parents, 2, CLK_SET_RATE_NO_REPARENT, + SLCR_IOPLL_CTRL, 4, 1, 0, &iopll_lock); + + /* CPU clocks */ + tmp = readl(SLCR_621_TRUE) & 1; + clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_ARM_CLK_CTRL, 4, 2, 0, + &armclk_lock); + clk = clk_register_divider(NULL, "cpu_div", "cpu_mux", 0, + SLCR_ARM_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, &armclk_lock); + + clks[cpu_6or4x] = clk_register_gate(NULL, clk_output_name[cpu_6or4x], + "cpu_div", CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + SLCR_ARM_CLK_CTRL, 24, 0, &armclk_lock); + + clk = clk_register_fixed_factor(NULL, "cpu_3or2x_div", "cpu_div", 0, + 1, 2); + clks[cpu_3or2x] = clk_register_gate(NULL, clk_output_name[cpu_3or2x], + "cpu_3or2x_div", CLK_IGNORE_UNUSED, + SLCR_ARM_CLK_CTRL, 25, 0, &armclk_lock); + + clk = clk_register_fixed_factor(NULL, "cpu_2x_div", "cpu_div", 0, 1, + 2 + tmp); + clks[cpu_2x] = clk_register_gate(NULL, clk_output_name[cpu_2x], + "cpu_2x_div", CLK_IGNORE_UNUSED, SLCR_ARM_CLK_CTRL, + 26, 0, &armclk_lock); + + clk = clk_register_fixed_factor(NULL, "cpu_1x_div", "cpu_div", 0, 1, + 4 + 2 * tmp); + clks[cpu_1x] = clk_register_gate(NULL, clk_output_name[cpu_1x], + "cpu_1x_div", CLK_IGNORE_UNUSED, SLCR_ARM_CLK_CTRL, 27, + 0, &armclk_lock); + + /* Timers */ + swdt_ext_clk_mux_parents[0] = clk_output_name[cpu_1x]; + for (i = 0; i < ARRAY_SIZE(swdt_ext_clk_input_names); i++) { + int idx = of_property_match_string(np, "clock-names", + swdt_ext_clk_input_names[i]); + if (idx >= 0) + swdt_ext_clk_mux_parents[i + 1] = + of_clk_get_parent_name(np, idx); + else + swdt_ext_clk_mux_parents[i + 1] = dummy_nm; + } + clks[swdt] = clk_register_mux(NULL, clk_output_name[swdt], + swdt_ext_clk_mux_parents, 2, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_SWDT_CLK_SEL, 0, 1, 0, + &swdtclk_lock); + + /* DDR clocks */ + clk = clk_register_divider(NULL, "ddr2x_div", "ddrpll", 0, + SLCR_DDR_CLK_CTRL, 26, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, &ddrclk_lock); + clks[ddr2x] = clk_register_gate(NULL, clk_output_name[ddr2x], + "ddr2x_div", 0, SLCR_DDR_CLK_CTRL, 1, 0, &ddrclk_lock); + clk_prepare_enable(clks[ddr2x]); + clk = clk_register_divider(NULL, "ddr3x_div", "ddrpll", 0, + SLCR_DDR_CLK_CTRL, 20, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, &ddrclk_lock); + clks[ddr3x] = clk_register_gate(NULL, clk_output_name[ddr3x], + "ddr3x_div", 0, SLCR_DDR_CLK_CTRL, 0, 0, &ddrclk_lock); + clk_prepare_enable(clks[ddr3x]); + + clk = clk_register_divider(NULL, "dci_div0", "ddrpll", 0, + SLCR_DCI_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, &dciclk_lock); + clk = clk_register_divider(NULL, "dci_div1", "dci_div0", + CLK_SET_RATE_PARENT, SLCR_DCI_CLK_CTRL, 20, 6, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + &dciclk_lock); + clks[dci] = clk_register_gate(NULL, clk_output_name[dci], "dci_div1", + CLK_SET_RATE_PARENT, SLCR_DCI_CLK_CTRL, 0, 0, + &dciclk_lock); + clk_prepare_enable(clks[dci]); + + /* Peripheral clocks */ + for (i = fclk0; i <= fclk3; i++) + zynq_clk_register_fclk(i, clk_output_name[i], + SLCR_FPGA0_CLK_CTRL + 0x10 * (i - fclk0), + periph_parents); + + zynq_clk_register_periph_clk(lqspi, 0, clk_output_name[lqspi], NULL, + SLCR_LQSPI_CLK_CTRL, periph_parents, 0); + + zynq_clk_register_periph_clk(smc, 0, clk_output_name[smc], NULL, + SLCR_SMC_CLK_CTRL, periph_parents, 0); + + zynq_clk_register_periph_clk(pcap, 0, clk_output_name[pcap], NULL, + SLCR_PCAP_CLK_CTRL, periph_parents, 0); + + zynq_clk_register_periph_clk(sdio0, sdio1, clk_output_name[sdio0], + clk_output_name[sdio1], SLCR_SDIO_CLK_CTRL, + periph_parents, 1); + + zynq_clk_register_periph_clk(uart0, uart1, clk_output_name[uart0], + clk_output_name[uart1], SLCR_UART_CLK_CTRL, + periph_parents, 1); + + zynq_clk_register_periph_clk(spi0, spi1, clk_output_name[spi0], + clk_output_name[spi1], SLCR_SPI_CLK_CTRL, + periph_parents, 1); + + for (i = 0; i < ARRAY_SIZE(gem0_emio_input_names); i++) { + int idx = of_property_match_string(np, "clock-names", + gem0_emio_input_names[i]); + if (idx >= 0) + gem0_mux_parents[i + 1] = of_clk_get_parent_name(np, + idx); + } + clk = clk_register_mux(NULL, "gem0_mux", periph_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_GEM0_CLK_CTRL, 4, 2, 0, + &gem0clk_lock); + clk = clk_register_divider(NULL, "gem0_div0", "gem0_mux", 0, + SLCR_GEM0_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, &gem0clk_lock); + clk = clk_register_divider(NULL, "gem0_div1", "gem0_div0", + CLK_SET_RATE_PARENT, SLCR_GEM0_CLK_CTRL, 20, 6, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + &gem0clk_lock); + clk = clk_register_mux(NULL, "gem0_emio_mux", gem0_mux_parents, 2, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + SLCR_GEM0_CLK_CTRL, 6, 1, 0, + &gem0clk_lock); + clks[gem0] = clk_register_gate(NULL, clk_output_name[gem0], + "gem0_emio_mux", CLK_SET_RATE_PARENT, + SLCR_GEM0_CLK_CTRL, 0, 0, &gem0clk_lock); + + for (i = 0; i < ARRAY_SIZE(gem1_emio_input_names); i++) { + int idx = of_property_match_string(np, "clock-names", + gem1_emio_input_names[i]); + if (idx >= 0) + gem1_mux_parents[i + 1] = of_clk_get_parent_name(np, + idx); + } + clk = clk_register_mux(NULL, "gem1_mux", periph_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_GEM1_CLK_CTRL, 4, 2, 0, + &gem1clk_lock); + clk = clk_register_divider(NULL, "gem1_div0", "gem1_mux", 0, + SLCR_GEM1_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, &gem1clk_lock); + clk = clk_register_divider(NULL, "gem1_div1", "gem1_div0", + CLK_SET_RATE_PARENT, SLCR_GEM1_CLK_CTRL, 20, 6, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + &gem1clk_lock); + clk = clk_register_mux(NULL, "gem1_emio_mux", gem1_mux_parents, 2, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + SLCR_GEM1_CLK_CTRL, 6, 1, 0, + &gem1clk_lock); + clks[gem1] = clk_register_gate(NULL, clk_output_name[gem1], + "gem1_emio_mux", CLK_SET_RATE_PARENT, + SLCR_GEM1_CLK_CTRL, 0, 0, &gem1clk_lock); + + tmp = strlen("mio_clk_00x"); + clk_name = kmalloc(tmp, GFP_KERNEL); + for (i = 0; i < NUM_MIO_PINS; i++) { + int idx; + + snprintf(clk_name, tmp, "mio_clk_%2.2d", i); + idx = of_property_match_string(np, "clock-names", clk_name); + if (idx >= 0) + can_mio_mux_parents[i] = of_clk_get_parent_name(np, + idx); + else + can_mio_mux_parents[i] = dummy_nm; + } + kfree(clk_name); + clk = clk_register_mux(NULL, "can_mux", periph_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_CLK_CTRL, 4, 2, 0, + &canclk_lock); + clk = clk_register_divider(NULL, "can_div0", "can_mux", 0, + SLCR_CAN_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, &canclk_lock); + clk = clk_register_divider(NULL, "can_div1", "can_div0", + CLK_SET_RATE_PARENT, SLCR_CAN_CLK_CTRL, 20, 6, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + &canclk_lock); + clk = clk_register_gate(NULL, "can0_gate", "can_div1", + CLK_SET_RATE_PARENT, SLCR_CAN_CLK_CTRL, 0, 0, + &canclk_lock); + clk = clk_register_gate(NULL, "can1_gate", "can_div1", + CLK_SET_RATE_PARENT, SLCR_CAN_CLK_CTRL, 1, 0, + &canclk_lock); + clk = clk_register_mux(NULL, "can0_mio_mux", + can_mio_mux_parents, 54, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_MIOCLK_CTRL, 0, 6, 0, + &canmioclk_lock); + clk = clk_register_mux(NULL, "can1_mio_mux", + can_mio_mux_parents, 54, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_MIOCLK_CTRL, 16, 6, + 0, &canmioclk_lock); + clks[can0] = clk_register_mux(NULL, clk_output_name[can0], + can0_mio_mux2_parents, 2, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_MIOCLK_CTRL, 6, 1, 0, + &canmioclk_lock); + clks[can1] = clk_register_mux(NULL, clk_output_name[can1], + can1_mio_mux2_parents, 2, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_MIOCLK_CTRL, 22, 1, + 0, &canmioclk_lock); + + for (i = 0; i < ARRAY_SIZE(dbgtrc_emio_input_names); i++) { + int idx = of_property_match_string(np, "clock-names", + dbgtrc_emio_input_names[i]); + if (idx >= 0) + dbg_emio_mux_parents[i + 1] = of_clk_get_parent_name(np, + idx); + } + clk = clk_register_mux(NULL, "dbg_mux", periph_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_DBG_CLK_CTRL, 4, 2, 0, + &dbgclk_lock); + clk = clk_register_divider(NULL, "dbg_div", "dbg_mux", 0, + SLCR_DBG_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, &dbgclk_lock); + clk = clk_register_mux(NULL, "dbg_emio_mux", dbg_emio_mux_parents, 2, + CLK_SET_RATE_NO_REPARENT, SLCR_DBG_CLK_CTRL, 6, 1, 0, + &dbgclk_lock); + clks[dbg_trc] = clk_register_gate(NULL, clk_output_name[dbg_trc], + "dbg_emio_mux", CLK_SET_RATE_PARENT, SLCR_DBG_CLK_CTRL, + 0, 0, &dbgclk_lock); + clks[dbg_apb] = clk_register_gate(NULL, clk_output_name[dbg_apb], + clk_output_name[cpu_1x], 0, SLCR_DBG_CLK_CTRL, 1, 0, + &dbgclk_lock); + + /* One gated clock for all APER clocks. */ + clks[dma] = clk_register_gate(NULL, clk_output_name[dma], + clk_output_name[cpu_2x], 0, SLCR_APER_CLK_CTRL, 0, 0, + &aperclk_lock); + clks[usb0_aper] = clk_register_gate(NULL, clk_output_name[usb0_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 2, 0, + &aperclk_lock); + clks[usb1_aper] = clk_register_gate(NULL, clk_output_name[usb1_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 3, 0, + &aperclk_lock); + clks[gem0_aper] = clk_register_gate(NULL, clk_output_name[gem0_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 6, 0, + &aperclk_lock); + clks[gem1_aper] = clk_register_gate(NULL, clk_output_name[gem1_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 7, 0, + &aperclk_lock); + clks[sdio0_aper] = clk_register_gate(NULL, clk_output_name[sdio0_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 10, 0, + &aperclk_lock); + clks[sdio1_aper] = clk_register_gate(NULL, clk_output_name[sdio1_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 11, 0, + &aperclk_lock); + clks[spi0_aper] = clk_register_gate(NULL, clk_output_name[spi0_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 14, 0, + &aperclk_lock); + clks[spi1_aper] = clk_register_gate(NULL, clk_output_name[spi1_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 15, 0, + &aperclk_lock); + clks[can0_aper] = clk_register_gate(NULL, clk_output_name[can0_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 16, 0, + &aperclk_lock); + clks[can1_aper] = clk_register_gate(NULL, clk_output_name[can1_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 17, 0, + &aperclk_lock); + clks[i2c0_aper] = clk_register_gate(NULL, clk_output_name[i2c0_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 18, 0, + &aperclk_lock); + clks[i2c1_aper] = clk_register_gate(NULL, clk_output_name[i2c1_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 19, 0, + &aperclk_lock); + clks[uart0_aper] = clk_register_gate(NULL, clk_output_name[uart0_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 20, 0, + &aperclk_lock); + clks[uart1_aper] = clk_register_gate(NULL, clk_output_name[uart1_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 21, 0, + &aperclk_lock); + clks[gpio_aper] = clk_register_gate(NULL, clk_output_name[gpio_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 22, 0, + &aperclk_lock); + clks[lqspi_aper] = clk_register_gate(NULL, clk_output_name[lqspi_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 23, 0, + &aperclk_lock); + clks[smc_aper] = clk_register_gate(NULL, clk_output_name[smc_aper], + clk_output_name[cpu_1x], 0, SLCR_APER_CLK_CTRL, 24, 0, + &aperclk_lock); + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + if (IS_ERR(clks[i])) { + pr_err("Zynq clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + BUG(); + } + } + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} + +CLK_OF_DECLARE(zynq_clkc, "xlnx,ps7-clkc", zynq_clk_setup); + +void __init zynq_clock_init(void __iomem *slcr_base) +{ + zynq_slcr_base_priv = slcr_base; + of_clk_init(NULL); +} diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c new file mode 100644 index 000000000000..3226f54fa595 --- /dev/null +++ b/drivers/clk/zynq/pll.c @@ -0,0 +1,244 @@ +/* + * Zynq PLL driver + * + * Copyright (C) 2013 Xilinx + * + * Sören Brinkmann <soren.brinkmann@xilinx.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 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. + * + * 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/zynq.h> +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> + +/** + * struct zynq_pll + * @hw: Handle between common and hardware-specific interfaces + * @pll_ctrl: PLL control register + * @pll_status: PLL status register + * @lock: Register lock + * @lockbit: Indicates the associated PLL_LOCKED bit in the PLL status + * register. + */ +struct zynq_pll { + struct clk_hw hw; + void __iomem *pll_ctrl; + void __iomem *pll_status; + spinlock_t *lock; + u8 lockbit; +}; +#define to_zynq_pll(_hw) container_of(_hw, struct zynq_pll, hw) + +/* Register bitfield defines */ +#define PLLCTRL_FBDIV_MASK 0x7f000 +#define PLLCTRL_FBDIV_SHIFT 12 +#define PLLCTRL_BPQUAL_MASK (1 << 3) +#define PLLCTRL_PWRDWN_MASK 2 +#define PLLCTRL_PWRDWN_SHIFT 1 +#define PLLCTRL_RESET_MASK 1 +#define PLLCTRL_RESET_SHIFT 0 + +#define PLL_FBDIV_MIN 13 +#define PLL_FBDIV_MAX 66 + +/** + * zynq_pll_round_rate() - Round a clock frequency + * @hw: Handle between common and hardware-specific interfaces + * @rate: Desired clock frequency + * @prate: Clock frequency of parent clock + * Returns frequency closest to @rate the hardware can generate. + */ +static long zynq_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + u32 fbdiv; + + fbdiv = DIV_ROUND_CLOSEST(rate, *prate); + if (fbdiv < PLL_FBDIV_MIN) + fbdiv = PLL_FBDIV_MIN; + else if (fbdiv > PLL_FBDIV_MAX) + fbdiv = PLL_FBDIV_MAX; + + return *prate * fbdiv; +} + +/** + * zynq_pll_recalc_rate() - Recalculate clock frequency + * @hw: Handle between common and hardware-specific interfaces + * @parent_rate: Clock frequency of parent clock + * Returns current clock frequency. + */ +static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct zynq_pll *clk = to_zynq_pll(hw); + u32 fbdiv; + + /* + * makes probably sense to redundantly save fbdiv in the struct + * zynq_pll to save the IO access. + */ + fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >> + PLLCTRL_FBDIV_SHIFT; + + return parent_rate * fbdiv; +} + +/** + * zynq_pll_is_enabled - Check if a clock is enabled + * @hw: Handle between common and hardware-specific interfaces + * Returns 1 if the clock is enabled, 0 otherwise. + * + * Not sure this is a good idea, but since disabled means bypassed for + * this clock implementation we say we are always enabled. + */ +static int zynq_pll_is_enabled(struct clk_hw *hw) +{ + unsigned long flags = 0; + u32 reg; + struct zynq_pll *clk = to_zynq_pll(hw); + + spin_lock_irqsave(clk->lock, flags); + + reg = readl(clk->pll_ctrl); + + spin_unlock_irqrestore(clk->lock, flags); + + return !(reg & (PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK)); +} + +/** + * zynq_pll_enable - Enable clock + * @hw: Handle between common and hardware-specific interfaces + * Returns 0 on success + */ +static int zynq_pll_enable(struct clk_hw *hw) +{ + unsigned long flags = 0; + u32 reg; + struct zynq_pll *clk = to_zynq_pll(hw); + + if (zynq_pll_is_enabled(hw)) + return 0; + + pr_info("PLL: enable\n"); + + /* Power up PLL and wait for lock */ + spin_lock_irqsave(clk->lock, flags); + + reg = readl(clk->pll_ctrl); + reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK); + writel(reg, clk->pll_ctrl); + while (!(readl(clk->pll_status) & (1 << clk->lockbit))) + ; + + spin_unlock_irqrestore(clk->lock, flags); + + return 0; +} + +/** + * zynq_pll_disable - Disable clock + * @hw: Handle between common and hardware-specific interfaces + * Returns 0 on success + */ +static void zynq_pll_disable(struct clk_hw *hw) +{ + unsigned long flags = 0; + u32 reg; + struct zynq_pll *clk = to_zynq_pll(hw); + + if (!zynq_pll_is_enabled(hw)) + return; + + pr_info("PLL: shutdown\n"); + + /* shut down PLL */ + spin_lock_irqsave(clk->lock, flags); + + reg = readl(clk->pll_ctrl); + reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK; + writel(reg, clk->pll_ctrl); + + spin_unlock_irqrestore(clk->lock, flags); +} + +static const struct clk_ops zynq_pll_ops = { + .enable = zynq_pll_enable, + .disable = zynq_pll_disable, + .is_enabled = zynq_pll_is_enabled, + .round_rate = zynq_pll_round_rate, + .recalc_rate = zynq_pll_recalc_rate +}; + +/** + * clk_register_zynq_pll() - Register PLL with the clock framework + * @name PLL name + * @parent Parent clock name + * @pll_ctrl Pointer to PLL control register + * @pll_status Pointer to PLL status register + * @lock_index Bit index to this PLL's lock status bit in @pll_status + * @lock Register lock + * Returns handle to the registered clock. + */ +struct clk *clk_register_zynq_pll(const char *name, const char *parent, + void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index, + spinlock_t *lock) +{ + struct zynq_pll *pll; + struct clk *clk; + u32 reg; + const char *parent_arr[1] = {parent}; + unsigned long flags = 0; + struct clk_init_data initd = { + .name = name, + .parent_names = parent_arr, + .ops = &zynq_pll_ops, + .num_parents = 1, + .flags = 0 + }; + + pll = kmalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: Could not allocate Zynq PLL clk.\n", __func__); + return ERR_PTR(-ENOMEM); + } + + /* Populate the struct */ + pll->hw.init = &initd; + pll->pll_ctrl = pll_ctrl; + pll->pll_status = pll_status; + pll->lockbit = lock_index; + pll->lock = lock; + + spin_lock_irqsave(pll->lock, flags); + + reg = readl(pll->pll_ctrl); + reg &= ~PLLCTRL_BPQUAL_MASK; + writel(reg, pll->pll_ctrl); + + spin_unlock_irqrestore(pll->lock, flags); + + clk = clk_register(NULL, &pll->hw); + if (WARN_ON(IS_ERR(clk))) + goto free_pll; + + return clk; + +free_pll: + kfree(pll); + + return clk; +} |