From 526d239c301e6cb7fe6657642c43d6c524590745 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 24 Dec 2012 21:55:01 -0700 Subject: clk: bcm2835: probe for fixed-clock in device tree This will allow fixed-clocks to be registered in device tree, which in turn will allow clock clients to look up their clocks in device tree. The first driver to use this feature will be the bcm2835 SDHCI/MMC driver. In the long run, it is likely these fixed clocks will be replaced with an actual clock implementation which speaks to the VideoCore processor, but this change should be transparent to clock consumers. Cc: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/clk-bcm2835.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/clk-bcm2835.c b/drivers/clk/clk-bcm2835.c index e69991aab43a..792bc57a9db7 100644 --- a/drivers/clk/clk-bcm2835.c +++ b/drivers/clk/clk-bcm2835.c @@ -20,6 +20,13 @@ #include #include #include +#include +#include + +static const __initconst struct of_device_id clk_match[] = { + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { } +}; /* * These are fixed clocks. They're probably not all root clocks and it may @@ -56,4 +63,6 @@ void __init bcm2835_init_clocks(void) ret = clk_register_clkdev(clk, NULL, "20215000.uart"); if (ret) pr_err("uart1_pclk alias not registered\n"); + + of_clk_init(clk_match); } -- cgit v1.2.3 From 8f8f484bf355e546c62c47b8a8c8d19b28787798 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Fri, 11 Jan 2013 13:16:20 +0530 Subject: clk: tegra: add Tegra specific clocks Add Tegra specific clocks, pll, pll_out, peripheral, frac_divider, super. Signed-off-by: Prashant Gaikwad [swarren: alloc sizeof(*foo) not sizeof(struct foo), add comments re: storing pointers to stack variables, make a timeout loop more idiomatic, use _clk_pll_disable() not clk_disable_pll() from _program_pll() to avoid redundant lock operations, unified tegra_clk_periph() and tegra_clk_periph_nodiv(), unified tegra_clk_pll{,e}, rename all clock registration functions so they don't have the same name as the clock structs, return -EINVAL from clk_plle_enable when matching table rate not found, pass ops to _tegra_clk_register_pll rather than a bool.] Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/Makefile | 1 + drivers/clk/tegra/Makefile | 8 + drivers/clk/tegra/clk-audio-sync.c | 87 +++++ drivers/clk/tegra/clk-divider.c | 187 +++++++++++ drivers/clk/tegra/clk-periph-gate.c | 179 ++++++++++ drivers/clk/tegra/clk-periph.c | 180 ++++++++++ drivers/clk/tegra/clk-pll-out.c | 123 +++++++ drivers/clk/tegra/clk-pll.c | 648 ++++++++++++++++++++++++++++++++++++ drivers/clk/tegra/clk-super.c | 154 +++++++++ drivers/clk/tegra/clk.c | 69 ++++ drivers/clk/tegra/clk.h | 490 +++++++++++++++++++++++++++ 11 files changed, 2126 insertions(+) create mode 100644 drivers/clk/tegra/Makefile create mode 100644 drivers/clk/tegra/clk-audio-sync.c create mode 100644 drivers/clk/tegra/clk-divider.c create mode 100644 drivers/clk/tegra/clk-periph-gate.c create mode 100644 drivers/clk/tegra/clk-periph.c create mode 100644 drivers/clk/tegra/clk-pll-out.c create mode 100644 drivers/clk/tegra/clk-pll.c create mode 100644 drivers/clk/tegra/clk-super.c create mode 100644 drivers/clk/tegra/clk.c create mode 100644 drivers/clk/tegra/clk.h (limited to 'drivers') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index ee90e87e7675..f0b269a2058d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_SUNXI) += clk-sunxi.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o +obj-$(CONFIG_ARCH_TEGRA) += tegra/ # Chip specific obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile new file mode 100644 index 000000000000..68bd353a2108 --- /dev/null +++ b/drivers/clk/tegra/Makefile @@ -0,0 +1,8 @@ +obj-y += clk.o +obj-y += clk-audio-sync.o +obj-y += clk-divider.o +obj-y += clk-periph.o +obj-y += clk-periph-gate.o +obj-y += clk-pll.o +obj-y += clk-pll-out.o +obj-y += clk-super.o diff --git a/drivers/clk/tegra/clk-audio-sync.c b/drivers/clk/tegra/clk-audio-sync.c new file mode 100644 index 000000000000..c0f7843e80e6 --- /dev/null +++ b/drivers/clk/tegra/clk-audio-sync.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "clk.h" + +static unsigned long clk_sync_source_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_sync_source *sync = to_clk_sync_source(hw); + + return sync->rate; +} + +static long clk_sync_source_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_sync_source *sync = to_clk_sync_source(hw); + + if (rate > sync->max_rate) + return -EINVAL; + else + return rate; +} + +static int clk_sync_source_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_sync_source *sync = to_clk_sync_source(hw); + + sync->rate = rate; + return 0; +} + +const struct clk_ops tegra_clk_sync_source_ops = { + .round_rate = clk_sync_source_round_rate, + .set_rate = clk_sync_source_set_rate, + .recalc_rate = clk_sync_source_recalc_rate, +}; + +struct clk *tegra_clk_register_sync_source(const char *name, + unsigned long rate, unsigned long max_rate) +{ + struct tegra_clk_sync_source *sync; + struct clk_init_data init; + struct clk *clk; + + sync = kzalloc(sizeof(*sync), GFP_KERNEL); + if (!sync) { + pr_err("%s: could not allocate sync source clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + sync->rate = rate; + sync->max_rate = max_rate; + + init.ops = &tegra_clk_sync_source_ops; + init.name = name; + init.flags = CLK_IS_ROOT; + init.parent_names = NULL; + init.num_parents = 0; + + /* Data in .init is copied by clk_register(), so stack variable OK */ + sync->hw.init = &init; + + clk = clk_register(NULL, &sync->hw); + if (IS_ERR(clk)) + kfree(sync); + + return clk; +} diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c new file mode 100644 index 000000000000..4d75b1f37e3a --- /dev/null +++ b/drivers/clk/tegra/clk-divider.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define pll_out_override(p) (BIT((p->shift - 6))) +#define div_mask(d) ((1 << (d->width)) - 1) +#define get_mul(d) (1 << d->frac_width) +#define get_max_div(d) div_mask(d) + +#define PERIPH_CLK_UART_DIV_ENB BIT(24) + +static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, + unsigned long parent_rate) +{ + s64 divider_ux1 = parent_rate; + u8 flags = divider->flags; + int mul; + + if (!rate) + return 0; + + mul = get_mul(divider); + + if (!(flags & TEGRA_DIVIDER_INT)) + divider_ux1 *= mul; + + if (flags & TEGRA_DIVIDER_ROUND_UP) + divider_ux1 += rate - 1; + + do_div(divider_ux1, rate); + + if (flags & TEGRA_DIVIDER_INT) + divider_ux1 *= mul; + + divider_ux1 -= mul; + + if (divider_ux1 < 0) + return 0; + + if (divider_ux1 > get_max_div(divider)) + return -EINVAL; + + return divider_ux1; +} + +static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); + u32 reg; + int div, mul; + u64 rate = parent_rate; + + reg = readl_relaxed(divider->reg) >> divider->shift; + div = reg & div_mask(divider); + + mul = get_mul(divider); + div += mul; + + rate *= mul; + rate += div - 1; + do_div(rate, div); + + return rate; +} + +static long clk_frac_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); + int div, mul; + unsigned long output_rate = *prate; + + if (!rate) + return output_rate; + + div = get_div(divider, rate, output_rate); + if (div < 0) + return *prate; + + mul = get_mul(divider); + + return DIV_ROUND_UP(output_rate * mul, div + mul); +} + +static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); + int div; + unsigned long flags = 0; + u32 val; + + div = get_div(divider, rate, parent_rate); + if (div < 0) + return div; + + if (divider->lock) + spin_lock_irqsave(divider->lock, flags); + + val = readl_relaxed(divider->reg); + val &= ~(div_mask(divider) << divider->shift); + val |= div << divider->shift; + + if (divider->flags & TEGRA_DIVIDER_UART) { + if (div) + val |= PERIPH_CLK_UART_DIV_ENB; + else + val &= ~PERIPH_CLK_UART_DIV_ENB; + } + + if (divider->flags & TEGRA_DIVIDER_FIXED) + val |= pll_out_override(divider); + + writel_relaxed(val, divider->reg); + + if (divider->lock) + spin_unlock_irqrestore(divider->lock, flags); + + return 0; +} + +const struct clk_ops tegra_clk_frac_div_ops = { + .recalc_rate = clk_frac_div_recalc_rate, + .set_rate = clk_frac_div_set_rate, + .round_rate = clk_frac_div_round_rate, +}; + +struct clk *tegra_clk_register_divider(const char *name, + const char *parent_name, void __iomem *reg, + unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, + u8 frac_width, spinlock_t *lock) +{ + struct tegra_clk_frac_div *divider; + struct clk *clk; + struct clk_init_data init; + + divider = kzalloc(sizeof(*divider), GFP_KERNEL); + if (!divider) { + pr_err("%s: could not allocate fractional divider clk\n", + __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &tegra_clk_frac_div_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + divider->reg = reg; + divider->shift = shift; + divider->width = width; + divider->frac_width = frac_width; + divider->lock = lock; + divider->flags = clk_divider_flags; + + /* Data in .init is copied by clk_register(), so stack variable OK */ + divider->hw.init = &init; + + clk = clk_register(NULL, ÷r->hw); + if (IS_ERR(clk)) + kfree(divider); + + return clk; +} diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c new file mode 100644 index 000000000000..6dd533251e7b --- /dev/null +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +static DEFINE_SPINLOCK(periph_ref_lock); + +/* Macros to assist peripheral gate clock */ +#define read_enb(gate) \ + readl_relaxed(gate->clk_base + (gate->regs->enb_reg)) +#define write_enb_set(val, gate) \ + writel_relaxed(val, gate->clk_base + (gate->regs->enb_set_reg)) +#define write_enb_clr(val, gate) \ + writel_relaxed(val, gate->clk_base + (gate->regs->enb_clr_reg)) + +#define read_rst(gate) \ + readl_relaxed(gate->clk_base + (gate->regs->rst_reg)) +#define write_rst_set(val, gate) \ + writel_relaxed(val, gate->clk_base + (gate->regs->rst_set_reg)) +#define write_rst_clr(val, gate) \ + writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg)) + +#define periph_clk_to_bit(periph) (1 << (gate->clk_num % 32)) + +/* Peripheral gate clock ops */ +static int clk_periph_is_enabled(struct clk_hw *hw) +{ + struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw); + int state = 1; + + if (!(read_enb(gate) & periph_clk_to_bit(gate))) + state = 0; + + if (!(gate->flags & TEGRA_PERIPH_NO_RESET)) + if (read_rst(gate) & periph_clk_to_bit(gate)) + state = 0; + + return state; +} + +static int clk_periph_enable(struct clk_hw *hw) +{ + struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw); + unsigned long flags = 0; + + spin_lock_irqsave(&periph_ref_lock, flags); + + gate->enable_refcnt[gate->clk_num]++; + if (gate->enable_refcnt[gate->clk_num] > 1) { + spin_unlock_irqrestore(&periph_ref_lock, flags); + return 0; + } + + write_enb_set(periph_clk_to_bit(gate), gate); + udelay(2); + + if (!(gate->flags & TEGRA_PERIPH_NO_RESET) && + !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) { + if (read_rst(gate) & periph_clk_to_bit(gate)) { + udelay(5); /* reset propogation delay */ + write_rst_clr(periph_clk_to_bit(gate), gate); + } + } + + spin_unlock_irqrestore(&periph_ref_lock, flags); + + return 0; +} + +static void clk_periph_disable(struct clk_hw *hw) +{ + struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw); + unsigned long flags = 0; + + spin_lock_irqsave(&periph_ref_lock, flags); + + gate->enable_refcnt[gate->clk_num]--; + if (gate->enable_refcnt[gate->clk_num] > 0) { + spin_unlock_irqrestore(&periph_ref_lock, flags); + return; + } + + /* + * If peripheral is in the APB bus then read the APB bus to + * flush the write operation in apb bus. This will avoid the + * peripheral access after disabling clock + */ + if (gate->flags & TEGRA_PERIPH_ON_APB) + tegra_read_chipid(); + + write_enb_clr(periph_clk_to_bit(gate), gate); + + spin_unlock_irqrestore(&periph_ref_lock, flags); +} + +void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert) +{ + if (gate->flags & TEGRA_PERIPH_NO_RESET) + return; + + if (assert) { + /* + * If peripheral is in the APB bus then read the APB bus to + * flush the write operation in apb bus. This will avoid the + * peripheral access after disabling clock + */ + if (gate->flags & TEGRA_PERIPH_ON_APB) + tegra_read_chipid(); + + write_rst_set(periph_clk_to_bit(gate), gate); + } else { + write_rst_clr(periph_clk_to_bit(gate), gate); + } +} + +const struct clk_ops tegra_clk_periph_gate_ops = { + .is_enabled = clk_periph_is_enabled, + .enable = clk_periph_enable, + .disable = clk_periph_disable, +}; + +struct clk *tegra_clk_register_periph_gate(const char *name, + const char *parent_name, u8 gate_flags, void __iomem *clk_base, + unsigned long flags, int clk_num, + struct tegra_clk_periph_regs *pregs, int *enable_refcnt) +{ + struct tegra_clk_periph_gate *gate; + struct clk *clk; + struct clk_init_data init; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + pr_err("%s: could not allocate periph gate clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = &tegra_clk_periph_gate_ops; + + gate->magic = TEGRA_CLK_PERIPH_GATE_MAGIC; + gate->clk_base = clk_base; + gate->clk_num = clk_num; + gate->flags = gate_flags; + gate->enable_refcnt = enable_refcnt; + gate->regs = pregs; + + /* Data in .init is copied by clk_register(), so stack variable OK */ + gate->hw.init = &init; + + clk = clk_register(NULL, &gate->hw); + if (IS_ERR(clk)) + kfree(gate); + + return clk; +} diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c new file mode 100644 index 000000000000..5978e81b175b --- /dev/null +++ b/drivers/clk/tegra/clk-periph.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "clk.h" + +static u8 clk_periph_get_parent(struct clk_hw *hw) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *mux_ops = periph->mux_ops; + struct clk_hw *mux_hw = &periph->mux.hw; + + mux_hw->clk = hw->clk; + + return mux_ops->get_parent(mux_hw); +} + +static int clk_periph_set_parent(struct clk_hw *hw, u8 index) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *mux_ops = periph->mux_ops; + struct clk_hw *mux_hw = &periph->mux.hw; + + mux_hw->clk = hw->clk; + + return mux_ops->set_parent(mux_hw, index); +} + +static unsigned long clk_periph_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *div_ops = periph->div_ops; + struct clk_hw *div_hw = &periph->divider.hw; + + div_hw->clk = hw->clk; + + return div_ops->recalc_rate(div_hw, parent_rate); +} + +static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *div_ops = periph->div_ops; + struct clk_hw *div_hw = &periph->divider.hw; + + div_hw->clk = hw->clk; + + return div_ops->round_rate(div_hw, rate, prate); +} + +static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *div_ops = periph->div_ops; + struct clk_hw *div_hw = &periph->divider.hw; + + div_hw->clk = hw->clk; + + return div_ops->set_rate(div_hw, rate, parent_rate); +} + +static int clk_periph_is_enabled(struct clk_hw *hw) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *gate_ops = periph->gate_ops; + struct clk_hw *gate_hw = &periph->gate.hw; + + gate_hw->clk = hw->clk; + + return gate_ops->is_enabled(gate_hw); +} + +static int clk_periph_enable(struct clk_hw *hw) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *gate_ops = periph->gate_ops; + struct clk_hw *gate_hw = &periph->gate.hw; + + gate_hw->clk = hw->clk; + + return gate_ops->enable(gate_hw); +} + +static void clk_periph_disable(struct clk_hw *hw) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *gate_ops = periph->gate_ops; + struct clk_hw *gate_hw = &periph->gate.hw; + + gate_ops->disable(gate_hw); +} + +const struct clk_ops tegra_clk_periph_ops = { + .get_parent = clk_periph_get_parent, + .set_parent = clk_periph_set_parent, + .recalc_rate = clk_periph_recalc_rate, + .round_rate = clk_periph_round_rate, + .set_rate = clk_periph_set_rate, + .is_enabled = clk_periph_is_enabled, + .enable = clk_periph_enable, + .disable = clk_periph_disable, +}; + +const struct clk_ops tegra_clk_periph_nodiv_ops = { + .get_parent = clk_periph_get_parent, + .set_parent = clk_periph_set_parent, + .is_enabled = clk_periph_is_enabled, + .enable = clk_periph_enable, + .disable = clk_periph_disable, +}; + +static struct clk *_tegra_clk_register_periph(const char *name, + const char **parent_names, int num_parents, + struct tegra_clk_periph *periph, + void __iomem *clk_base, u32 offset, bool div) +{ + struct clk *clk; + struct clk_init_data init; + + init.name = name; + init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops; + init.flags = div ? 0 : CLK_SET_RATE_PARENT; + init.parent_names = parent_names; + init.num_parents = num_parents; + + /* Data in .init is copied by clk_register(), so stack variable OK */ + periph->hw.init = &init; + periph->magic = TEGRA_CLK_PERIPH_MAGIC; + periph->mux.reg = clk_base + offset; + periph->divider.reg = div ? (clk_base + offset) : NULL; + periph->gate.clk_base = clk_base; + + clk = clk_register(NULL, &periph->hw); + if (IS_ERR(clk)) + return clk; + + periph->mux.hw.clk = clk; + periph->divider.hw.clk = div ? clk : NULL; + periph->gate.hw.clk = clk; + + return clk; +} + +struct clk *tegra_clk_register_periph(const char *name, + const char **parent_names, int num_parents, + struct tegra_clk_periph *periph, void __iomem *clk_base, + u32 offset) +{ + return _tegra_clk_register_periph(name, parent_names, num_parents, + periph, clk_base, offset, true); +} + +struct clk *tegra_clk_register_periph_nodiv(const char *name, + const char **parent_names, int num_parents, + struct tegra_clk_periph *periph, void __iomem *clk_base, + u32 offset) +{ + return _tegra_clk_register_periph(name, parent_names, num_parents, + periph, clk_base, offset, false); +} diff --git a/drivers/clk/tegra/clk-pll-out.c b/drivers/clk/tegra/clk-pll-out.c new file mode 100644 index 000000000000..3598987a451d --- /dev/null +++ b/drivers/clk/tegra/clk-pll-out.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define pll_out_enb(p) (BIT(p->enb_bit_idx)) +#define pll_out_rst(p) (BIT(p->rst_bit_idx)) + +static int clk_pll_out_is_enabled(struct clk_hw *hw) +{ + struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw); + u32 val = readl_relaxed(pll_out->reg); + int state; + + state = (val & pll_out_enb(pll_out)) ? 1 : 0; + if (!(val & (pll_out_rst(pll_out)))) + state = 0; + return state; +} + +static int clk_pll_out_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw); + unsigned long flags = 0; + u32 val; + + if (pll_out->lock) + spin_lock_irqsave(pll_out->lock, flags); + + val = readl_relaxed(pll_out->reg); + + val |= (pll_out_enb(pll_out) | pll_out_rst(pll_out)); + + writel_relaxed(val, pll_out->reg); + udelay(2); + + if (pll_out->lock) + spin_unlock_irqrestore(pll_out->lock, flags); + + return 0; +} + +static void clk_pll_out_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw); + unsigned long flags = 0; + u32 val; + + if (pll_out->lock) + spin_lock_irqsave(pll_out->lock, flags); + + val = readl_relaxed(pll_out->reg); + + val &= ~(pll_out_enb(pll_out) | pll_out_rst(pll_out)); + + writel_relaxed(val, pll_out->reg); + udelay(2); + + if (pll_out->lock) + spin_unlock_irqrestore(pll_out->lock, flags); +} + +const struct clk_ops tegra_clk_pll_out_ops = { + .is_enabled = clk_pll_out_is_enabled, + .enable = clk_pll_out_enable, + .disable = clk_pll_out_disable, +}; + +struct clk *tegra_clk_register_pll_out(const char *name, + const char *parent_name, void __iomem *reg, u8 enb_bit_idx, + u8 rst_bit_idx, unsigned long flags, u8 pll_out_flags, + spinlock_t *lock) +{ + struct tegra_clk_pll_out *pll_out; + struct clk *clk; + struct clk_init_data init; + + pll_out = kzalloc(sizeof(*pll_out), GFP_KERNEL); + if (!pll_out) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &tegra_clk_pll_out_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + init.flags = flags; + + pll_out->reg = reg; + pll_out->enb_bit_idx = enb_bit_idx; + pll_out->rst_bit_idx = rst_bit_idx; + pll_out->flags = pll_out_flags; + pll_out->lock = lock; + + /* Data in .init is copied by clk_register(), so stack variable OK */ + pll_out->hw.init = &init; + + clk = clk_register(NULL, &pll_out->hw); + if (IS_ERR(clk)) + kfree(pll_out); + + return clk; +} diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c new file mode 100644 index 000000000000..165f24734c1b --- /dev/null +++ b/drivers/clk/tegra/clk-pll.c @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define PLL_BASE_BYPASS BIT(31) +#define PLL_BASE_ENABLE BIT(30) +#define PLL_BASE_REF_ENABLE BIT(29) +#define PLL_BASE_OVERRIDE BIT(28) + +#define PLL_BASE_DIVP_SHIFT 20 +#define PLL_BASE_DIVP_WIDTH 3 +#define PLL_BASE_DIVN_SHIFT 8 +#define PLL_BASE_DIVN_WIDTH 10 +#define PLL_BASE_DIVM_SHIFT 0 +#define PLL_BASE_DIVM_WIDTH 5 +#define PLLU_POST_DIVP_MASK 0x1 + +#define PLL_MISC_DCCON_SHIFT 20 +#define PLL_MISC_CPCON_SHIFT 8 +#define PLL_MISC_CPCON_WIDTH 4 +#define PLL_MISC_CPCON_MASK ((1 << PLL_MISC_CPCON_WIDTH) - 1) +#define PLL_MISC_LFCON_SHIFT 4 +#define PLL_MISC_LFCON_WIDTH 4 +#define PLL_MISC_LFCON_MASK ((1 << PLL_MISC_LFCON_WIDTH) - 1) +#define PLL_MISC_VCOCON_SHIFT 0 +#define PLL_MISC_VCOCON_WIDTH 4 +#define PLL_MISC_VCOCON_MASK ((1 << PLL_MISC_VCOCON_WIDTH) - 1) + +#define OUT_OF_TABLE_CPCON 8 + +#define PMC_PLLP_WB0_OVERRIDE 0xf8 +#define PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE BIT(12) +#define PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE BIT(11) + +#define PLL_POST_LOCK_DELAY 50 + +#define PLLDU_LFCON_SET_DIVN 600 + +#define PLLE_BASE_DIVCML_SHIFT 24 +#define PLLE_BASE_DIVCML_WIDTH 4 +#define PLLE_BASE_DIVP_SHIFT 16 +#define PLLE_BASE_DIVP_WIDTH 7 +#define PLLE_BASE_DIVN_SHIFT 8 +#define PLLE_BASE_DIVN_WIDTH 8 +#define PLLE_BASE_DIVM_SHIFT 0 +#define PLLE_BASE_DIVM_WIDTH 8 + +#define PLLE_MISC_SETUP_BASE_SHIFT 16 +#define PLLE_MISC_SETUP_BASE_MASK (0xffff << PLLE_MISC_SETUP_BASE_SHIFT) +#define PLLE_MISC_LOCK_ENABLE BIT(9) +#define PLLE_MISC_READY BIT(15) +#define PLLE_MISC_SETUP_EX_SHIFT 2 +#define PLLE_MISC_SETUP_EX_MASK (3 << PLLE_MISC_SETUP_EX_SHIFT) +#define PLLE_MISC_SETUP_MASK (PLLE_MISC_SETUP_BASE_MASK | \ + PLLE_MISC_SETUP_EX_MASK) +#define PLLE_MISC_SETUP_VALUE (7 << PLLE_MISC_SETUP_BASE_SHIFT) + +#define PLLE_SS_CTRL 0x68 +#define PLLE_SS_DISABLE (7 << 10) + +#define PMC_SATA_PWRGT 0x1ac +#define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5) +#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4) + +#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_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 mask(w) ((1 << (w)) - 1) +#define divm_mask(p) mask(p->divm_width) +#define divn_mask(p) mask(p->divn_width) +#define divp_mask(p) (p->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK : \ + mask(p->divp_width)) + +#define divm_max(p) (divm_mask(p)) +#define divn_max(p) (divn_mask(p)) +#define divp_max(p) (1 << (divp_mask(p))) + +static void clk_pll_enable_lock(struct tegra_clk_pll *pll) +{ + u32 val; + + if (!(pll->flags & TEGRA_PLL_USE_LOCK)) + return; + + val = pll_readl_misc(pll); + val |= BIT(pll->params->lock_enable_bit_idx); + pll_writel_misc(val, pll); +} + +static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll, + void __iomem *lock_addr, u32 lock_bit_idx) +{ + int i; + u32 val; + + if (!(pll->flags & TEGRA_PLL_USE_LOCK)) { + udelay(pll->params->lock_delay); + return 0; + } + + for (i = 0; i < pll->params->lock_delay; i++) { + val = readl_relaxed(lock_addr); + if (val & BIT(lock_bit_idx)) { + udelay(PLL_POST_LOCK_DELAY); + return 0; + } + udelay(2); /* timeout = 2 * lock time */ + } + + pr_err("%s: Timed out waiting for pll %s lock\n", __func__, + __clk_get_name(pll->hw.clk)); + + return -1; +} + +static int clk_pll_is_enabled(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + + if (pll->flags & TEGRA_PLLM) { + val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE); + if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) + return val & PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE ? 1 : 0; + } + + val = pll_readl_base(pll); + + return val & PLL_BASE_ENABLE ? 1 : 0; +} + +static int _clk_pll_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + + clk_pll_enable_lock(pll); + + val = pll_readl_base(pll); + val &= ~PLL_BASE_BYPASS; + val |= PLL_BASE_ENABLE; + pll_writel_base(val, pll); + + if (pll->flags & TEGRA_PLLM) { + val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE); + val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE; + writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE); + } + + clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->base_reg, + pll->params->lock_bit_idx); + + return 0; +} + +static void _clk_pll_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + + val = pll_readl_base(pll); + val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); + pll_writel_base(val, pll); + + if (pll->flags & TEGRA_PLLM) { + val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE); + val &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE; + writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE); + } +} + +static int clk_pll_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int ret; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + ret = _clk_pll_enable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void clk_pll_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_disable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +static int _get_table_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 tegra_clk_pll_freq_table *sel; + + for (sel = pll->freq_table; sel->input_rate != 0; sel++) + if (sel->input_rate == parent_rate && + sel->output_rate == rate) + break; + + if (sel->input_rate == 0) + return -EINVAL; + + BUG_ON(sel->p < 1); + + cfg->input_rate = sel->input_rate; + cfg->output_rate = sel->output_rate; + cfg->m = sel->m; + cfg->n = sel->n; + cfg->p = sel->p; + cfg->cpcon = sel->cpcon; + + return 0; +} + +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); + unsigned long cfreq; + u32 p_div = 0; + + switch (parent_rate) { + case 12000000: + case 26000000: + cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2000000; + break; + case 13000000: + cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2600000; + break; + case 16800000: + case 19200000: + cfreq = (rate <= 1200000 * 1000) ? 1200000 : 2400000; + break; + case 9600000: + case 28800000: + /* + * PLL_P_OUT1 rate is not listed in PLLA table + */ + cfreq = parent_rate/(parent_rate/1000000); + break; + default: + pr_err("%s Unexpected reference rate %lu\n", + __func__, parent_rate); + BUG(); + } + + /* Raise VCO to guarantee 0.5% accuracy */ + for (cfg->output_rate = rate; cfg->output_rate < 200 * cfreq; + cfg->output_rate <<= 1) + p_div++; + + cfg->p = 1 << p_div; + cfg->m = parent_rate / cfreq; + cfg->n = cfg->output_rate / cfreq; + cfg->cpcon = OUT_OF_TABLE_CPCON; + + if (cfg->m > divm_max(pll) || cfg->n > divn_max(pll) || + cfg->p > divp_max(pll) || cfg->output_rate > pll->params->vco_max) { + pr_err("%s: Failed to set %s rate %lu\n", + __func__, __clk_get_name(hw->clk), rate); + return -EINVAL; + } + + return 0; +} + +static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, + unsigned long rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + u32 divp, val, old_base; + int state; + + divp = __ffs(cfg->p); + + if (pll->flags & TEGRA_PLLU) + divp ^= 1; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + old_base = val = pll_readl_base(pll); + val &= ~((divm_mask(pll) << pll->divm_shift) | + (divn_mask(pll) << pll->divn_shift) | + (divp_mask(pll) << pll->divp_shift)); + val |= ((cfg->m << pll->divm_shift) | + (cfg->n << pll->divn_shift) | + (divp << pll->divp_shift)); + if (val == old_base) { + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + return 0; + } + + state = clk_pll_is_enabled(hw); + + if (state) { + _clk_pll_disable(hw); + val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); + } + pll_writel_base(val, pll); + + if (pll->flags & TEGRA_PLL_HAS_CPCON) { + val = pll_readl_misc(pll); + val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); + val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; + if (pll->flags & TEGRA_PLL_SET_LFCON) { + val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); + if (cfg->n >= PLLDU_LFCON_SET_DIVN) + val |= 0x1 << PLL_MISC_LFCON_SHIFT; + } else if (pll->flags & TEGRA_PLL_SET_DCCON) { + val &= ~(0x1 << PLL_MISC_DCCON_SHIFT); + if (rate >= (pll->params->vco_max >> 1)) + val |= 0x1 << PLL_MISC_DCCON_SHIFT; + } + pll_writel_misc(val, pll); + } + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + if (state) + clk_pll_enable(hw); + + return 0; +} + +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct tegra_clk_pll_freq_table cfg; + + if (pll->flags & TEGRA_PLL_FIXED) { + if (rate != pll->fixed_rate) { + pr_err("%s: Can not change %s fixed rate %lu to %lu\n", + __func__, __clk_get_name(hw->clk), + pll->fixed_rate, rate); + return -EINVAL; + } + return 0; + } + + if (_get_table_rate(hw, &cfg, rate, parent_rate) && + _calc_rate(hw, &cfg, rate, parent_rate)) + return -EINVAL; + + return _program_pll(hw, &cfg, rate); +} + +static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + 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; + + /* PLLM is used for memory; we do not change rate */ + if (pll->flags & TEGRA_PLLM) + return __clk_get_rate(hw->clk); + + if (_get_table_rate(hw, &cfg, rate, *prate) && + _calc_rate(hw, &cfg, rate, *prate)) + return -EINVAL; + + output_rate *= cfg.n; + do_div(output_rate, cfg.m * cfg.p); + + return output_rate; +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val = pll_readl_base(pll); + u32 divn = 0, divm = 0, divp = 0; + u64 rate = parent_rate; + + if (val & PLL_BASE_BYPASS) + return parent_rate; + + if ((pll->flags & TEGRA_PLL_FIXED) && !(val & PLL_BASE_OVERRIDE)) { + struct tegra_clk_pll_freq_table sel; + if (_get_table_rate(hw, &sel, pll->fixed_rate, parent_rate)) { + pr_err("Clock %s has unknown fixed frequency\n", + __clk_get_name(hw->clk)); + BUG(); + } + return pll->fixed_rate; + } + + divp = (val >> pll->divp_shift) & (divp_mask(pll)); + if (pll->flags & TEGRA_PLLU) + divp ^= 1; + + divn = (val >> pll->divn_shift) & (divn_mask(pll)); + divm = (val >> pll->divm_shift) & (divm_mask(pll)); + divm *= (1 << divp); + + rate *= divn; + do_div(rate, divm); + return rate; +} + +static int clk_plle_training(struct tegra_clk_pll *pll) +{ + u32 val; + unsigned long timeout; + + if (!pll->pmc) + return -ENOSYS; + + /* + * PLLE is already disabled, and setup cleared; + * create falling edge on PLLE IDDQ input. + */ + val = readl(pll->pmc + PMC_SATA_PWRGT); + val |= PMC_SATA_PWRGT_PLLE_IDDQ_VALUE; + writel(val, pll->pmc + PMC_SATA_PWRGT); + + val = readl(pll->pmc + PMC_SATA_PWRGT); + val |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL; + writel(val, pll->pmc + PMC_SATA_PWRGT); + + val = readl(pll->pmc + PMC_SATA_PWRGT); + val &= ~PMC_SATA_PWRGT_PLLE_IDDQ_VALUE; + writel(val, pll->pmc + PMC_SATA_PWRGT); + + val = pll_readl_misc(pll); + + timeout = jiffies + msecs_to_jiffies(100); + while (1) { + val = pll_readl_misc(pll); + if (val & PLLE_MISC_READY) + break; + if (time_after(jiffies, timeout)) { + pr_err("%s: timeout waiting for PLLE\n", __func__); + return -EBUSY; + } + udelay(300); + } + + return 0; +} + +static int clk_plle_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk)); + struct tegra_clk_pll_freq_table sel; + u32 val; + int err; + + if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate)) + return -EINVAL; + + clk_pll_disable(hw); + + val = pll_readl_misc(pll); + val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK); + pll_writel_misc(val, pll); + + val = pll_readl_misc(pll); + if (!(val & PLLE_MISC_READY)) { + err = clk_plle_training(pll); + if (err) + return err; + } + + if (pll->flags & TEGRA_PLLE_CONFIGURE) { + /* configure dividers */ + 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.cpcon << PLLE_BASE_DIVCML_SHIFT; + pll_writel_base(val, pll); + } + + val = pll_readl_misc(pll); + val |= PLLE_MISC_SETUP_VALUE; + val |= PLLE_MISC_LOCK_ENABLE; + pll_writel_misc(val, pll); + + val = readl(pll->clk_base + PLLE_SS_CTRL); + val |= PLLE_SS_DISABLE; + writel(val, pll->clk_base + PLLE_SS_CTRL); + + val |= pll_readl_base(pll); + val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE); + pll_writel_base(val, pll); + + clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->misc_reg, + pll->params->lock_bit_idx); + return 0; +} + +static unsigned long clk_plle_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val = pll_readl_base(pll); + u32 divn = 0, divm = 0, divp = 0; + 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)); + divm *= divp; + + rate *= divn; + do_div(rate, divm); + return rate; +} + +const struct clk_ops tegra_clk_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_enable, + .disable = clk_pll_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +const struct clk_ops tegra_clk_plle_ops = { + .recalc_rate = clk_plle_recalc_rate, + .is_enabled = clk_pll_is_enabled, + .disable = clk_pll_disable, + .enable = clk_plle_enable, +}; + +static struct clk *_tegra_clk_register_pll(const char *name, + const char *parent_name, void __iomem *clk_base, + void __iomem *pmc, unsigned long flags, + unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock, + const struct clk_ops *ops) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + pll->clk_base = clk_base; + pll->pmc = pmc; + + pll->freq_table = freq_table; + pll->params = pll_params; + pll->fixed_rate = fixed_rate; + 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; + + /* Data in .init is copied by clk_register(), so stack variable OK */ + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) +{ + return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, + flags, fixed_rate, pll_params, pll_flags, freq_table, + lock, &tegra_clk_pll_ops); +} + +struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) +{ + return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, + flags, fixed_rate, pll_params, pll_flags, freq_table, + lock, &tegra_clk_plle_ops); +} diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c new file mode 100644 index 000000000000..7ad48a832334 --- /dev/null +++ b/drivers/clk/tegra/clk-super.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define SUPER_STATE_IDLE 0 +#define SUPER_STATE_RUN 1 +#define SUPER_STATE_IRQ 2 +#define SUPER_STATE_FIQ 3 + +#define SUPER_STATE_SHIFT 28 +#define SUPER_STATE_MASK ((BIT(SUPER_STATE_IDLE) | BIT(SUPER_STATE_RUN) | \ + BIT(SUPER_STATE_IRQ) | BIT(SUPER_STATE_FIQ)) \ + << SUPER_STATE_SHIFT) + +#define SUPER_LP_DIV2_BYPASS (1 << 16) + +#define super_state(s) (BIT(s) << SUPER_STATE_SHIFT) +#define super_state_to_src_shift(m, s) ((m->width * s)) +#define super_state_to_src_mask(m) (((1 << m->width) - 1)) + +static u8 clk_super_get_parent(struct clk_hw *hw) +{ + struct tegra_clk_super_mux *mux = to_clk_super_mux(hw); + u32 val, state; + u8 source, shift; + + val = readl_relaxed(mux->reg); + + state = val & SUPER_STATE_MASK; + + BUG_ON((state != super_state(SUPER_STATE_RUN)) && + (state != super_state(SUPER_STATE_IDLE))); + shift = (state == super_state(SUPER_STATE_IDLE)) ? + super_state_to_src_shift(mux, SUPER_STATE_IDLE) : + super_state_to_src_shift(mux, SUPER_STATE_RUN); + + source = (val >> shift) & super_state_to_src_mask(mux); + + /* + * If LP_DIV2_BYPASS is not set and PLLX is current parent then + * PLLX/2 is the input source to CCLKLP. + */ + if ((mux->flags & TEGRA_DIVIDER_2) && !(val & SUPER_LP_DIV2_BYPASS) && + (source == mux->pllx_index)) + source = mux->div2_index; + + return source; +} + +static int clk_super_set_parent(struct clk_hw *hw, u8 index) +{ + struct tegra_clk_super_mux *mux = to_clk_super_mux(hw); + u32 val, state; + u8 parent_index, shift; + + val = readl_relaxed(mux->reg); + state = val & SUPER_STATE_MASK; + BUG_ON((state != super_state(SUPER_STATE_RUN)) && + (state != super_state(SUPER_STATE_IDLE))); + shift = (state == super_state(SUPER_STATE_IDLE)) ? + super_state_to_src_shift(mux, SUPER_STATE_IDLE) : + super_state_to_src_shift(mux, SUPER_STATE_RUN); + + /* + * For LP mode super-clock switch between PLLX direct + * and divided-by-2 outputs is allowed only when other + * than PLLX clock source is current parent. + */ + if ((mux->flags & TEGRA_DIVIDER_2) && ((index == mux->div2_index) || + (index == mux->pllx_index))) { + parent_index = clk_super_get_parent(hw); + if ((parent_index == mux->div2_index) || + (parent_index == mux->pllx_index)) + return -EINVAL; + + val ^= SUPER_LP_DIV2_BYPASS; + writel_relaxed(val, mux->reg); + udelay(2); + + if (index == mux->div2_index) + index = mux->pllx_index; + } + val &= ~((super_state_to_src_mask(mux)) << shift); + val |= (index & (super_state_to_src_mask(mux))) << shift; + + writel_relaxed(val, mux->reg); + udelay(2); + return 0; +} + +const struct clk_ops tegra_clk_super_ops = { + .get_parent = clk_super_get_parent, + .set_parent = clk_super_set_parent, +}; + +struct clk *tegra_clk_register_super_mux(const char *name, + const char **parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 clk_super_flags, + u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock) +{ + struct tegra_clk_super_mux *super; + struct clk *clk; + struct clk_init_data init; + + super = kzalloc(sizeof(*super), GFP_KERNEL); + if (!super) { + pr_err("%s: could not allocate super clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &tegra_clk_super_ops; + init.flags = flags; + init.parent_names = parent_names; + init.num_parents = num_parents; + + super->reg = reg; + super->pllx_index = pllx_index; + super->div2_index = div2_index; + super->lock = lock; + super->width = width; + super->flags = clk_super_flags; + + /* Data in .init is copied by clk_register(), so stack variable OK */ + super->hw.init = &init; + + clk = clk_register(NULL, &super->hw); + if (IS_ERR(clk)) + kfree(super); + + return clk; +} diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c new file mode 100644 index 000000000000..cf023a937208 --- /dev/null +++ b/drivers/clk/tegra/clk.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "clk.h" + +void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, + struct clk *clks[], int clk_max) +{ + struct clk *clk; + + for (; dup_list->clk_id < clk_max; dup_list++) { + clk = clks[dup_list->clk_id]; + dup_list->lookup.clk = clk; + clkdev_add(&dup_list->lookup); + } +} + +void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, + struct clk *clks[], int clk_max) +{ + struct clk *clk; + + for (; tbl->clk_id < clk_max; tbl++) { + clk = clks[tbl->clk_id]; + if (IS_ERR_OR_NULL(clk)) + return; + + if (tbl->parent_id < clk_max) { + struct clk *parent = clks[tbl->parent_id]; + if (clk_set_parent(clk, parent)) { + pr_err("%s: Failed to set parent %s of %s\n", + __func__, __clk_get_name(parent), + __clk_get_name(clk)); + WARN_ON(1); + } + } + + if (tbl->rate) + if (clk_set_rate(clk, tbl->rate)) { + pr_err("%s: Failed to set rate %lu of %s\n", + __func__, tbl->rate, + __clk_get_name(clk)); + WARN_ON(1); + } + + if (tbl->state) + if (clk_prepare_enable(clk)) { + pr_err("%s: Failed to enable %s\n", __func__, + __clk_get_name(clk)); + WARN_ON(1); + } + } +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h new file mode 100644 index 000000000000..5fcfa51d6b17 --- /dev/null +++ b/drivers/clk/tegra/clk.h @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TEGRA_CLK_H +#define __TEGRA_CLK_H + +#include +#include + +/** + * struct tegra_clk_sync_source - external clock source from codec + * + * @hw: handle between common and hardware-specific interfaces + * @rate: input frequency from source + * @max_rate: max rate allowed + */ +struct tegra_clk_sync_source { + struct clk_hw hw; + unsigned long rate; + unsigned long max_rate; +}; + +#define to_clk_sync_source(_hw) \ + container_of(_hw, struct tegra_clk_sync_source, hw) + +extern const struct clk_ops tegra_clk_sync_source_ops; +struct clk *tegra_clk_register_sync_source(const char *name, + unsigned long fixed_rate, unsigned long max_rate); + +/** + * struct tegra_clk_frac_div - fractional divider clock + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing divider + * @flags: hardware-specific flags + * @shift: shift to the divider bit field + * @width: width of the divider bit field + * @frac_width: width of the fractional bit field + * @lock: register lock + * + * Flags: + * TEGRA_DIVIDER_ROUND_UP - This flags indicates to round up the divider value. + * TEGRA_DIVIDER_FIXED - Fixed rate PLL dividers has addition override bit, this + * flag indicates that this divider is for fixed rate PLL. + * TEGRA_DIVIDER_INT - Some modules can not cope with the duty cycle when + * fraction bit is set. This flags indicates to calculate divider for which + * fracton bit will be zero. + * TEGRA_DIVIDER_UART - UART module divider has additional enable bit which is + * set when divider value is not 0. This flags indicates that the divider + * is for UART module. + */ +struct tegra_clk_frac_div { + struct clk_hw hw; + void __iomem *reg; + u8 flags; + u8 shift; + u8 width; + u8 frac_width; + spinlock_t *lock; +}; + +#define to_clk_frac_div(_hw) container_of(_hw, struct tegra_clk_frac_div, hw) + +#define TEGRA_DIVIDER_ROUND_UP BIT(0) +#define TEGRA_DIVIDER_FIXED BIT(1) +#define TEGRA_DIVIDER_INT BIT(2) +#define TEGRA_DIVIDER_UART BIT(3) + +extern const struct clk_ops tegra_clk_frac_div_ops; +struct clk *tegra_clk_register_divider(const char *name, + const char *parent_name, void __iomem *reg, + unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, + u8 frac_width, spinlock_t *lock); + +/* + * Tegra PLL: + * + * In general, there are 3 requirements for each PLL + * that SW needs to be comply with. + * (1) Input frequency range (REF). + * (2) Comparison frequency range (CF). CF = REF/DIVM. + * (3) VCO frequency range (VCO). VCO = CF * DIVN. + * + * The final PLL output frequency (FO) = VCO >> DIVP. + */ + +/** + * struct tegra_clk_pll_freq_table - PLL frequecy table + * + * @input_rate: input rate from source + * @output_rate: output rate from PLL for the input rate + * @n: feedback divider + * @m: input divider + * @p: post divider + * @cpcon: charge pump current + */ +struct tegra_clk_pll_freq_table { + unsigned long input_rate; + unsigned long output_rate; + u16 n; + u16 m; + u8 p; + u8 cpcon; +}; + +/** + * struct clk_pll_params - PLL parameters + * + * @input_min: Minimum input frequency + * @input_max: Maximum input frequency + * @cf_min: Minimum comparison frequency + * @cf_max: Maximum comparison frequency + * @vco_min: Minimum VCO frequency + * @vco_max: Maximum VCO frequency + * @base_reg: PLL base reg offset + * @misc_reg: PLL misc reg offset + * @lock_reg: PLL lock reg offset + * @lock_bit_idx: Bit index for PLL lock status + * @lock_enable_bit_idx: Bit index to enable PLL lock + * @lock_delay: Delay in us if PLL lock is not used + */ +struct tegra_clk_pll_params { + unsigned long input_min; + unsigned long input_max; + unsigned long cf_min; + unsigned long cf_max; + unsigned long vco_min; + unsigned long vco_max; + + u32 base_reg; + u32 misc_reg; + u32 lock_reg; + u32 lock_bit_idx; + u32 lock_enable_bit_idx; + int lock_delay; +}; + +/** + * struct tegra_clk_pll - Tegra PLL clock + * + * @hw: handle between common and hardware-specifix interfaces + * @clk_base: address of CAR controller + * @pmc: address of PMC, required to read override bits + * @freq_table: array of frequencies supported by PLL + * @params: PLL parameters + * @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 + * PLL locking. If not set it will use lock_delay value to wait. + * TEGRA_PLL_HAS_CPCON - This flag indicates that CPCON value needs + * to be programmed to change output frequency of the PLL. + * TEGRA_PLL_SET_LFCON - This flag indicates that LFCON value needs + * to be programmed to change output frequency of the PLL. + * TEGRA_PLL_SET_DCCON - This flag indicates that DCCON value needs + * to be programmed to change output frequency of the PLL. + * TEGRA_PLLU - PLLU has inverted post divider. This flags indicated + * that it is PLLU and invert post divider value. + * TEGRA_PLLM - PLLM has additional override settings in PMC. This + * flag indicates that it is PLLM and use override settings. + * TEGRA_PLL_FIXED - We are not supposed to change output frequency + * of some plls. + * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. + */ +struct tegra_clk_pll { + struct clk_hw hw; + void __iomem *clk_base; + void __iomem *pmc; + u8 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; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct tegra_clk_pll, hw) + +#define TEGRA_PLL_USE_LOCK BIT(0) +#define TEGRA_PLL_HAS_CPCON BIT(1) +#define TEGRA_PLL_SET_LFCON BIT(2) +#define TEGRA_PLL_SET_DCCON BIT(3) +#define TEGRA_PLLU BIT(4) +#define TEGRA_PLLM BIT(5) +#define TEGRA_PLL_FIXED BIT(6) +#define TEGRA_PLLE_CONFIGURE BIT(7) + +extern const struct clk_ops tegra_clk_pll_ops; +extern const struct clk_ops tegra_clk_plle_ops; +struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); +struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); + +/** + * struct tegra_clk_pll_out - PLL divider down clock + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing the PLL divider + * @enb_bit_idx: bit to enable/disable PLL divider + * @rst_bit_idx: bit to reset PLL divider + * @lock: register lock + * @flags: hardware-specific flags + */ +struct tegra_clk_pll_out { + struct clk_hw hw; + void __iomem *reg; + u8 enb_bit_idx; + u8 rst_bit_idx; + spinlock_t *lock; + u8 flags; +}; + +#define to_clk_pll_out(_hw) container_of(_hw, struct tegra_clk_pll_out, hw) + +extern const struct clk_ops tegra_clk_pll_out_ops; +struct clk *tegra_clk_register_pll_out(const char *name, + const char *parent_name, void __iomem *reg, u8 enb_bit_idx, + u8 rst_bit_idx, unsigned long flags, u8 pll_div_flags, + spinlock_t *lock); + +/** + * struct tegra_clk_periph_regs - Registers controlling peripheral clock + * + * @enb_reg: read the enable status + * @enb_set_reg: write 1 to enable clock + * @enb_clr_reg: write 1 to disable clock + * @rst_reg: read the reset status + * @rst_set_reg: write 1 to assert the reset of peripheral + * @rst_clr_reg: write 1 to deassert the reset of peripheral + */ +struct tegra_clk_periph_regs { + u32 enb_reg; + u32 enb_set_reg; + u32 enb_clr_reg; + u32 rst_reg; + u32 rst_set_reg; + u32 rst_clr_reg; +}; + +/** + * struct tegra_clk_periph_gate - peripheral gate clock + * + * @magic: magic number to validate type + * @hw: handle between common and hardware-specific interfaces + * @clk_base: address of CAR controller + * @regs: Registers to control the peripheral + * @flags: hardware-specific flags + * @clk_num: Clock number + * @enable_refcnt: array to maintain reference count of the clock + * + * Flags: + * TEGRA_PERIPH_NO_RESET - This flag indicates that reset is not allowed + * for this module. + * TEGRA_PERIPH_MANUAL_RESET - This flag indicates not to reset module + * after clock enable and driver for the module is responsible for + * doing reset. + * TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the + * bus to flush the write operation in apb bus. This flag indicates + * that this peripheral is in apb bus. + */ +struct tegra_clk_periph_gate { + u32 magic; + struct clk_hw hw; + void __iomem *clk_base; + u8 flags; + int clk_num; + int *enable_refcnt; + struct tegra_clk_periph_regs *regs; +}; + +#define to_clk_periph_gate(_hw) \ + container_of(_hw, struct tegra_clk_periph_gate, hw) + +#define TEGRA_CLK_PERIPH_GATE_MAGIC 0x17760309 + +#define TEGRA_PERIPH_NO_RESET BIT(0) +#define TEGRA_PERIPH_MANUAL_RESET BIT(1) +#define TEGRA_PERIPH_ON_APB BIT(2) + +void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert); +extern const struct clk_ops tegra_clk_periph_gate_ops; +struct clk *tegra_clk_register_periph_gate(const char *name, + const char *parent_name, u8 gate_flags, void __iomem *clk_base, + unsigned long flags, int clk_num, + struct tegra_clk_periph_regs *pregs, int *enable_refcnt); + +/** + * struct clk-periph - peripheral clock + * + * @magic: magic number to validate type + * @hw: handle between common and hardware-specific interfaces + * @mux: mux clock + * @divider: divider clock + * @gate: gate clock + * @mux_ops: mux clock ops + * @div_ops: divider clock ops + * @gate_ops: gate clock ops + */ +struct tegra_clk_periph { + u32 magic; + struct clk_hw hw; + struct clk_mux mux; + struct tegra_clk_frac_div divider; + struct tegra_clk_periph_gate gate; + + const struct clk_ops *mux_ops; + const struct clk_ops *div_ops; + const struct clk_ops *gate_ops; +}; + +#define to_clk_periph(_hw) container_of(_hw, struct tegra_clk_periph, hw) + +#define TEGRA_CLK_PERIPH_MAGIC 0x18221223 + +extern const struct clk_ops tegra_clk_periph_ops; +struct clk *tegra_clk_register_periph(const char *name, + const char **parent_names, int num_parents, + struct tegra_clk_periph *periph, void __iomem *clk_base, + u32 offset); +struct clk *tegra_clk_register_periph_nodiv(const char *name, + const char **parent_names, int num_parents, + struct tegra_clk_periph *periph, void __iomem *clk_base, + u32 offset); + +#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags, \ + _div_shift, _div_width, _div_frac_width, \ + _div_flags, _clk_num, _enb_refcnt, _regs, \ + _gate_flags) \ + { \ + .mux = { \ + .flags = _mux_flags, \ + .shift = _mux_shift, \ + .width = _mux_width, \ + }, \ + .divider = { \ + .flags = _div_flags, \ + .shift = _div_shift, \ + .width = _div_width, \ + .frac_width = _div_frac_width, \ + }, \ + .gate = { \ + .flags = _gate_flags, \ + .clk_num = _clk_num, \ + .enable_refcnt = _enb_refcnt, \ + .regs = _regs, \ + }, \ + .mux_ops = &clk_mux_ops, \ + .div_ops = &tegra_clk_frac_div_ops, \ + .gate_ops = &tegra_clk_periph_gate_ops, \ + } + +struct tegra_periph_init_data { + const char *name; + int clk_id; + const char **parent_names; + int num_parents; + struct tegra_clk_periph periph; + u32 offset; + const char *con_id; + const char *dev_id; +}; + +#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \ + _mux_shift, _mux_width, _mux_flags, _div_shift, \ + _div_width, _div_frac_width, _div_flags, _regs, \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + { \ + .name = _name, \ + .clk_id = _clk_id, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names), \ + .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \ + _mux_flags, _div_shift, \ + _div_width, _div_frac_width, \ + _div_flags, _clk_num, \ + _enb_refcnt, _regs, \ + _gate_flags), \ + .offset = _offset, \ + .con_id = _con_id, \ + .dev_id = _dev_id, \ + } + +/** + * struct clk_super_mux - super clock + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register controlling multiplexer + * @width: width of the multiplexer bit field + * @flags: hardware-specific flags + * @div2_index: bit controlling divide-by-2 + * @pllx_index: PLLX index in the parent list + * @lock: register lock + * + * Flags: + * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates + * that this is LP cluster clock. + */ +struct tegra_clk_super_mux { + struct clk_hw hw; + void __iomem *reg; + u8 width; + u8 flags; + u8 div2_index; + u8 pllx_index; + spinlock_t *lock; +}; + +#define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw) + +#define TEGRA_DIVIDER_2 BIT(0) + +extern const struct clk_ops tegra_clk_super_ops; +struct clk *tegra_clk_register_super_mux(const char *name, + const char **parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 clk_super_flags, + u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock); + +/** + * struct clk_init_tabel - clock initialization table + * @clk_id: clock id as mentioned in device tree bindings + * @parent_id: parent clock id as mentioned in device tree bindings + * @rate: rate to set + * @state: enable/disable + */ +struct tegra_clk_init_table { + unsigned int clk_id; + unsigned int parent_id; + unsigned long rate; + int state; +}; + +/** + * struct clk_duplicate - duplicate clocks + * @clk_id: clock id as mentioned in device tree bindings + * @lookup: duplicate lookup entry for the clock + */ +struct tegra_clk_duplicate { + int clk_id; + struct clk_lookup lookup; +}; + +#define TEGRA_CLK_DUPLICATE(_clk_id, _dev, _con) \ + { \ + .clk_id = _clk_id, \ + .lookup = { \ + .dev_id = _dev, \ + .con_id = _con, \ + }, \ + } + +void tegra_init_from_table(struct tegra_clk_init_table *tbl, + struct clk *clks[], int clk_max); + +void tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, + struct clk *clks[], int clk_max); + +#endif /* TEGRA_CLK_H */ -- cgit v1.2.3 From 37c26a906527b8a6a252614ca83d21ad318c4e84 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Fri, 11 Jan 2013 13:16:24 +0530 Subject: clk: tegra: add clock support for Tegra20 Add Tegra20 clock support based on common clock framework. Signed-off-by: Prashant Gaikwad [swarren: s/1GHz/100MHz/ in call to tegra_clk_plle() to fix PCIe, implemented KBC clock, ensure all OF lookups return valid cookies i.e. an explicit error pointer or valid pointer not NULL, adapt to renames in earlier patches, fixed some checkpatch issues.] Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/Makefile | 2 + drivers/clk/tegra/clk-tegra20.c | 1273 +++++++++++++++++++++++++++++++++++++++ drivers/clk/tegra/clk.h | 6 + 3 files changed, 1281 insertions(+) create mode 100644 drivers/clk/tegra/clk-tegra20.c (limited to 'drivers') diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 68bd353a2108..00484fd4e515 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -6,3 +6,5 @@ obj-y += clk-periph-gate.o obj-y += clk-pll.o obj-y += clk-pll-out.o obj-y += clk-super.o + +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c new file mode 100644 index 000000000000..4ae8287b0f81 --- /dev/null +++ b/drivers/clk/tegra/clk-tegra20.c @@ -0,0 +1,1273 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define RST_DEVICES_L 0x004 +#define RST_DEVICES_H 0x008 +#define RST_DEVICES_U 0x00c +#define RST_DEVICES_SET_L 0x300 +#define RST_DEVICES_CLR_L 0x304 +#define RST_DEVICES_SET_H 0x308 +#define RST_DEVICES_CLR_H 0x30c +#define RST_DEVICES_SET_U 0x310 +#define RST_DEVICES_CLR_U 0x314 +#define RST_DEVICES_NUM 3 + +#define CLK_OUT_ENB_L 0x010 +#define CLK_OUT_ENB_H 0x014 +#define CLK_OUT_ENB_U 0x018 +#define CLK_OUT_ENB_SET_L 0x320 +#define CLK_OUT_ENB_CLR_L 0x324 +#define CLK_OUT_ENB_SET_H 0x328 +#define CLK_OUT_ENB_CLR_H 0x32c +#define CLK_OUT_ENB_SET_U 0x330 +#define CLK_OUT_ENB_CLR_U 0x334 +#define CLK_OUT_ENB_NUM 3 + +#define OSC_CTRL 0x50 +#define OSC_CTRL_OSC_FREQ_MASK (3<<30) +#define OSC_CTRL_OSC_FREQ_13MHZ (0<<30) +#define OSC_CTRL_OSC_FREQ_19_2MHZ (1<<30) +#define OSC_CTRL_OSC_FREQ_12MHZ (2<<30) +#define OSC_CTRL_OSC_FREQ_26MHZ (3<<30) +#define OSC_CTRL_MASK (0x3f2 | OSC_CTRL_OSC_FREQ_MASK) + +#define OSC_CTRL_PLL_REF_DIV_MASK (3<<28) +#define OSC_CTRL_PLL_REF_DIV_1 (0<<28) +#define OSC_CTRL_PLL_REF_DIV_2 (1<<28) +#define OSC_CTRL_PLL_REF_DIV_4 (2<<28) + +#define OSC_FREQ_DET 0x58 +#define OSC_FREQ_DET_TRIG (1<<31) + +#define OSC_FREQ_DET_STATUS 0x5c +#define OSC_FREQ_DET_BUSY (1<<31) +#define OSC_FREQ_DET_CNT_MASK 0xFFFF + +#define PLLS_BASE 0xf0 +#define PLLS_MISC 0xf4 +#define PLLC_BASE 0x80 +#define PLLC_MISC 0x8c +#define PLLM_BASE 0x90 +#define PLLM_MISC 0x9c +#define PLLP_BASE 0xa0 +#define PLLP_MISC 0xac +#define PLLA_BASE 0xb0 +#define PLLA_MISC 0xbc +#define PLLU_BASE 0xc0 +#define PLLU_MISC 0xcc +#define PLLD_BASE 0xd0 +#define PLLD_MISC 0xdc +#define PLLX_BASE 0xe0 +#define PLLX_MISC 0xe4 +#define PLLE_BASE 0xe8 +#define PLLE_MISC 0xec + +#define PLL_BASE_LOCK 27 +#define PLLE_MISC_LOCK 11 + +#define PLL_MISC_LOCK_ENABLE 18 +#define PLLDU_MISC_LOCK_ENABLE 22 +#define PLLE_MISC_LOCK_ENABLE 9 + +#define PLLC_OUT 0x84 +#define PLLM_OUT 0x94 +#define PLLP_OUTA 0xa4 +#define PLLP_OUTB 0xa8 +#define PLLA_OUT 0xb4 + +#define CCLK_BURST_POLICY 0x20 +#define SUPER_CCLK_DIVIDER 0x24 +#define SCLK_BURST_POLICY 0x28 +#define SUPER_SCLK_DIVIDER 0x2c +#define CLK_SYSTEM_RATE 0x30 + +#define CLK_SOURCE_I2S1 0x100 +#define CLK_SOURCE_I2S2 0x104 +#define CLK_SOURCE_SPDIF_OUT 0x108 +#define CLK_SOURCE_SPDIF_IN 0x10c +#define CLK_SOURCE_PWM 0x110 +#define CLK_SOURCE_SPI 0x114 +#define CLK_SOURCE_SBC1 0x134 +#define CLK_SOURCE_SBC2 0x118 +#define CLK_SOURCE_SBC3 0x11c +#define CLK_SOURCE_SBC4 0x1b4 +#define CLK_SOURCE_XIO 0x120 +#define CLK_SOURCE_TWC 0x12c +#define CLK_SOURCE_IDE 0x144 +#define CLK_SOURCE_NDFLASH 0x160 +#define CLK_SOURCE_VFIR 0x168 +#define CLK_SOURCE_SDMMC1 0x150 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC3 0x1bc +#define CLK_SOURCE_SDMMC4 0x164 +#define CLK_SOURCE_CVE 0x140 +#define CLK_SOURCE_TVO 0x188 +#define CLK_SOURCE_TVDAC 0x194 +#define CLK_SOURCE_HDMI 0x18c +#define CLK_SOURCE_DISP1 0x138 +#define CLK_SOURCE_DISP2 0x13c +#define CLK_SOURCE_CSITE 0x1d4 +#define CLK_SOURCE_LA 0x1f8 +#define CLK_SOURCE_OWR 0x1cc +#define CLK_SOURCE_NOR 0x1d0 +#define CLK_SOURCE_MIPI 0x174 +#define CLK_SOURCE_I2C1 0x124 +#define CLK_SOURCE_I2C2 0x198 +#define CLK_SOURCE_I2C3 0x1b8 +#define CLK_SOURCE_DVC 0x128 +#define CLK_SOURCE_UARTA 0x178 +#define CLK_SOURCE_UARTB 0x17c +#define CLK_SOURCE_UARTC 0x1a0 +#define CLK_SOURCE_UARTD 0x1c0 +#define CLK_SOURCE_UARTE 0x1c4 +#define CLK_SOURCE_3D 0x158 +#define CLK_SOURCE_2D 0x15c +#define CLK_SOURCE_MPE 0x170 +#define CLK_SOURCE_EPP 0x16c +#define CLK_SOURCE_HOST1X 0x180 +#define CLK_SOURCE_VDE 0x1c8 +#define CLK_SOURCE_VI 0x148 +#define CLK_SOURCE_VI_SENSOR 0x1a8 +#define CLK_SOURCE_EMC 0x19c + +#define AUDIO_SYNC_CLK 0x38 + +#define PMC_CTRL 0x0 +#define PMC_CTRL_BLINK_ENB 7 +#define PMC_DPD_PADS_ORIDE 0x1c +#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20 +#define PMC_BLINK_TIMER 0x40 + +/* Tegra CPU clock and reset control regs */ +#define TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4c +#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET 0x340 +#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344 + +#define CPU_CLOCK(cpu) (0x1 << (8 + cpu)) +#define CPU_RESET(cpu) (0x1111ul << (cpu)) + +static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; + +static void __iomem *clk_base; +static void __iomem *pmc_base; + +static DEFINE_SPINLOCK(pll_div_lock); + +#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, \ + _regs, _clk_num, periph_clk_enb_refcnt, \ + _gate_flags, _clk_id) + +#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id) + +#define TEGRA_INIT_DATA_DIV16(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + 30, 2, 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id) + +#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \ + _mux_shift, _mux_width, _clk_num, _regs, \ + _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + _mux_shift, _mux_width, 0, 0, 0, 0, 0, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id) + +/* IDs assigned here must be in sync with DT bindings definition + * for Tegra20 clocks . + */ +enum tegra20_clk { + cpu, ac97 = 3, rtc, timer, uarta, gpio = 8, sdmmc2, i2s1 = 11, i2c1, + ndflash, sdmmc1, sdmmc4, twc, pwm, i2s2, epp, gr2d = 21, usbd, isp, + gr3d, ide, disp2, disp1, host1x, vcp, cache2 = 31, mem, ahbdma, apbdma, + kbc = 36, stat_mon, pmc, fuse, kfuse, sbc1, nor, spi, sbc2, xio, sbc3, + dvc, dsi, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2, + usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, + pex, owr, afi, csite, pcie_xclk, avpucq = 75, la, irama = 84, iramb, + iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev1, cdev2, + uartb = 96, vfir, spdif_in, spdif_out, vi, vi_sensor, tvo, cve, + osc, clk_32k, clk_m, sclk, cclk, hclk, pclk, blink, pll_a, pll_a_out0, + pll_c, pll_c_out1, pll_d, pll_d_out0, pll_e, pll_m, pll_m_out1, + pll_p, pll_p_out1, pll_p_out2, pll_p_out3, pll_p_out4, pll_u, + pll_x, audio, pll_ref, twd, clk_max, +}; + +static struct clk *clks[clk_max]; +static struct clk_onecell_data clk_data; + +static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { + { 12000000, 600000000, 600, 12, 1, 8 }, + { 13000000, 600000000, 600, 13, 1, 8 }, + { 19200000, 600000000, 500, 16, 1, 6 }, + { 26000000, 600000000, 600, 26, 1, 8 }, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { + { 12000000, 666000000, 666, 12, 1, 8}, + { 13000000, 666000000, 666, 13, 1, 8}, + { 19200000, 666000000, 555, 16, 1, 8}, + { 26000000, 666000000, 666, 26, 1, 8}, + { 12000000, 600000000, 600, 12, 1, 8}, + { 13000000, 600000000, 600, 13, 1, 8}, + { 19200000, 600000000, 375, 12, 1, 6}, + { 26000000, 600000000, 600, 26, 1, 8}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { + { 12000000, 216000000, 432, 12, 2, 8}, + { 13000000, 216000000, 432, 13, 2, 8}, + { 19200000, 216000000, 90, 4, 2, 1}, + { 26000000, 216000000, 432, 26, 2, 8}, + { 12000000, 432000000, 432, 12, 1, 8}, + { 13000000, 432000000, 432, 13, 1, 8}, + { 19200000, 432000000, 90, 4, 1, 1}, + { 26000000, 432000000, 432, 26, 1, 8}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { + { 28800000, 56448000, 49, 25, 1, 1}, + { 28800000, 73728000, 64, 25, 1, 1}, + { 28800000, 24000000, 5, 6, 1, 1}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { + { 12000000, 216000000, 216, 12, 1, 4}, + { 13000000, 216000000, 216, 13, 1, 4}, + { 19200000, 216000000, 135, 12, 1, 3}, + { 26000000, 216000000, 216, 26, 1, 4}, + + { 12000000, 594000000, 594, 12, 1, 8}, + { 13000000, 594000000, 594, 13, 1, 8}, + { 19200000, 594000000, 495, 16, 1, 8}, + { 26000000, 594000000, 594, 26, 1, 8}, + + { 12000000, 1000000000, 1000, 12, 1, 12}, + { 13000000, 1000000000, 1000, 13, 1, 12}, + { 19200000, 1000000000, 625, 12, 1, 8}, + { 26000000, 1000000000, 1000, 26, 1, 12}, + + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { + { 12000000, 480000000, 960, 12, 2, 0}, + { 13000000, 480000000, 960, 13, 2, 0}, + { 19200000, 480000000, 200, 4, 2, 0}, + { 26000000, 480000000, 960, 26, 2, 0}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { + /* 1 GHz */ + { 12000000, 1000000000, 1000, 12, 1, 12}, + { 13000000, 1000000000, 1000, 13, 1, 12}, + { 19200000, 1000000000, 625, 12, 1, 8}, + { 26000000, 1000000000, 1000, 26, 1, 12}, + + /* 912 MHz */ + { 12000000, 912000000, 912, 12, 1, 12}, + { 13000000, 912000000, 912, 13, 1, 12}, + { 19200000, 912000000, 760, 16, 1, 8}, + { 26000000, 912000000, 912, 26, 1, 12}, + + /* 816 MHz */ + { 12000000, 816000000, 816, 12, 1, 12}, + { 13000000, 816000000, 816, 13, 1, 12}, + { 19200000, 816000000, 680, 16, 1, 8}, + { 26000000, 816000000, 816, 26, 1, 12}, + + /* 760 MHz */ + { 12000000, 760000000, 760, 12, 1, 12}, + { 13000000, 760000000, 760, 13, 1, 12}, + { 19200000, 760000000, 950, 24, 1, 8}, + { 26000000, 760000000, 760, 26, 1, 12}, + + /* 750 MHz */ + { 12000000, 750000000, 750, 12, 1, 12}, + { 13000000, 750000000, 750, 13, 1, 12}, + { 19200000, 750000000, 625, 16, 1, 8}, + { 26000000, 750000000, 750, 26, 1, 12}, + + /* 608 MHz */ + { 12000000, 608000000, 608, 12, 1, 12}, + { 13000000, 608000000, 608, 13, 1, 12}, + { 19200000, 608000000, 380, 12, 1, 8}, + { 26000000, 608000000, 608, 26, 1, 12}, + + /* 456 MHz */ + { 12000000, 456000000, 456, 12, 1, 12}, + { 13000000, 456000000, 456, 13, 1, 12}, + { 19200000, 456000000, 380, 16, 1, 8}, + { 26000000, 456000000, 456, 26, 1, 12}, + + /* 312 MHz */ + { 12000000, 312000000, 312, 12, 1, 12}, + { 13000000, 312000000, 312, 13, 1, 12}, + { 19200000, 312000000, 260, 16, 1, 8}, + { 26000000, 312000000, 312, 26, 1, 12}, + + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { + { 12000000, 100000000, 200, 24, 1, 0 }, + { 0, 0, 0, 0, 0, 0 }, +}; + +/* PLL parameters */ +static struct tegra_clk_pll_params pll_c_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1400000000, + .base_reg = PLLC_BASE, + .misc_reg = PLLC_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_m_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1200000000, + .base_reg = PLLM_BASE, + .misc_reg = PLLM_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_p_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1400000000, + .base_reg = PLLP_BASE, + .misc_reg = PLLP_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_a_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1400000000, + .base_reg = PLLA_BASE, + .misc_reg = PLLA_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_d_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 40000000, + .vco_max = 1000000000, + .base_reg = PLLD_BASE, + .misc_reg = PLLD_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_u_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 48000000, + .vco_max = 960000000, + .base_reg = PLLU_BASE, + .misc_reg = PLLU_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_x_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1200000000, + .base_reg = PLLX_BASE, + .misc_reg = PLLX_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_e_params = { + .input_min = 12000000, + .input_max = 12000000, + .cf_min = 0, + .cf_max = 0, + .vco_min = 0, + .vco_max = 0, + .base_reg = PLLE_BASE, + .misc_reg = PLLE_MISC, + .lock_bit_idx = PLLE_MISC_LOCK, + .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, + .lock_delay = 0, +}; + +/* Peripheral clock registers */ +static struct tegra_clk_periph_regs periph_l_regs = { + .enb_reg = CLK_OUT_ENB_L, + .enb_set_reg = CLK_OUT_ENB_SET_L, + .enb_clr_reg = CLK_OUT_ENB_CLR_L, + .rst_reg = RST_DEVICES_L, + .rst_set_reg = RST_DEVICES_SET_L, + .rst_clr_reg = RST_DEVICES_CLR_L, +}; + +static struct tegra_clk_periph_regs periph_h_regs = { + .enb_reg = CLK_OUT_ENB_H, + .enb_set_reg = CLK_OUT_ENB_SET_H, + .enb_clr_reg = CLK_OUT_ENB_CLR_H, + .rst_reg = RST_DEVICES_H, + .rst_set_reg = RST_DEVICES_SET_H, + .rst_clr_reg = RST_DEVICES_CLR_H, +}; + +static struct tegra_clk_periph_regs periph_u_regs = { + .enb_reg = CLK_OUT_ENB_U, + .enb_set_reg = CLK_OUT_ENB_SET_U, + .enb_clr_reg = CLK_OUT_ENB_CLR_U, + .rst_reg = RST_DEVICES_U, + .rst_set_reg = RST_DEVICES_SET_U, + .rst_clr_reg = RST_DEVICES_CLR_U, +}; + +static unsigned long tegra20_clk_measure_input_freq(void) +{ + u32 osc_ctrl = readl_relaxed(clk_base + OSC_CTRL); + u32 auto_clk_control = osc_ctrl & OSC_CTRL_OSC_FREQ_MASK; + u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK; + unsigned long input_freq; + + switch (auto_clk_control) { + case OSC_CTRL_OSC_FREQ_12MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 12000000; + break; + case OSC_CTRL_OSC_FREQ_13MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 13000000; + break; + case OSC_CTRL_OSC_FREQ_19_2MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 19200000; + break; + case OSC_CTRL_OSC_FREQ_26MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 26000000; + break; + default: + pr_err("Unexpected clock autodetect value %d", + auto_clk_control); + BUG(); + return 0; + } + + return input_freq; +} + +static unsigned int tegra20_get_pll_ref_div(void) +{ + u32 pll_ref_div = readl_relaxed(clk_base + OSC_CTRL) & + OSC_CTRL_PLL_REF_DIV_MASK; + + switch (pll_ref_div) { + case OSC_CTRL_PLL_REF_DIV_1: + return 1; + case OSC_CTRL_PLL_REF_DIV_2: + return 2; + case OSC_CTRL_PLL_REF_DIV_4: + return 4; + default: + pr_err("Invalied pll ref divider %d\n", pll_ref_div); + BUG(); + } + return 0; +} + +static void tegra20_pll_init(void) +{ + struct clk *clk; + + /* PLLC */ + clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, NULL, 0, + 0, &pll_c_params, TEGRA_PLL_HAS_CPCON, + pll_c_freq_table, NULL); + clk_register_clkdev(clk, "pll_c", NULL); + clks[pll_c] = clk; + + /* PLLC_OUT1 */ + clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c", + clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div", + clk_base + PLLC_OUT, 1, 0, CLK_SET_RATE_PARENT, + 0, NULL); + clk_register_clkdev(clk, "pll_c_out1", NULL); + clks[pll_c_out1] = clk; + + /* PLLP */ + clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, NULL, 0, + 216000000, &pll_p_params, TEGRA_PLL_FIXED | + TEGRA_PLL_HAS_CPCON, pll_p_freq_table, NULL); + clk_register_clkdev(clk, "pll_p", NULL); + clks[pll_p] = clk; + + /* PLLP_OUT1 */ + clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p", + clk_base + PLLP_OUTA, 0, + TEGRA_DIVIDER_FIXED | TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div", + clk_base + PLLP_OUTA, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out1", NULL); + clks[pll_p_out1] = clk; + + /* PLLP_OUT2 */ + clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p", + clk_base + PLLP_OUTA, 0, + TEGRA_DIVIDER_FIXED | TEGRA_DIVIDER_ROUND_UP, + 24, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div", + clk_base + PLLP_OUTA, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out2", NULL); + clks[pll_p_out2] = clk; + + /* PLLP_OUT3 */ + clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p", + clk_base + PLLP_OUTB, 0, + TEGRA_DIVIDER_FIXED | TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div", + clk_base + PLLP_OUTB, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out3", NULL); + clks[pll_p_out3] = clk; + + /* PLLP_OUT4 */ + clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p", + clk_base + PLLP_OUTB, 0, + TEGRA_DIVIDER_FIXED | TEGRA_DIVIDER_ROUND_UP, + 24, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div", + clk_base + PLLP_OUTB, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out4", NULL); + clks[pll_p_out4] = clk; + + /* PLLM */ + clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, NULL, + CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0, + &pll_m_params, TEGRA_PLL_HAS_CPCON, + pll_m_freq_table, NULL); + clk_register_clkdev(clk, "pll_m", NULL); + clks[pll_m] = clk; + + /* PLLM_OUT1 */ + clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m", + clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div", + clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_m_out1", NULL); + clks[pll_m_out1] = clk; + + /* PLLX */ + clk = tegra_clk_register_pll("pll_x", "pll_ref", clk_base, NULL, 0, + 0, &pll_x_params, TEGRA_PLL_HAS_CPCON, + pll_x_freq_table, NULL); + clk_register_clkdev(clk, "pll_x", NULL); + clks[pll_x] = clk; + + /* PLLU */ + clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, NULL, 0, + 0, &pll_u_params, TEGRA_PLLU | TEGRA_PLL_HAS_CPCON, + pll_u_freq_table, NULL); + clk_register_clkdev(clk, "pll_u", NULL); + clks[pll_u] = clk; + + /* PLLD */ + clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, NULL, 0, + 0, &pll_d_params, TEGRA_PLL_HAS_CPCON, + pll_d_freq_table, NULL); + clk_register_clkdev(clk, "pll_d", NULL); + clks[pll_d] = clk; + + /* PLLD_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d_out0", NULL); + clks[pll_d_out0] = clk; + + /* PLLA */ + clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, NULL, 0, + 0, &pll_a_params, TEGRA_PLL_HAS_CPCON, + pll_a_freq_table, NULL); + clk_register_clkdev(clk, "pll_a", NULL); + clks[pll_a] = clk; + + /* PLLA_OUT0 */ + clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a", + clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div", + clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_a_out0", NULL); + clks[pll_a_out0] = clk; + + /* PLLE */ + clk = tegra_clk_register_plle("pll_e", "pll_ref", clk_base, NULL, + 0, 100000000, &pll_e_params, + 0, pll_e_freq_table, NULL); + clk_register_clkdev(clk, "pll_e", NULL); + clks[pll_e] = clk; +} + +static const char *cclk_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p_cclk", "pll_p_out4_cclk", + "pll_p_out3_cclk", "clk_d", "pll_x" }; +static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", + "pll_p_out3", "pll_p_out2", "clk_d", + "clk_32k", "pll_m_out1" }; + +static void tegra20_super_clk_init(void) +{ + struct clk *clk; + + /* + * DIV_U71 dividers for CCLK, these dividers are used only + * if parent clock is fixed rate. + */ + + /* + * Clock input to cclk divided from pll_p using + * U71 divider of cclk. + */ + clk = tegra_clk_register_divider("pll_p_cclk", "pll_p", + clk_base + SUPER_CCLK_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_cclk", NULL); + + /* + * Clock input to cclk divided from pll_p_out3 using + * U71 divider of cclk. + */ + clk = tegra_clk_register_divider("pll_p_out3_cclk", "pll_p_out3", + clk_base + SUPER_CCLK_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_out3_cclk", NULL); + + /* + * Clock input to cclk divided from pll_p_out4 using + * U71 divider of cclk. + */ + clk = tegra_clk_register_divider("pll_p_out4_cclk", "pll_p_out4", + clk_base + SUPER_CCLK_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_out4_cclk", NULL); + + /* CCLK */ + clk = tegra_clk_register_super_mux("cclk", cclk_parents, + ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT, + clk_base + CCLK_BURST_POLICY, 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "cclk", NULL); + clks[cclk] = clk; + + /* SCLK */ + clk = tegra_clk_register_super_mux("sclk", sclk_parents, + ARRAY_SIZE(sclk_parents), CLK_SET_RATE_PARENT, + clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "sclk", NULL); + clks[sclk] = clk; + + /* HCLK */ + clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, + clk_base + CLK_SYSTEM_RATE, 4, 2, 0, NULL); + clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT, + clk_base + CLK_SYSTEM_RATE, 7, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "hclk", NULL); + clks[hclk] = clk; + + /* PCLK */ + clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, + clk_base + CLK_SYSTEM_RATE, 0, 2, 0, NULL); + clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT, + clk_base + CLK_SYSTEM_RATE, 3, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "pclk", NULL); + clks[pclk] = clk; + + /* twd */ + clk = clk_register_fixed_factor(NULL, "twd", "cclk", 0, 1, 4); + clk_register_clkdev(clk, "twd", NULL); + clks[twd] = clk; +} + +static const char *audio_parents[] = {"spdif_in", "i2s1", "i2s2", "unused", + "pll_a_out0", "unused", "unused", + "unused"}; + +static void __init tegra20_audio_clk_init(void) +{ + struct clk *clk; + + /* audio */ + clk = clk_register_mux(NULL, "audio_mux", audio_parents, + ARRAY_SIZE(audio_parents), 0, + clk_base + AUDIO_SYNC_CLK, 0, 3, 0, NULL); + clk = clk_register_gate(NULL, "audio", "audio_mux", 0, + clk_base + AUDIO_SYNC_CLK, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio", NULL); + clks[audio] = clk; + + /* audio_2x */ + clk = clk_register_fixed_factor(NULL, "audio_doubler", "audio", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_periph_gate("audio_2x", "audio_doubler", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 89, &periph_u_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio_2x", NULL); + clks[audio_2x] = clk; + +} + +static const char *i2s1_parents[] = {"pll_a_out0", "audio_2x", "pll_p", + "clk_m"}; +static const char *i2s2_parents[] = {"pll_a_out0", "audio_2x", "pll_p", + "clk_m"}; +static const char *spdif_out_parents[] = {"pll_a_out0", "audio_2x", "pll_p", + "clk_m"}; +static const char *spdif_in_parents[] = {"pll_p", "pll_c", "pll_m"}; +static const char *pwm_parents[] = {"pll_p", "pll_c", "audio", "clk_m", + "clk_32k"}; +static const char *mux_pllpcm_clkm[] = {"pll_p", "pll_c", "pll_m", "clk_m"}; +static const char *mux_pllmcpa[] = {"pll_m", "pll_c", "pll_c", "pll_a"}; +static const char *mux_pllpdc_clkm[] = {"pll_p", "pll_d_out0", "pll_c", + "clk_m"}; +static const char *mux_pllmcp_clkm[] = {"pll_m", "pll_c", "pll_p", "clk_m"}; + +static struct tegra_periph_init_data tegra_periph_clk_list[] = { + TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra20-i2s.0", i2s1_parents, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1), + TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra20-i2s.1", i2s2_parents, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2), + TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra20-spdif", spdif_out_parents, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out), + TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra20-spdif", spdif_in_parents, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in), + TEGRA_INIT_DATA_MUX("sbc1", NULL, "spi_tegra.0", mux_pllpcm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1), + TEGRA_INIT_DATA_MUX("sbc2", NULL, "spi_tegra.1", mux_pllpcm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2), + TEGRA_INIT_DATA_MUX("sbc3", NULL, "spi_tegra.2", mux_pllpcm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3), + TEGRA_INIT_DATA_MUX("sbc4", NULL, "spi_tegra.3", mux_pllpcm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4), + TEGRA_INIT_DATA_MUX("spi", NULL, "spi", mux_pllpcm_clkm, CLK_SOURCE_SPI, 43, &periph_h_regs, TEGRA_PERIPH_ON_APB, spi), + TEGRA_INIT_DATA_MUX("xio", NULL, "xio", mux_pllpcm_clkm, CLK_SOURCE_XIO, 45, &periph_h_regs, 0, xio), + TEGRA_INIT_DATA_MUX("twc", NULL, "twc", mux_pllpcm_clkm, CLK_SOURCE_TWC, 16, &periph_l_regs, TEGRA_PERIPH_ON_APB, twc), + TEGRA_INIT_DATA_MUX("ide", NULL, "ide", mux_pllpcm_clkm, CLK_SOURCE_XIO, 25, &periph_l_regs, 0, ide), + TEGRA_INIT_DATA_MUX("ndflash", NULL, "tegra_nand", mux_pllpcm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_l_regs, 0, ndflash), + TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllpcm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir), + TEGRA_INIT_DATA_MUX("csite", NULL, "csite", mux_pllpcm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, 0, csite), + TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllpcm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, 0, la), + TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllpcm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr), + TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllpcm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi), + TEGRA_INIT_DATA_MUX("vde", NULL, "vde", mux_pllpcm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde), + TEGRA_INIT_DATA_MUX("vi", "vi", "tegra_camera", mux_pllmcpa, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi), + TEGRA_INIT_DATA_MUX("epp", NULL, "epp", mux_pllmcpa, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp), + TEGRA_INIT_DATA_MUX("mpe", NULL, "mpe", mux_pllmcpa, CLK_SOURCE_MPE, 60, &periph_h_regs, 0, mpe), + TEGRA_INIT_DATA_MUX("host1x", NULL, "host1x", mux_pllmcpa, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x), + TEGRA_INIT_DATA_MUX("3d", NULL, "3d", mux_pllmcpa, CLK_SOURCE_3D, 24, &periph_l_regs, TEGRA_PERIPH_MANUAL_RESET, gr3d), + TEGRA_INIT_DATA_MUX("2d", NULL, "2d", mux_pllmcpa, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr2d), + TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllpcm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor), + TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllpcm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1), + TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllpcm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2), + TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllpcm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3), + TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllpcm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4), + TEGRA_INIT_DATA_MUX("cve", NULL, "cve", mux_pllpdc_clkm, CLK_SOURCE_CVE, 49, &periph_h_regs, 0, cve), + TEGRA_INIT_DATA_MUX("tvo", NULL, "tvo", mux_pllpdc_clkm, CLK_SOURCE_TVO, 49, &periph_h_regs, 0, tvo), + TEGRA_INIT_DATA_MUX("tvdac", NULL, "tvdac", mux_pllpdc_clkm, CLK_SOURCE_TVDAC, 53, &periph_h_regs, 0, tvdac), + TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllmcpa, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor), + TEGRA_INIT_DATA_DIV16("i2c1", "div-clk", "tegra-i2c.0", mux_pllpcm_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2c1), + TEGRA_INIT_DATA_DIV16("i2c2", "div-clk", "tegra-i2c.1", mux_pllpcm_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, TEGRA_PERIPH_ON_APB, i2c2), + TEGRA_INIT_DATA_DIV16("i2c3", "div-clk", "tegra-i2c.2", mux_pllpcm_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2c3), + TEGRA_INIT_DATA_DIV16("dvc", "div-clk", "tegra-i2c.3", mux_pllpcm_clkm, CLK_SOURCE_DVC, 47, &periph_h_regs, TEGRA_PERIPH_ON_APB, dvc), + TEGRA_INIT_DATA_MUX("hdmi", NULL, "hdmi", mux_pllpdc_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi), + TEGRA_INIT_DATA("pwm", NULL, "tegra-pwm", pwm_parents, CLK_SOURCE_PWM, 28, 3, 0, 0, 8, 1, 0, &periph_l_regs, 17, periph_clk_enb_refcnt, TEGRA_PERIPH_ON_APB, pwm), +}; + +static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = { + TEGRA_INIT_DATA_NODIV("uarta", NULL, "tegra_uart.0", mux_pllpcm_clkm, CLK_SOURCE_UARTA, 30, 2, 6, &periph_l_regs, TEGRA_PERIPH_ON_APB, uarta), + TEGRA_INIT_DATA_NODIV("uartb", NULL, "tegra_uart.1", mux_pllpcm_clkm, CLK_SOURCE_UARTB, 30, 2, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, uartb), + TEGRA_INIT_DATA_NODIV("uartc", NULL, "tegra_uart.2", mux_pllpcm_clkm, CLK_SOURCE_UARTC, 30, 2, 55, &periph_h_regs, TEGRA_PERIPH_ON_APB, uartc), + TEGRA_INIT_DATA_NODIV("uartd", NULL, "tegra_uart.3", mux_pllpcm_clkm, CLK_SOURCE_UARTD, 30, 2, 65, &periph_u_regs, TEGRA_PERIPH_ON_APB, uartd), + TEGRA_INIT_DATA_NODIV("uarte", NULL, "tegra_uart.4", mux_pllpcm_clkm, CLK_SOURCE_UARTE, 30, 2, 66, &periph_u_regs, TEGRA_PERIPH_ON_APB, uarte), + TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllpdc_clkm, CLK_SOURCE_DISP1, 30, 2, 27, &periph_l_regs, 0, disp1), + TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26, &periph_l_regs, 0, disp2), +}; + +static void __init tegra20_periph_clk_init(void) +{ + struct tegra_periph_init_data *data; + struct clk *clk; + int i; + + /* apbdma */ + clk = tegra_clk_register_periph_gate("apbdma", "pclk", 0, clk_base, + 0, 34, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra-apbdma"); + clks[apbdma] = clk; + + /* rtc */ + clk = tegra_clk_register_periph_gate("rtc", "clk_32k", + TEGRA_PERIPH_NO_RESET, + clk_base, 0, 4, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "rtc-tegra"); + clks[rtc] = clk; + + /* timer */ + clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base, + 0, 5, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "timer"); + clks[timer] = clk; + + /* kbc */ + clk = tegra_clk_register_periph_gate("kbc", "clk_32k", + TEGRA_PERIPH_NO_RESET | TEGRA_PERIPH_ON_APB, + clk_base, 0, 36, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra-kbc"); + clks[kbc] = clk; + + /* csus */ + clk = tegra_clk_register_periph_gate("csus", "clk_m", + TEGRA_PERIPH_NO_RESET, + clk_base, 0, 92, &periph_u_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "csus", "tengra_camera"); + clks[csus] = clk; + + /* vcp */ + clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0, + clk_base, 0, 29, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "vcp", "tegra-avp"); + clks[vcp] = clk; + + /* bsea */ + clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0, + clk_base, 0, 62, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "bsea", "tegra-avp"); + clks[bsea] = clk; + + /* bsev */ + clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0, + clk_base, 0, 63, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "bsev", "tegra-aes"); + clks[bsev] = clk; + + /* emc */ + clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, + ARRAY_SIZE(mux_pllmcp_clkm), 0, + clk_base + CLK_SOURCE_EMC, + 30, 2, 0, NULL); + clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, + 57, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "emc", NULL); + clks[emc] = clk; + + /* usbd */ + clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base, 0, + 22, &periph_l_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "fsl-tegra-udc"); + clks[usbd] = clk; + + /* usb2 */ + clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base, 0, + 58, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra-ehci.1"); + clks[usb2] = clk; + + /* usb3 */ + clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base, 0, + 59, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra-ehci.2"); + clks[usb3] = clk; + + /* dsi */ + clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0, + 48, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "dsi"); + clks[dsi] = clk; + + /* csi */ + clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base, + 0, 52, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "csi", "tegra_camera"); + clks[csi] = clk; + + /* isp */ + clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0, 23, + &periph_l_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "isp", "tegra_camera"); + clks[isp] = clk; + + /* pex */ + clk = tegra_clk_register_periph_gate("pex", "clk_m", 0, clk_base, 0, 70, + &periph_u_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "pex", NULL); + clks[pex] = clk; + + /* afi */ + clk = tegra_clk_register_periph_gate("afi", "clk_m", 0, clk_base, 0, 72, + &periph_u_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "afi", NULL); + clks[afi] = clk; + + /* pcie_xclk */ + clk = tegra_clk_register_periph_gate("pcie_xclk", "clk_m", 0, clk_base, + 0, 74, &periph_u_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "pcie_xclk", NULL); + clks[pcie_xclk] = clk; + + /* cdev1 */ + clk = clk_register_fixed_rate(NULL, "cdev1_fixed", NULL, CLK_IS_ROOT, + 26000000); + clk = tegra_clk_register_periph_gate("cdev1", "cdev1_fixed", 0, + clk_base, 0, 94, &periph_u_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "cdev1", NULL); + clks[cdev1] = clk; + + /* cdev2 */ + clk = clk_register_fixed_rate(NULL, "cdev2_fixed", NULL, CLK_IS_ROOT, + 26000000); + clk = tegra_clk_register_periph_gate("cdev2", "cdev2_fixed", 0, + clk_base, 0, 93, &periph_u_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "cdev2", NULL); + clks[cdev2] = clk; + + for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { + data = &tegra_periph_clk_list[i]; + clk = tegra_clk_register_periph(data->name, data->parent_names, + data->num_parents, &data->periph, + clk_base, data->offset); + clk_register_clkdev(clk, data->con_id, data->dev_id); + clks[data->clk_id] = clk; + } + + for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) { + data = &tegra_periph_nodiv_clk_list[i]; + clk = tegra_clk_register_periph_nodiv(data->name, + data->parent_names, + data->num_parents, &data->periph, + clk_base, data->offset); + clk_register_clkdev(clk, data->con_id, data->dev_id); + clks[data->clk_id] = clk; + } +} + + +static void __init tegra20_fixed_clk_init(void) +{ + struct clk *clk; + + /* clk_32k */ + clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT, + 32768); + clk_register_clkdev(clk, "clk_32k", NULL); + clks[clk_32k] = clk; +} + +static void __init tegra20_pmc_clk_init(void) +{ + struct clk *clk; + + /* blink */ + 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); + clk = clk_register_gate(NULL, "blink", "blink_override", 0, + pmc_base + PMC_CTRL, + PMC_CTRL_BLINK_ENB, 0, NULL); + clk_register_clkdev(clk, "blink", NULL); + clks[blink] = clk; +} + +static void __init tegra20_osc_clk_init(void) +{ + struct clk *clk; + unsigned long input_freq; + unsigned int pll_ref_div; + + input_freq = tegra20_clk_measure_input_freq(); + + /* clk_m */ + clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT | + CLK_IGNORE_UNUSED, input_freq); + clk_register_clkdev(clk, "clk_m", NULL); + clks[clk_m] = clk; + + /* pll_ref */ + pll_ref_div = tegra20_get_pll_ref_div(); + clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m", + CLK_SET_RATE_PARENT, 1, pll_ref_div); + clk_register_clkdev(clk, "pll_ref", NULL); + clks[pll_ref] = clk; +} + +/* Tegra20 CPU clock and reset control functions */ +static void tegra20_wait_cpu_in_reset(u32 cpu) +{ + unsigned int reg; + + do { + reg = readl(clk_base + + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + cpu_relax(); + } while (!(reg & (1 << cpu))); /* check CPU been reset or not */ + + return; +} + +static void tegra20_put_cpu_in_reset(u32 cpu) +{ + writel(CPU_RESET(cpu), + clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + dmb(); +} + +static void tegra20_cpu_out_of_reset(u32 cpu) +{ + writel(CPU_RESET(cpu), + clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); + wmb(); +} + +static void tegra20_enable_cpu_clock(u32 cpu) +{ + unsigned int reg; + + reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + writel(reg & ~CPU_CLOCK(cpu), + clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + barrier(); + reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); +} + +static void tegra20_disable_cpu_clock(u32 cpu) +{ + unsigned int reg; + + reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + writel(reg | CPU_CLOCK(cpu), + clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); +} + +static struct tegra_cpu_car_ops tegra20_cpu_car_ops = { + .wait_for_reset = tegra20_wait_cpu_in_reset, + .put_in_reset = tegra20_put_cpu_in_reset, + .out_of_reset = tegra20_cpu_out_of_reset, + .enable_clock = tegra20_enable_cpu_clock, + .disable_clock = tegra20_disable_cpu_clock, +}; + +static __initdata struct tegra_clk_init_table init_table[] = { + {pll_p, clk_max, 216000000, 1}, + {pll_p_out1, clk_max, 28800000, 1}, + {pll_p_out2, clk_max, 48000000, 1}, + {pll_p_out3, clk_max, 72000000, 1}, + {pll_p_out4, clk_max, 24000000, 1}, + {pll_c, clk_max, 600000000, 1}, + {pll_c_out1, clk_max, 120000000, 1}, + {sclk, pll_c_out1, 0, 1}, + {hclk, clk_max, 0, 1}, + {pclk, clk_max, 60000000, 1}, + {csite, clk_max, 0, 1}, + {emc, clk_max, 0, 1}, + {cclk, clk_max, 0, 1}, + {uarta, pll_p, 0, 1}, + {uartd, pll_p, 0, 1}, + {usbd, clk_max, 12000000, 0}, + {usb2, clk_max, 12000000, 0}, + {usb3, clk_max, 12000000, 0}, + {pll_a, clk_max, 56448000, 1}, + {pll_a_out0, clk_max, 11289600, 1}, + {cdev1, clk_max, 0, 1}, + {blink, clk_max, 32768, 1}, + {i2s1, pll_a_out0, 11289600, 0}, + {i2s2, pll_a_out0, 11289600, 0}, + {sdmmc1, pll_p, 48000000, 0}, + {sdmmc3, pll_p, 48000000, 0}, + {sdmmc4, pll_p, 48000000, 0}, + {spi, pll_p, 20000000, 0}, + {sbc1, pll_p, 100000000, 0}, + {sbc2, pll_p, 100000000, 0}, + {sbc3, pll_p, 100000000, 0}, + {sbc4, pll_p, 100000000, 0}, + {host1x, pll_c, 150000000, 0}, + {disp1, pll_p, 600000000, 0}, + {disp2, pll_p, 600000000, 0}, + {clk_max, clk_max, 0, 0}, /* This MUST be the last entry */ +}; + +/* + * Some clocks may be used by different drivers depending on the board + * configuration. List those here to register them twice in the clock lookup + * table under two names. + */ +static struct tegra_clk_duplicate tegra_clk_duplicates[] = { + TEGRA_CLK_DUPLICATE(uarta, "serial8250.0", NULL), + TEGRA_CLK_DUPLICATE(uartb, "serial8250.1", NULL), + TEGRA_CLK_DUPLICATE(uartc, "serial8250.2", NULL), + TEGRA_CLK_DUPLICATE(uartd, "serial8250.3", NULL), + TEGRA_CLK_DUPLICATE(uarte, "serial8250.4", NULL), + TEGRA_CLK_DUPLICATE(usbd, "utmip-pad", NULL), + TEGRA_CLK_DUPLICATE(usbd, "tegra-ehci.0", NULL), + TEGRA_CLK_DUPLICATE(usbd, "tegra-otg", NULL), + TEGRA_CLK_DUPLICATE(pll_p, "tegradc.0", "parent"), + TEGRA_CLK_DUPLICATE(pll_p, "tegradc.1", "parent"), + TEGRA_CLK_DUPLICATE(pll_d_out0, "hdmi", "parent"), + TEGRA_CLK_DUPLICATE(gr2d, "tegra_grhost", "gr2d"), + TEGRA_CLK_DUPLICATE(gr3d, "tegra_grhost", "gr3d"), + TEGRA_CLK_DUPLICATE(epp, "tegra_grhost", "epp"), + TEGRA_CLK_DUPLICATE(mpe, "tegra_grhost", "mpe"), + TEGRA_CLK_DUPLICATE(vde, "tegra-aes", "vde"), + TEGRA_CLK_DUPLICATE(cclk, NULL, "cpu"), + TEGRA_CLK_DUPLICATE(twd, "smp_twd", NULL), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.0", "fast-clk"), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.1", "fast-clk"), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.2", "fast-clk"), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.3", "fast-clk"), + TEGRA_CLK_DUPLICATE(clk_max, NULL, NULL), /* Must be the last entry */ +}; + +static const struct of_device_id pmc_match[] __initconst = { + { .compatible = "nvidia,tegra20-pmc" }, + {}, +}; + +void __init tegra20_clock_init(struct device_node *np) +{ + int i; + struct device_node *node; + + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("Can't map CAR registers\n"); + BUG(); + } + + node = of_find_matching_node(NULL, pmc_match); + if (!node) { + pr_err("Failed to find pmc node\n"); + BUG(); + } + + pmc_base = of_iomap(node, 0); + if (!pmc_base) { + pr_err("Can't map pmc registers\n"); + BUG(); + } + + tegra20_osc_clk_init(); + tegra20_pmc_clk_init(); + tegra20_fixed_clk_init(); + tegra20_pll_init(); + tegra20_super_clk_init(); + tegra20_periph_clk_init(); + tegra20_audio_clk_init(); + + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + if (IS_ERR(clks[i])) { + pr_err("Tegra20 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + BUG(); + } + if (!clks[i]) + clks[i] = ERR_PTR(-EINVAL); + } + + tegra_init_dup_clks(tegra_clk_duplicates, clks, clk_max); + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + tegra_init_from_table(init_table, clks, clk_max); + + tegra_cpu_car_ops = &tegra20_cpu_car_ops; +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 5fcfa51d6b17..82835f24533c 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -487,4 +487,10 @@ 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 */ + #endif /* TEGRA_CLK_H */ -- cgit v1.2.3 From b08e8c0ecc42afa3a2e1019851af741980dd5a6b Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Fri, 11 Jan 2013 13:16:25 +0530 Subject: clk: tegra: add clock support for Tegra30 Add Tegra30 clock support based on common clock framework. Signed-off-by: Prashant Gaikwad [swarren: ensure all OF lookups return valid cookies i.e. an explicit error pointer or valid pointer not NULL, adapt to renames in earlier patches, fixed some checkpatch issues.] Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/Makefile | 1 + drivers/clk/tegra/clk-tegra30.c | 2057 +++++++++++++++++++++++++++++++++++++++ drivers/clk/tegra/clk.h | 6 + 3 files changed, 2064 insertions(+) create mode 100644 drivers/clk/tegra/clk-tegra30.c (limited to 'drivers') diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 00484fd4e515..2b41b0f4f731 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -8,3 +8,4 @@ obj-y += clk-pll-out.o obj-y += clk-super.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c new file mode 100644 index 000000000000..1c2765db1199 --- /dev/null +++ b/drivers/clk/tegra/clk-tegra30.c @@ -0,0 +1,2057 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" + +#define RST_DEVICES_L 0x004 +#define RST_DEVICES_H 0x008 +#define RST_DEVICES_U 0x00c +#define RST_DEVICES_V 0x358 +#define RST_DEVICES_W 0x35c +#define RST_DEVICES_SET_L 0x300 +#define RST_DEVICES_CLR_L 0x304 +#define RST_DEVICES_SET_H 0x308 +#define RST_DEVICES_CLR_H 0x30c +#define RST_DEVICES_SET_U 0x310 +#define RST_DEVICES_CLR_U 0x314 +#define RST_DEVICES_SET_V 0x430 +#define RST_DEVICES_CLR_V 0x434 +#define RST_DEVICES_SET_W 0x438 +#define RST_DEVICES_CLR_W 0x43c +#define RST_DEVICES_NUM 5 + +#define CLK_OUT_ENB_L 0x010 +#define CLK_OUT_ENB_H 0x014 +#define CLK_OUT_ENB_U 0x018 +#define CLK_OUT_ENB_V 0x360 +#define CLK_OUT_ENB_W 0x364 +#define CLK_OUT_ENB_SET_L 0x320 +#define CLK_OUT_ENB_CLR_L 0x324 +#define CLK_OUT_ENB_SET_H 0x328 +#define CLK_OUT_ENB_CLR_H 0x32c +#define CLK_OUT_ENB_SET_U 0x330 +#define CLK_OUT_ENB_CLR_U 0x334 +#define CLK_OUT_ENB_SET_V 0x440 +#define CLK_OUT_ENB_CLR_V 0x444 +#define CLK_OUT_ENB_SET_W 0x448 +#define CLK_OUT_ENB_CLR_W 0x44c +#define CLK_OUT_ENB_NUM 5 + +#define OSC_CTRL 0x50 +#define OSC_CTRL_OSC_FREQ_MASK (0xF<<28) +#define OSC_CTRL_OSC_FREQ_13MHZ (0X0<<28) +#define OSC_CTRL_OSC_FREQ_19_2MHZ (0X4<<28) +#define OSC_CTRL_OSC_FREQ_12MHZ (0X8<<28) +#define OSC_CTRL_OSC_FREQ_26MHZ (0XC<<28) +#define OSC_CTRL_OSC_FREQ_16_8MHZ (0X1<<28) +#define OSC_CTRL_OSC_FREQ_38_4MHZ (0X5<<28) +#define OSC_CTRL_OSC_FREQ_48MHZ (0X9<<28) +#define OSC_CTRL_MASK (0x3f2 | OSC_CTRL_OSC_FREQ_MASK) + +#define OSC_CTRL_PLL_REF_DIV_MASK (3<<26) +#define OSC_CTRL_PLL_REF_DIV_1 (0<<26) +#define OSC_CTRL_PLL_REF_DIV_2 (1<<26) +#define OSC_CTRL_PLL_REF_DIV_4 (2<<26) + +#define OSC_FREQ_DET 0x58 +#define OSC_FREQ_DET_TRIG BIT(31) + +#define OSC_FREQ_DET_STATUS 0x5c +#define OSC_FREQ_DET_BUSY BIT(31) +#define OSC_FREQ_DET_CNT_MASK 0xffff + +#define CCLKG_BURST_POLICY 0x368 +#define SUPER_CCLKG_DIVIDER 0x36c +#define CCLKLP_BURST_POLICY 0x370 +#define SUPER_CCLKLP_DIVIDER 0x374 +#define SCLK_BURST_POLICY 0x028 +#define SUPER_SCLK_DIVIDER 0x02c + +#define SYSTEM_CLK_RATE 0x030 + +#define PLLC_BASE 0x80 +#define PLLC_MISC 0x8c +#define PLLM_BASE 0x90 +#define PLLM_MISC 0x9c +#define PLLP_BASE 0xa0 +#define PLLP_MISC 0xac +#define PLLX_BASE 0xe0 +#define PLLX_MISC 0xe4 +#define PLLD_BASE 0xd0 +#define PLLD_MISC 0xdc +#define PLLD2_BASE 0x4b8 +#define PLLD2_MISC 0x4bc +#define PLLE_BASE 0xe8 +#define PLLE_MISC 0xec +#define PLLA_BASE 0xb0 +#define PLLA_MISC 0xbc +#define PLLU_BASE 0xc0 +#define PLLU_MISC 0xcc + +#define PLL_MISC_LOCK_ENABLE 18 +#define PLLDU_MISC_LOCK_ENABLE 22 +#define PLLE_MISC_LOCK_ENABLE 9 + +#define PLL_BASE_LOCK 27 +#define PLLE_MISC_LOCK 11 + +#define PLLE_AUX 0x48c +#define PLLC_OUT 0x84 +#define PLLM_OUT 0x94 +#define PLLP_OUTA 0xa4 +#define PLLP_OUTB 0xa8 +#define PLLA_OUT 0xb4 + +#define AUDIO_SYNC_CLK_I2S0 0x4a0 +#define AUDIO_SYNC_CLK_I2S1 0x4a4 +#define AUDIO_SYNC_CLK_I2S2 0x4a8 +#define AUDIO_SYNC_CLK_I2S3 0x4ac +#define AUDIO_SYNC_CLK_I2S4 0x4b0 +#define AUDIO_SYNC_CLK_SPDIF 0x4b4 + +#define PMC_CLK_OUT_CNTRL 0x1a8 + +#define CLK_SOURCE_I2S0 0x1d8 +#define CLK_SOURCE_I2S1 0x100 +#define CLK_SOURCE_I2S2 0x104 +#define CLK_SOURCE_I2S3 0x3bc +#define CLK_SOURCE_I2S4 0x3c0 +#define CLK_SOURCE_SPDIF_OUT 0x108 +#define CLK_SOURCE_SPDIF_IN 0x10c +#define CLK_SOURCE_PWM 0x110 +#define CLK_SOURCE_D_AUDIO 0x3d0 +#define CLK_SOURCE_DAM0 0x3d8 +#define CLK_SOURCE_DAM1 0x3dc +#define CLK_SOURCE_DAM2 0x3e0 +#define CLK_SOURCE_HDA 0x428 +#define CLK_SOURCE_HDA2CODEC_2X 0x3e4 +#define CLK_SOURCE_SBC1 0x134 +#define CLK_SOURCE_SBC2 0x118 +#define CLK_SOURCE_SBC3 0x11c +#define CLK_SOURCE_SBC4 0x1b4 +#define CLK_SOURCE_SBC5 0x3c8 +#define CLK_SOURCE_SBC6 0x3cc +#define CLK_SOURCE_SATA_OOB 0x420 +#define CLK_SOURCE_SATA 0x424 +#define CLK_SOURCE_NDFLASH 0x160 +#define CLK_SOURCE_NDSPEED 0x3f8 +#define CLK_SOURCE_VFIR 0x168 +#define CLK_SOURCE_SDMMC1 0x150 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC3 0x1bc +#define CLK_SOURCE_SDMMC4 0x164 +#define CLK_SOURCE_VDE 0x1c8 +#define CLK_SOURCE_CSITE 0x1d4 +#define CLK_SOURCE_LA 0x1f8 +#define CLK_SOURCE_OWR 0x1cc +#define CLK_SOURCE_NOR 0x1d0 +#define CLK_SOURCE_MIPI 0x174 +#define CLK_SOURCE_I2C1 0x124 +#define CLK_SOURCE_I2C2 0x198 +#define CLK_SOURCE_I2C3 0x1b8 +#define CLK_SOURCE_I2C4 0x3c4 +#define CLK_SOURCE_I2C5 0x128 +#define CLK_SOURCE_UARTA 0x178 +#define CLK_SOURCE_UARTB 0x17c +#define CLK_SOURCE_UARTC 0x1a0 +#define CLK_SOURCE_UARTD 0x1c0 +#define CLK_SOURCE_UARTE 0x1c4 +#define CLK_SOURCE_VI 0x148 +#define CLK_SOURCE_VI_SENSOR 0x1a8 +#define CLK_SOURCE_3D 0x158 +#define CLK_SOURCE_3D2 0x3b0 +#define CLK_SOURCE_2D 0x15c +#define CLK_SOURCE_EPP 0x16c +#define CLK_SOURCE_MPE 0x170 +#define CLK_SOURCE_HOST1X 0x180 +#define CLK_SOURCE_CVE 0x140 +#define CLK_SOURCE_TVO 0x188 +#define CLK_SOURCE_DTV 0x1dc +#define CLK_SOURCE_HDMI 0x18c +#define CLK_SOURCE_TVDAC 0x194 +#define CLK_SOURCE_DISP1 0x138 +#define CLK_SOURCE_DISP2 0x13c +#define CLK_SOURCE_DSIB 0xd0 +#define CLK_SOURCE_TSENSOR 0x3b8 +#define CLK_SOURCE_ACTMON 0x3e8 +#define CLK_SOURCE_EXTERN1 0x3ec +#define CLK_SOURCE_EXTERN2 0x3f0 +#define CLK_SOURCE_EXTERN3 0x3f4 +#define CLK_SOURCE_I2CSLOW 0x3fc +#define CLK_SOURCE_SE 0x42c +#define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_EMC 0x19c + +#define AUDIO_SYNC_DOUBLER 0x49c + +#define PMC_CTRL 0 +#define PMC_CTRL_BLINK_ENB 7 + +#define PMC_DPD_PADS_ORIDE 0x1c +#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20 +#define PMC_BLINK_TIMER 0x40 + +#define UTMIP_PLL_CFG2 0x488 +#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6) +#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4) + +#define UTMIP_PLL_CFG1 0x484 +#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6) +#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14) +#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16) + +/* Tegra CPU clock and reset control regs */ +#define TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4c +#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET 0x340 +#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344 +#define TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR 0x34c +#define TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470 + +#define CPU_CLOCK(cpu) (0x1 << (8 + cpu)) +#define CPU_RESET(cpu) (0x1111ul << (cpu)) + +#define CLK_RESET_CCLK_BURST 0x20 +#define CLK_RESET_CCLK_DIVIDER 0x24 +#define CLK_RESET_PLLX_BASE 0xe0 +#define CLK_RESET_PLLX_MISC 0xe4 + +#define CLK_RESET_SOURCE_CSITE 0x1d4 + +#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28 +#define CLK_RESET_CCLK_RUN_POLICY_SHIFT 4 +#define CLK_RESET_CCLK_IDLE_POLICY_SHIFT 0 +#define CLK_RESET_CCLK_IDLE_POLICY 1 +#define CLK_RESET_CCLK_RUN_POLICY 2 +#define CLK_RESET_CCLK_BURST_POLICY_PLLX 8 + +#ifdef CONFIG_PM_SLEEP +static struct cpu_clk_suspend_context { + u32 pllx_misc; + u32 pllx_base; + + u32 cpu_burst; + u32 clk_csite_src; + u32 cclk_divider; +} tegra30_cpu_clk_sctx; +#endif + +static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; + +static void __iomem *clk_base; +static void __iomem *pmc_base; +static unsigned long input_freq; + +static DEFINE_SPINLOCK(clk_doubler_lock); +static DEFINE_SPINLOCK(clk_out_lock); +static DEFINE_SPINLOCK(pll_div_lock); +static DEFINE_SPINLOCK(cml_lock); +static DEFINE_SPINLOCK(pll_d_lock); + +#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + 30, 2, 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id) + +#define TEGRA_INIT_DATA_DIV16(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + 30, 2, 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP, \ + _regs, _clk_num, periph_clk_enb_refcnt, \ + _gate_flags, _clk_id) + +#define TEGRA_INIT_DATA_MUX8(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + 29, 3, 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id) + +#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id) + +#define TEGRA_INIT_DATA_UART(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + 30, 2, 0, 0, 16, 1, TEGRA_DIVIDER_UART, _regs, \ + _clk_num, periph_clk_enb_refcnt, 0, _clk_id) + +#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \ + _mux_shift, _mux_width, _clk_num, _regs, \ + _gate_flags, _clk_id) \ + TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parents, _offset, \ + _mux_shift, _mux_width, 0, 0, 0, 0, 0, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id) + +/* + * IDs assigned here must be in sync with DT bindings definition + * for Tegra30 clocks. + */ +enum tegra30_clk { + cpu, rtc = 4, timer, uarta, gpio = 8, sdmmc2, i2s1 = 11, i2c1, ndflash, + sdmmc1, sdmmc4, pwm = 17, i2s2, epp, gr2d = 21, usbd, isp, gr3d, + disp2 = 26, disp1, host1x, vcp, i2s0, cop_cache, mc, ahbdma, apbdma, + kbc = 36, statmon, pmc, kfuse = 40, sbc1, nor, sbc2 = 44, sbc3 = 46, + i2c5, dsia, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2, + usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, + pcie, owr, afi, csite, pciex, avpucq, la, dtv = 79, ndspeed, i2c_slow, + dsib, irama = 84, iramb, iramc, iramd, cram2, audio_2x = 90, csus = 92, + cdev1, cdev2, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4, + i2c4, sbc5, sbc6, d_audio, apbif, dam0, dam1, dam2, hda2codec_2x, + atomics, audio0_2x, audio1_2x, audio2_2x, audio3_2x, audio4_2x, + spdif_2x, actmon, extern1, extern2, extern3, sata_oob, sata, hda, se, + hda2hdmi, sata_cold, uartb = 160, vfir, spdif_out, spdif_in, vi, + vi_sensor, fuse, fuse_burn, cve, tvo, clk_32k, clk_m, clk_m_div2, + clk_m_div4, pll_ref, pll_c, pll_c_out1, pll_m, pll_m_out1, pll_p, + pll_p_out1, pll_p_out2, pll_p_out3, pll_p_out4, pll_a, pll_a_out0, + pll_d, pll_d_out0, pll_d2, pll_d2_out0, pll_u, pll_x, pll_x_out0, pll_e, + spdif_in_sync, i2s0_sync, i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync, + vimclk_sync, audio0, audio1, audio2, audio3, audio4, spdif, clk_out_1, + clk_out_2, clk_out_3, sclk, blink, cclk_g, cclk_lp, twd, cml0, cml1, + i2cslow, hclk, pclk, clk_out_1_mux = 300, clk_max +}; + +static struct clk *clks[clk_max]; +static struct clk_onecell_data clk_data; + +/* + * Structure defining the fields for USB UTMI clocks Parameters. + */ +struct utmi_clk_param { + /* Oscillator Frequency in KHz */ + u32 osc_frequency; + /* UTMIP PLL Enable Delay Count */ + u8 enable_delay_count; + /* UTMIP PLL Stable count */ + u8 stable_count; + /* UTMIP PLL Active delay count */ + u8 active_delay_count; + /* UTMIP PLL Xtal frequency count */ + u8 xtal_freq_count; +}; + +static const struct utmi_clk_param utmi_parameters[] = { +/* OSC_FREQUENCY, ENABLE_DLY, STABLE_CNT, ACTIVE_DLY, XTAL_FREQ_CNT */ + {13000000, 0x02, 0x33, 0x05, 0x7F}, + {19200000, 0x03, 0x4B, 0x06, 0xBB}, + {12000000, 0x02, 0x2F, 0x04, 0x76}, + {26000000, 0x04, 0x66, 0x09, 0xFE}, + {16800000, 0x03, 0x41, 0x0A, 0xA4}, +}; + +static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { + { 12000000, 1040000000, 520, 6, 1, 8}, + { 13000000, 1040000000, 480, 6, 1, 8}, + { 16800000, 1040000000, 495, 8, 1, 8}, /* actual: 1039.5 MHz */ + { 19200000, 1040000000, 325, 6, 1, 6}, + { 26000000, 1040000000, 520, 13, 1, 8}, + + { 12000000, 832000000, 416, 6, 1, 8}, + { 13000000, 832000000, 832, 13, 1, 8}, + { 16800000, 832000000, 396, 8, 1, 8}, /* actual: 831.6 MHz */ + { 19200000, 832000000, 260, 6, 1, 8}, + { 26000000, 832000000, 416, 13, 1, 8}, + + { 12000000, 624000000, 624, 12, 1, 8}, + { 13000000, 624000000, 624, 13, 1, 8}, + { 16800000, 600000000, 520, 14, 1, 8}, + { 19200000, 624000000, 520, 16, 1, 8}, + { 26000000, 624000000, 624, 26, 1, 8}, + + { 12000000, 600000000, 600, 12, 1, 8}, + { 13000000, 600000000, 600, 13, 1, 8}, + { 16800000, 600000000, 500, 14, 1, 8}, + { 19200000, 600000000, 375, 12, 1, 6}, + { 26000000, 600000000, 600, 26, 1, 8}, + + { 12000000, 520000000, 520, 12, 1, 8}, + { 13000000, 520000000, 520, 13, 1, 8}, + { 16800000, 520000000, 495, 16, 1, 8}, /* actual: 519.75 MHz */ + { 19200000, 520000000, 325, 12, 1, 6}, + { 26000000, 520000000, 520, 26, 1, 8}, + + { 12000000, 416000000, 416, 12, 1, 8}, + { 13000000, 416000000, 416, 13, 1, 8}, + { 16800000, 416000000, 396, 16, 1, 8}, /* actual: 415.8 MHz */ + { 19200000, 416000000, 260, 12, 1, 6}, + { 26000000, 416000000, 416, 26, 1, 8}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { + { 12000000, 666000000, 666, 12, 1, 8}, + { 13000000, 666000000, 666, 13, 1, 8}, + { 16800000, 666000000, 555, 14, 1, 8}, + { 19200000, 666000000, 555, 16, 1, 8}, + { 26000000, 666000000, 666, 26, 1, 8}, + { 12000000, 600000000, 600, 12, 1, 8}, + { 13000000, 600000000, 600, 13, 1, 8}, + { 16800000, 600000000, 500, 14, 1, 8}, + { 19200000, 600000000, 375, 12, 1, 6}, + { 26000000, 600000000, 600, 26, 1, 8}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { + { 12000000, 216000000, 432, 12, 2, 8}, + { 13000000, 216000000, 432, 13, 2, 8}, + { 16800000, 216000000, 360, 14, 2, 8}, + { 19200000, 216000000, 360, 16, 2, 8}, + { 26000000, 216000000, 432, 26, 2, 8}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { + { 9600000, 564480000, 294, 5, 1, 4}, + { 9600000, 552960000, 288, 5, 1, 4}, + { 9600000, 24000000, 5, 2, 1, 1}, + + { 28800000, 56448000, 49, 25, 1, 1}, + { 28800000, 73728000, 64, 25, 1, 1}, + { 28800000, 24000000, 5, 6, 1, 1}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { + { 12000000, 216000000, 216, 12, 1, 4}, + { 13000000, 216000000, 216, 13, 1, 4}, + { 16800000, 216000000, 180, 14, 1, 4}, + { 19200000, 216000000, 180, 16, 1, 4}, + { 26000000, 216000000, 216, 26, 1, 4}, + + { 12000000, 594000000, 594, 12, 1, 8}, + { 13000000, 594000000, 594, 13, 1, 8}, + { 16800000, 594000000, 495, 14, 1, 8}, + { 19200000, 594000000, 495, 16, 1, 8}, + { 26000000, 594000000, 594, 26, 1, 8}, + + { 12000000, 1000000000, 1000, 12, 1, 12}, + { 13000000, 1000000000, 1000, 13, 1, 12}, + { 19200000, 1000000000, 625, 12, 1, 8}, + { 26000000, 1000000000, 1000, 26, 1, 12}, + + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { + { 12000000, 480000000, 960, 12, 2, 12}, + { 13000000, 480000000, 960, 13, 2, 12}, + { 16800000, 480000000, 400, 7, 2, 5}, + { 19200000, 480000000, 200, 4, 2, 3}, + { 26000000, 480000000, 960, 26, 2, 12}, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { + /* 1.7 GHz */ + { 12000000, 1700000000, 850, 6, 1, 8}, + { 13000000, 1700000000, 915, 7, 1, 8}, /* actual: 1699.2 MHz */ + { 16800000, 1700000000, 708, 7, 1, 8}, /* actual: 1699.2 MHz */ + { 19200000, 1700000000, 885, 10, 1, 8}, /* actual: 1699.2 MHz */ + { 26000000, 1700000000, 850, 13, 1, 8}, + + /* 1.6 GHz */ + { 12000000, 1600000000, 800, 6, 1, 8}, + { 13000000, 1600000000, 738, 6, 1, 8}, /* actual: 1599.0 MHz */ + { 16800000, 1600000000, 857, 9, 1, 8}, /* actual: 1599.7 MHz */ + { 19200000, 1600000000, 500, 6, 1, 8}, + { 26000000, 1600000000, 800, 13, 1, 8}, + + /* 1.5 GHz */ + { 12000000, 1500000000, 750, 6, 1, 8}, + { 13000000, 1500000000, 923, 8, 1, 8}, /* actual: 1499.8 MHz */ + { 16800000, 1500000000, 625, 7, 1, 8}, + { 19200000, 1500000000, 625, 8, 1, 8}, + { 26000000, 1500000000, 750, 13, 1, 8}, + + /* 1.4 GHz */ + { 12000000, 1400000000, 700, 6, 1, 8}, + { 13000000, 1400000000, 969, 9, 1, 8}, /* actual: 1399.7 MHz */ + { 16800000, 1400000000, 1000, 12, 1, 8}, + { 19200000, 1400000000, 875, 12, 1, 8}, + { 26000000, 1400000000, 700, 13, 1, 8}, + + /* 1.3 GHz */ + { 12000000, 1300000000, 975, 9, 1, 8}, + { 13000000, 1300000000, 1000, 10, 1, 8}, + { 16800000, 1300000000, 928, 12, 1, 8}, /* actual: 1299.2 MHz */ + { 19200000, 1300000000, 812, 12, 1, 8}, /* actual: 1299.2 MHz */ + { 26000000, 1300000000, 650, 13, 1, 8}, + + /* 1.2 GHz */ + { 12000000, 1200000000, 1000, 10, 1, 8}, + { 13000000, 1200000000, 923, 10, 1, 8}, /* actual: 1199.9 MHz */ + { 16800000, 1200000000, 1000, 14, 1, 8}, + { 19200000, 1200000000, 1000, 16, 1, 8}, + { 26000000, 1200000000, 600, 13, 1, 8}, + + /* 1.1 GHz */ + { 12000000, 1100000000, 825, 9, 1, 8}, + { 13000000, 1100000000, 846, 10, 1, 8}, /* actual: 1099.8 MHz */ + { 16800000, 1100000000, 982, 15, 1, 8}, /* actual: 1099.8 MHz */ + { 19200000, 1100000000, 859, 15, 1, 8}, /* actual: 1099.5 MHz */ + { 26000000, 1100000000, 550, 13, 1, 8}, + + /* 1 GHz */ + { 12000000, 1000000000, 1000, 12, 1, 8}, + { 13000000, 1000000000, 1000, 13, 1, 8}, + { 16800000, 1000000000, 833, 14, 1, 8}, /* actual: 999.6 MHz */ + { 19200000, 1000000000, 625, 12, 1, 8}, + { 26000000, 1000000000, 1000, 26, 1, 8}, + + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { + /* PLLE special case: use cpcon field to store cml divider value */ + { 12000000, 100000000, 150, 1, 18, 11}, + { 216000000, 100000000, 200, 18, 24, 13}, + { 0, 0, 0, 0, 0, 0 }, +}; + +/* PLL parameters */ +static struct tegra_clk_pll_params pll_c_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1400000000, + .base_reg = PLLC_BASE, + .misc_reg = PLLC_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_m_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1200000000, + .base_reg = PLLM_BASE, + .misc_reg = PLLM_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_p_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1400000000, + .base_reg = PLLP_BASE, + .misc_reg = PLLP_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_a_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1400000000, + .base_reg = PLLA_BASE, + .misc_reg = PLLA_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_d_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 40000000, + .vco_max = 1000000000, + .base_reg = PLLD_BASE, + .misc_reg = PLLD_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_d2_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 40000000, + .vco_max = 1000000000, + .base_reg = PLLD2_BASE, + .misc_reg = PLLD2_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_u_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 48000000, + .vco_max = 960000000, + .base_reg = PLLU_BASE, + .misc_reg = PLLU_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_x_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 20000000, + .vco_max = 1700000000, + .base_reg = PLLX_BASE, + .misc_reg = PLLX_MISC, + .lock_bit_idx = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_e_params = { + .input_min = 12000000, + .input_max = 216000000, + .cf_min = 12000000, + .cf_max = 12000000, + .vco_min = 1200000000, + .vco_max = 2400000000U, + .base_reg = PLLE_BASE, + .misc_reg = PLLE_MISC, + .lock_bit_idx = PLLE_MISC_LOCK, + .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +/* Peripheral clock registers */ +static struct tegra_clk_periph_regs periph_l_regs = { + .enb_reg = CLK_OUT_ENB_L, + .enb_set_reg = CLK_OUT_ENB_SET_L, + .enb_clr_reg = CLK_OUT_ENB_CLR_L, + .rst_reg = RST_DEVICES_L, + .rst_set_reg = RST_DEVICES_SET_L, + .rst_clr_reg = RST_DEVICES_CLR_L, +}; + +static struct tegra_clk_periph_regs periph_h_regs = { + .enb_reg = CLK_OUT_ENB_H, + .enb_set_reg = CLK_OUT_ENB_SET_H, + .enb_clr_reg = CLK_OUT_ENB_CLR_H, + .rst_reg = RST_DEVICES_H, + .rst_set_reg = RST_DEVICES_SET_H, + .rst_clr_reg = RST_DEVICES_CLR_H, +}; + +static struct tegra_clk_periph_regs periph_u_regs = { + .enb_reg = CLK_OUT_ENB_U, + .enb_set_reg = CLK_OUT_ENB_SET_U, + .enb_clr_reg = CLK_OUT_ENB_CLR_U, + .rst_reg = RST_DEVICES_U, + .rst_set_reg = RST_DEVICES_SET_U, + .rst_clr_reg = RST_DEVICES_CLR_U, +}; + +static struct tegra_clk_periph_regs periph_v_regs = { + .enb_reg = CLK_OUT_ENB_V, + .enb_set_reg = CLK_OUT_ENB_SET_V, + .enb_clr_reg = CLK_OUT_ENB_CLR_V, + .rst_reg = RST_DEVICES_V, + .rst_set_reg = RST_DEVICES_SET_V, + .rst_clr_reg = RST_DEVICES_CLR_V, +}; + +static struct tegra_clk_periph_regs periph_w_regs = { + .enb_reg = CLK_OUT_ENB_W, + .enb_set_reg = CLK_OUT_ENB_SET_W, + .enb_clr_reg = CLK_OUT_ENB_CLR_W, + .rst_reg = RST_DEVICES_W, + .rst_set_reg = RST_DEVICES_SET_W, + .rst_clr_reg = RST_DEVICES_CLR_W, +}; + +static void tegra30_clk_measure_input_freq(void) +{ + u32 osc_ctrl = readl_relaxed(clk_base + OSC_CTRL); + u32 auto_clk_control = osc_ctrl & OSC_CTRL_OSC_FREQ_MASK; + u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK; + + switch (auto_clk_control) { + case OSC_CTRL_OSC_FREQ_12MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 12000000; + break; + case OSC_CTRL_OSC_FREQ_13MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 13000000; + break; + case OSC_CTRL_OSC_FREQ_19_2MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 19200000; + break; + case OSC_CTRL_OSC_FREQ_26MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 26000000; + break; + case OSC_CTRL_OSC_FREQ_16_8MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1); + input_freq = 16800000; + break; + case OSC_CTRL_OSC_FREQ_38_4MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_2); + input_freq = 38400000; + break; + case OSC_CTRL_OSC_FREQ_48MHZ: + BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_4); + input_freq = 48000000; + break; + default: + pr_err("Unexpected auto clock control value %d", + auto_clk_control); + BUG(); + return; + } +} + +static unsigned int tegra30_get_pll_ref_div(void) +{ + u32 pll_ref_div = readl_relaxed(clk_base + OSC_CTRL) & + OSC_CTRL_PLL_REF_DIV_MASK; + + switch (pll_ref_div) { + case OSC_CTRL_PLL_REF_DIV_1: + return 1; + case OSC_CTRL_PLL_REF_DIV_2: + return 2; + case OSC_CTRL_PLL_REF_DIV_4: + return 4; + default: + pr_err("Invalid pll ref divider %d", pll_ref_div); + BUG(); + } + return 0; +} + +static void tegra30_utmi_param_configure(void) +{ + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) { + if (input_freq == utmi_parameters[i].osc_frequency) + break; + } + + if (i >= ARRAY_SIZE(utmi_parameters)) { + pr_err("%s: Unexpected input rate %lu\n", __func__, input_freq); + return; + } + + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL stable and active counts */ + reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); + reg |= UTMIP_PLL_CFG2_STABLE_COUNT( + utmi_parameters[i].stable_count); + + reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); + + reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT( + utmi_parameters[i].active_delay_count); + + /* Remove power downs from UTMIP PLL control bits */ + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN; + + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL delay and oscillator frequency counts */ + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + + reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT( + utmi_parameters[i].enable_delay_count); + + reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); + reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT( + utmi_parameters[i].xtal_freq_count); + + /* Remove power downs from UTMIP PLL control bits */ + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN; + + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); +} + +static const char *pll_e_parents[] = {"pll_ref", "pll_p"}; + +static void __init tegra30_pll_init(void) +{ + struct clk *clk; + + /* PLLC */ + clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0, + 0, &pll_c_params, + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK, + pll_c_freq_table, NULL); + clk_register_clkdev(clk, "pll_c", NULL); + clks[pll_c] = clk; + + /* PLLC_OUT1 */ + clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c", + clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div", + clk_base + PLLC_OUT, 1, 0, CLK_SET_RATE_PARENT, + 0, NULL); + clk_register_clkdev(clk, "pll_c_out1", NULL); + clks[pll_c_out1] = clk; + + /* PLLP */ + clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, pmc_base, 0, + 408000000, &pll_p_params, + TEGRA_PLL_FIXED | TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_USE_LOCK, pll_p_freq_table, NULL); + clk_register_clkdev(clk, "pll_p", NULL); + clks[pll_p] = clk; + + /* PLLP_OUT1 */ + clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p", + clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div", + clk_base + PLLP_OUTA, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out1", NULL); + clks[pll_p_out1] = clk; + + /* PLLP_OUT2 */ + clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p", + clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div", + clk_base + PLLP_OUTA, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out2", NULL); + clks[pll_p_out2] = clk; + + /* PLLP_OUT3 */ + clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p", + clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div", + clk_base + PLLP_OUTB, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out3", NULL); + clks[pll_p_out3] = clk; + + /* PLLP_OUT4 */ + clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p", + clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div", + clk_base + PLLP_OUTB, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out4", NULL); + clks[pll_p_out4] = clk; + + /* PLLM */ + clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base, + CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0, + &pll_m_params, TEGRA_PLLM | TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_SET_DCCON | TEGRA_PLL_USE_LOCK, + pll_m_freq_table, NULL); + clk_register_clkdev(clk, "pll_m", NULL); + clks[pll_m] = clk; + + /* PLLM_OUT1 */ + clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m", + clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div", + clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_m_out1", NULL); + clks[pll_m_out1] = clk; + + /* PLLX */ + clk = tegra_clk_register_pll("pll_x", "pll_ref", clk_base, pmc_base, 0, + 0, &pll_x_params, TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_SET_DCCON | TEGRA_PLL_USE_LOCK, + pll_x_freq_table, NULL); + clk_register_clkdev(clk, "pll_x", NULL); + clks[pll_x] = clk; + + /* PLLX_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_x_out0", NULL); + clks[pll_x_out0] = clk; + + /* PLLU */ + 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, + pll_u_freq_table, + NULL); + clk_register_clkdev(clk, "pll_u", NULL); + clks[pll_u] = clk; + + tegra30_utmi_param_configure(); + + /* PLLD */ + clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc_base, 0, + 0, &pll_d_params, TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_SET_LFCON | TEGRA_PLL_USE_LOCK, + pll_d_freq_table, &pll_d_lock); + clk_register_clkdev(clk, "pll_d", NULL); + clks[pll_d] = clk; + + /* PLLD_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d_out0", NULL); + clks[pll_d_out0] = clk; + + /* PLLD2 */ + clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc_base, 0, + 0, &pll_d2_params, TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_SET_LFCON | TEGRA_PLL_USE_LOCK, + pll_d_freq_table, NULL); + clk_register_clkdev(clk, "pll_d2", NULL); + clks[pll_d2] = clk; + + /* PLLD2_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d2_out0", NULL); + clks[pll_d2_out0] = clk; + + /* PLLA */ + clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, pmc_base, + 0, 0, &pll_a_params, TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_USE_LOCK, pll_a_freq_table, NULL); + clk_register_clkdev(clk, "pll_a", NULL); + clks[pll_a] = clk; + + /* PLLA_OUT0 */ + clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a", + clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div", + clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_a_out0", NULL); + clks[pll_a_out0] = clk; + + /* PLLE */ + clk = clk_register_mux(NULL, "pll_e_mux", pll_e_parents, + ARRAY_SIZE(pll_e_parents), 0, + 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, + TEGRA_PLLE_CONFIGURE, pll_e_freq_table, NULL); + clk_register_clkdev(clk, "pll_e", NULL); + clks[pll_e] = clk; +} + +static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync", + "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",}; +static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern1", }; +static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern2", }; +static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern3", }; + +static void __init tegra30_audio_clk_init(void) +{ + struct clk *clk; + + /* spdif_in_sync */ + clk = tegra_clk_register_sync_source("spdif_in_sync", 24000000, + 24000000); + clk_register_clkdev(clk, "spdif_in_sync", NULL); + clks[spdif_in_sync] = clk; + + /* i2s0_sync */ + clk = tegra_clk_register_sync_source("i2s0_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s0_sync", NULL); + clks[i2s0_sync] = clk; + + /* i2s1_sync */ + clk = tegra_clk_register_sync_source("i2s1_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s1_sync", NULL); + clks[i2s1_sync] = clk; + + /* i2s2_sync */ + clk = tegra_clk_register_sync_source("i2s2_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s2_sync", NULL); + clks[i2s2_sync] = clk; + + /* i2s3_sync */ + clk = tegra_clk_register_sync_source("i2s3_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s3_sync", NULL); + clks[i2s3_sync] = clk; + + /* i2s4_sync */ + clk = tegra_clk_register_sync_source("i2s4_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s4_sync", NULL); + clks[i2s4_sync] = clk; + + /* vimclk_sync */ + clk = tegra_clk_register_sync_source("vimclk_sync", 24000000, 24000000); + clk_register_clkdev(clk, "vimclk_sync", NULL); + clks[vimclk_sync] = clk; + + /* audio0 */ + clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, NULL); + clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S0, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio0", NULL); + clks[audio0] = clk; + + /* audio1 */ + clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, NULL); + clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S1, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio1", NULL); + clks[audio1] = clk; + + /* audio2 */ + clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, NULL); + clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S2, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio2", NULL); + clks[audio2] = clk; + + /* audio3 */ + clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, NULL); + clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S3, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio3", NULL); + clks[audio3] = clk; + + /* audio4 */ + clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, NULL); + clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S4, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio4", NULL); + clks[audio4] = clk; + + /* spdif */ + clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, NULL); + clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0, + clk_base + AUDIO_SYNC_CLK_SPDIF, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "spdif", NULL); + clks[spdif] = clk; + + /* audio0_2x */ + clk = clk_register_fixed_factor(NULL, "audio0_doubler", "audio0", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio0_div", "audio0_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 24, 1, 0, + &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio0_2x", "audio0_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 113, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio0_2x", NULL); + clks[audio0_2x] = clk; + + /* audio1_2x */ + clk = clk_register_fixed_factor(NULL, "audio1_doubler", "audio1", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio1_div", "audio1_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 25, 1, 0, + &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio1_2x", "audio1_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 114, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio1_2x", NULL); + clks[audio1_2x] = clk; + + /* audio2_2x */ + clk = clk_register_fixed_factor(NULL, "audio2_doubler", "audio2", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio2_div", "audio2_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 26, 1, 0, + &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio2_2x", "audio2_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 115, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio2_2x", NULL); + clks[audio2_2x] = clk; + + /* audio3_2x */ + clk = clk_register_fixed_factor(NULL, "audio3_doubler", "audio3", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio3_div", "audio3_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 27, 1, 0, + &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio3_2x", "audio3_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 116, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio3_2x", NULL); + clks[audio3_2x] = clk; + + /* audio4_2x */ + clk = clk_register_fixed_factor(NULL, "audio4_doubler", "audio4", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio4_div", "audio4_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 28, 1, 0, + &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio4_2x", "audio4_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 117, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio4_2x", NULL); + clks[audio4_2x] = clk; + + /* spdif_2x */ + clk = clk_register_fixed_factor(NULL, "spdif_doubler", "spdif", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("spdif_div", "spdif_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 29, 1, 0, + &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("spdif_2x", "spdif_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 118, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "spdif_2x", NULL); + clks[spdif_2x] = clk; +} + +static void __init tegra30_pmc_clk_init(void) +{ + struct clk *clk; + + /* clk_out_1 */ + clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, + &clk_out_lock); + clks[clk_out_1_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_1", "clk_out_1_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 2, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern1", "clk_out_1"); + clks[clk_out_1] = clk; + + /* clk_out_2 */ + clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, + &clk_out_lock); + clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 10, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern2", "clk_out_2"); + clks[clk_out_2] = clk; + + /* clk_out_3 */ + clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, + &clk_out_lock); + clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 18, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern3", "clk_out_3"); + clks[clk_out_3] = clk; + + /* blink */ + 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); + clk = clk_register_gate(NULL, "blink", "blink_override", 0, + pmc_base + PMC_CTRL, + PMC_CTRL_BLINK_ENB, 0, NULL); + clk_register_clkdev(clk, "blink", NULL); + clks[blink] = clk; + +} + +const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p_cclkg", "pll_p_out4_cclkg", + "pll_p_out3_cclkg", "unused", "pll_x" }; +const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p_cclklp", "pll_p_out4_cclklp", + "pll_p_out3_cclklp", "unused", "pll_x", + "pll_x_out0" }; +const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", + "pll_p_out3", "pll_p_out2", "unused", + "clk_32k", "pll_m_out1" }; + +static void __init tegra30_super_clk_init(void) +{ + struct clk *clk; + + /* + * Clock input to cclk_g divided from pll_p using + * U71 divider of cclk_g. + */ + clk = tegra_clk_register_divider("pll_p_cclkg", "pll_p", + clk_base + SUPER_CCLKG_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_cclkg", NULL); + + /* + * Clock input to cclk_g divided from pll_p_out3 using + * U71 divider of cclk_g. + */ + clk = tegra_clk_register_divider("pll_p_out3_cclkg", "pll_p_out3", + clk_base + SUPER_CCLKG_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_out3_cclkg", NULL); + + /* + * Clock input to cclk_g divided from pll_p_out4 using + * U71 divider of cclk_g. + */ + clk = tegra_clk_register_divider("pll_p_out4_cclkg", "pll_p_out4", + clk_base + SUPER_CCLKG_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_out4_cclkg", NULL); + + /* CCLKG */ + clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents, + ARRAY_SIZE(cclk_g_parents), + CLK_SET_RATE_PARENT, + clk_base + CCLKG_BURST_POLICY, + 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "cclk_g", NULL); + clks[cclk_g] = clk; + + /* + * Clock input to cclk_lp divided from pll_p using + * U71 divider of cclk_lp. + */ + clk = tegra_clk_register_divider("pll_p_cclklp", "pll_p", + clk_base + SUPER_CCLKLP_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_cclklp", NULL); + + /* + * Clock input to cclk_lp divided from pll_p_out3 using + * U71 divider of cclk_lp. + */ + clk = tegra_clk_register_divider("pll_p_out3_cclklp", "pll_p_out3", + clk_base + SUPER_CCLKG_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_out3_cclklp", NULL); + + /* + * Clock input to cclk_lp divided from pll_p_out4 using + * U71 divider of cclk_lp. + */ + clk = tegra_clk_register_divider("pll_p_out4_cclklp", "pll_p_out4", + clk_base + SUPER_CCLKLP_DIVIDER, 0, + TEGRA_DIVIDER_INT, 16, 8, 1, NULL); + clk_register_clkdev(clk, "pll_p_out4_cclklp", NULL); + + /* CCLKLP */ + clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents, + ARRAY_SIZE(cclk_lp_parents), + CLK_SET_RATE_PARENT, + clk_base + CCLKLP_BURST_POLICY, + TEGRA_DIVIDER_2, 4, 8, 9, + NULL); + clk_register_clkdev(clk, "cclk_lp", NULL); + clks[cclk_lp] = clk; + + /* SCLK */ + clk = tegra_clk_register_super_mux("sclk", sclk_parents, + ARRAY_SIZE(sclk_parents), + CLK_SET_RATE_PARENT, + clk_base + SCLK_BURST_POLICY, + 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "sclk", NULL); + clks[sclk] = clk; + + /* HCLK */ + clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, + clk_base + SYSTEM_CLK_RATE, 4, 2, 0, NULL); + clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT, + clk_base + SYSTEM_CLK_RATE, 7, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "hclk", NULL); + clks[hclk] = clk; + + /* PCLK */ + clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, + clk_base + SYSTEM_CLK_RATE, 0, 2, 0, NULL); + clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT, + clk_base + SYSTEM_CLK_RATE, 3, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "pclk", NULL); + clks[pclk] = clk; + + /* twd */ + clk = clk_register_fixed_factor(NULL, "twd", "cclk_g", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "twd", NULL); + clks[twd] = clk; +} + +static const char *mux_pllacp_clkm[] = { "pll_a_out0", "unused", "pll_p", + "clk_m" }; +static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" }; +static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" }; +static const char *i2s0_parents[] = { "pll_a_out0", "audio0_2x", "pll_p", + "clk_m" }; +static const char *i2s1_parents[] = { "pll_a_out0", "audio1_2x", "pll_p", + "clk_m" }; +static const char *i2s2_parents[] = { "pll_a_out0", "audio2_2x", "pll_p", + "clk_m" }; +static const char *i2s3_parents[] = { "pll_a_out0", "audio3_2x", "pll_p", + "clk_m" }; +static const char *i2s4_parents[] = { "pll_a_out0", "audio4_2x", "pll_p", + "clk_m" }; +static const char *spdif_out_parents[] = { "pll_a_out0", "spdif_2x", "pll_p", + "clk_m" }; +static const char *spdif_in_parents[] = { "pll_p", "pll_c", "pll_m" }; +static const char *mux_pllpc_clk32k_clkm[] = { "pll_p", "pll_c", "clk_32k", + "clk_m" }; +static const char *mux_pllpc_clkm_clk32k[] = { "pll_p", "pll_c", "clk_m", + "clk_32k" }; +static const char *mux_pllmcpa[] = { "pll_m", "pll_c", "pll_p", "pll_a_out0" }; +static const char *mux_pllpdc_clkm[] = { "pll_p", "pll_d_out0", "pll_c", + "clk_m" }; +static const char *mux_pllp_clkm[] = { "pll_p", "unused", "unused", "clk_m" }; +static const char *mux_pllpmdacd2_clkm[] = { "pll_p", "pll_m", "pll_d_out0", + "pll_a_out0", "pll_c", + "pll_d2_out0", "clk_m" }; +static const char *mux_plla_clk32k_pllp_clkm_plle[] = { "pll_a_out0", + "clk_32k", "pll_p", + "clk_m", "pll_e" }; +static const char *mux_plld_out0_plld2_out0[] = { "pll_d_out0", + "pll_d2_out0" }; + +static struct tegra_periph_init_data tegra_periph_clk_list[] = { + TEGRA_INIT_DATA_MUX("i2s0", NULL, "tegra30-i2s.0", i2s0_parents, CLK_SOURCE_I2S0, 30, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s0), + TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra30-i2s.1", i2s1_parents, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1), + TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra30-i2s.2", i2s2_parents, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2), + TEGRA_INIT_DATA_MUX("i2s3", NULL, "tegra30-i2s.3", i2s3_parents, CLK_SOURCE_I2S3, 101, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s3), + TEGRA_INIT_DATA_MUX("i2s4", NULL, "tegra30-i2s.4", i2s4_parents, CLK_SOURCE_I2S4, 102, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s4), + TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra30-spdif", spdif_out_parents, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out), + TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra30-spdif", spdif_in_parents, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in), + TEGRA_INIT_DATA_MUX("d_audio", "d_audio", "tegra30-ahub", mux_pllacp_clkm, CLK_SOURCE_D_AUDIO, 106, &periph_v_regs, 0, d_audio), + TEGRA_INIT_DATA_MUX("dam0", NULL, "tegra30-dam.0", mux_pllacp_clkm, CLK_SOURCE_DAM0, 108, &periph_v_regs, 0, dam0), + TEGRA_INIT_DATA_MUX("dam1", NULL, "tegra30-dam.1", mux_pllacp_clkm, CLK_SOURCE_DAM1, 109, &periph_v_regs, 0, dam1), + TEGRA_INIT_DATA_MUX("dam2", NULL, "tegra30-dam.2", mux_pllacp_clkm, CLK_SOURCE_DAM2, 110, &periph_v_regs, 0, dam2), + TEGRA_INIT_DATA_MUX("hda", "hda", "tegra30-hda", mux_pllpcm_clkm, CLK_SOURCE_HDA, 125, &periph_v_regs, 0, hda), + TEGRA_INIT_DATA_MUX("hda2codec_2x", "hda2codec", "tegra30-hda", mux_pllpcm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, &periph_v_regs, 0, hda2codec_2x), + TEGRA_INIT_DATA_MUX("sbc1", NULL, "spi_tegra.0", mux_pllpcm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1), + TEGRA_INIT_DATA_MUX("sbc2", NULL, "spi_tegra.1", mux_pllpcm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2), + TEGRA_INIT_DATA_MUX("sbc3", NULL, "spi_tegra.2", mux_pllpcm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3), + TEGRA_INIT_DATA_MUX("sbc4", NULL, "spi_tegra.3", mux_pllpcm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4), + TEGRA_INIT_DATA_MUX("sbc5", NULL, "spi_tegra.4", mux_pllpcm_clkm, CLK_SOURCE_SBC5, 104, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc5), + TEGRA_INIT_DATA_MUX("sbc6", NULL, "spi_tegra.5", mux_pllpcm_clkm, CLK_SOURCE_SBC6, 105, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc6), + TEGRA_INIT_DATA_MUX("sata_oob", NULL, "tegra_sata_oob", mux_pllpcm_clkm, CLK_SOURCE_SATA_OOB, 123, &periph_v_regs, TEGRA_PERIPH_ON_APB, sata_oob), + TEGRA_INIT_DATA_MUX("sata", NULL, "tegra_sata", mux_pllpcm_clkm, CLK_SOURCE_SATA, 124, &periph_v_regs, TEGRA_PERIPH_ON_APB, sata), + TEGRA_INIT_DATA_MUX("ndflash", NULL, "tegra_nand", mux_pllpcm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_l_regs, TEGRA_PERIPH_ON_APB, ndflash), + TEGRA_INIT_DATA_MUX("ndspeed", NULL, "tegra_nand_speed", mux_pllpcm_clkm, CLK_SOURCE_NDSPEED, 80, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), + TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllpcm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir), + TEGRA_INIT_DATA_MUX("csite", NULL, "csite", mux_pllpcm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, TEGRA_PERIPH_ON_APB, csite), + TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllpcm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, TEGRA_PERIPH_ON_APB, la), + TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllpcm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr), + TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllpcm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi), + TEGRA_INIT_DATA_MUX("tsensor", NULL, "tegra-tsensor", mux_pllpc_clkm_clk32k, CLK_SOURCE_TSENSOR, 100, &periph_v_regs, TEGRA_PERIPH_ON_APB, tsensor), + TEGRA_INIT_DATA_MUX("i2cslow", NULL, "i2cslow", mux_pllpc_clk32k_clkm, CLK_SOURCE_I2CSLOW, 81, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2cslow), + TEGRA_INIT_DATA_INT("vde", NULL, "vde", mux_pllpcm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde), + TEGRA_INIT_DATA_INT("vi", "vi", "tegra_camera", mux_pllmcpa, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi), + TEGRA_INIT_DATA_INT("epp", NULL, "epp", mux_pllmcpa, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp), + TEGRA_INIT_DATA_INT("mpe", NULL, "mpe", mux_pllmcpa, CLK_SOURCE_MPE, 60, &periph_h_regs, 0, mpe), + TEGRA_INIT_DATA_INT("host1x", NULL, "host1x", mux_pllmcpa, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x), + TEGRA_INIT_DATA_INT("3d", NULL, "3d", mux_pllmcpa, CLK_SOURCE_3D, 24, &periph_l_regs, TEGRA_PERIPH_MANUAL_RESET, gr3d), + TEGRA_INIT_DATA_INT("3d2", NULL, "3d2", mux_pllmcpa, CLK_SOURCE_3D2, 98, &periph_v_regs, TEGRA_PERIPH_MANUAL_RESET, gr3d2), + TEGRA_INIT_DATA_INT("2d", NULL, "2d", mux_pllmcpa, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr2d), + TEGRA_INIT_DATA_INT("se", NULL, "se", mux_pllpcm_clkm, CLK_SOURCE_SE, 127, &periph_v_regs, 0, se), + TEGRA_INIT_DATA_MUX("mselect", NULL, "mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, &periph_v_regs, 0, mselect), + TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllpcm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor), + TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllpcm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1), + TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllpcm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2), + TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllpcm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3), + TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllpcm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4), + TEGRA_INIT_DATA_MUX("cve", NULL, "cve", mux_pllpdc_clkm, CLK_SOURCE_CVE, 49, &periph_h_regs, 0, cve), + TEGRA_INIT_DATA_MUX("tvo", NULL, "tvo", mux_pllpdc_clkm, CLK_SOURCE_TVO, 49, &periph_h_regs, 0, tvo), + TEGRA_INIT_DATA_MUX("tvdac", NULL, "tvdac", mux_pllpdc_clkm, CLK_SOURCE_TVDAC, 53, &periph_h_regs, 0, tvdac), + TEGRA_INIT_DATA_MUX("actmon", NULL, "actmon", mux_pllpc_clk32k_clkm, CLK_SOURCE_ACTMON, 119, &periph_v_regs, 0, actmon), + TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllmcpa, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor), + TEGRA_INIT_DATA_DIV16("i2c1", "div-clk", "tegra-i2c.0", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2c1), + TEGRA_INIT_DATA_DIV16("i2c2", "div-clk", "tegra-i2c.1", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, TEGRA_PERIPH_ON_APB, i2c2), + TEGRA_INIT_DATA_DIV16("i2c3", "div-clk", "tegra-i2c.2", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2c3), + TEGRA_INIT_DATA_DIV16("i2c4", "div-clk", "tegra-i2c.3", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2c4), + TEGRA_INIT_DATA_DIV16("i2c5", "div-clk", "tegra-i2c.4", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, &periph_h_regs, TEGRA_PERIPH_ON_APB, i2c5), + TEGRA_INIT_DATA_UART("uarta", NULL, "tegra_uart.0", mux_pllpcm_clkm, CLK_SOURCE_UARTA, 6, &periph_l_regs, uarta), + TEGRA_INIT_DATA_UART("uartb", NULL, "tegra_uart.1", mux_pllpcm_clkm, CLK_SOURCE_UARTB, 7, &periph_l_regs, uartb), + TEGRA_INIT_DATA_UART("uartc", NULL, "tegra_uart.2", mux_pllpcm_clkm, CLK_SOURCE_UARTC, 55, &periph_h_regs, uartc), + TEGRA_INIT_DATA_UART("uartd", NULL, "tegra_uart.3", mux_pllpcm_clkm, CLK_SOURCE_UARTD, 65, &periph_u_regs, uartd), + TEGRA_INIT_DATA_UART("uarte", NULL, "tegra_uart.4", mux_pllpcm_clkm, CLK_SOURCE_UARTE, 66, &periph_u_regs, uarte), + TEGRA_INIT_DATA_MUX8("hdmi", NULL, "hdmi", mux_pllpmdacd2_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi), + TEGRA_INIT_DATA_MUX8("extern1", NULL, "extern1", mux_plla_clk32k_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, &periph_v_regs, 0, extern1), + TEGRA_INIT_DATA_MUX8("extern2", NULL, "extern2", mux_plla_clk32k_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, &periph_v_regs, 0, extern2), + TEGRA_INIT_DATA_MUX8("extern3", NULL, "extern3", mux_plla_clk32k_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, &periph_v_regs, 0, extern3), + TEGRA_INIT_DATA("pwm", NULL, "pwm", mux_pllpc_clk32k_clkm, CLK_SOURCE_PWM, 28, 2, 0, 0, 8, 1, 0, &periph_l_regs, 17, periph_clk_enb_refcnt, 0, pwm), +}; + +static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = { + TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllpmdacd2_clkm, CLK_SOURCE_DISP1, 29, 3, 27, &periph_l_regs, 0, disp1), + TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllpmdacd2_clkm, CLK_SOURCE_DISP2, 29, 3, 26, &periph_l_regs, 0, disp2), + TEGRA_INIT_DATA_NODIV("dsib", NULL, "tegradc.1", mux_plld_out0_plld2_out0, CLK_SOURCE_DSIB, 25, 1, 82, &periph_u_regs, 0, dsib), +}; + +static void __init tegra30_periph_clk_init(void) +{ + struct tegra_periph_init_data *data; + struct clk *clk; + int i; + + /* apbdma */ + clk = tegra_clk_register_periph_gate("apbdma", "clk_m", 0, clk_base, 0, 34, + &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra-apbdma"); + clks[apbdma] = clk; + + /* rtc */ + clk = tegra_clk_register_periph_gate("rtc", "clk_32k", + TEGRA_PERIPH_NO_RESET | TEGRA_PERIPH_ON_APB, + clk_base, 0, 4, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "rtc-tegra"); + clks[rtc] = clk; + + /* timer */ + clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base, 0, + 5, &periph_l_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "timer"); + clks[timer] = clk; + + /* kbc */ + clk = tegra_clk_register_periph_gate("kbc", "clk_32k", + TEGRA_PERIPH_NO_RESET | TEGRA_PERIPH_ON_APB, + clk_base, 0, 36, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra-kbc"); + clks[kbc] = clk; + + /* csus */ + clk = tegra_clk_register_periph_gate("csus", "clk_m", + TEGRA_PERIPH_NO_RESET | TEGRA_PERIPH_ON_APB, + clk_base, 0, 92, &periph_u_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "csus", "tengra_camera"); + clks[csus] = clk; + + /* vcp */ + clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0, clk_base, 0, 29, + &periph_l_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "vcp", "tegra-avp"); + clks[vcp] = clk; + + /* bsea */ + clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0, clk_base, 0, + 62, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "bsea", "tegra-avp"); + clks[bsea] = clk; + + /* bsev */ + clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0, clk_base, 0, + 63, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "bsev", "tegra-aes"); + clks[bsev] = clk; + + /* usbd */ + clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base, 0, + 22, &periph_l_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "fsl-tegra-udc"); + clks[usbd] = clk; + + /* usb2 */ + clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base, 0, + 58, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra-ehci.1"); + clks[usb2] = clk; + + /* usb3 */ + clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base, 0, + 59, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra-ehci.2"); + clks[usb3] = clk; + + /* dsia */ + clk = tegra_clk_register_periph_gate("dsia", "pll_d_out0", 0, clk_base, + 0, 48, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "dsia", "tegradc.0"); + clks[dsia] = clk; + + /* csi */ + clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base, + 0, 52, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "csi", "tegra_camera"); + clks[csi] = clk; + + /* isp */ + clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0, 23, + &periph_l_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "isp", "tegra_camera"); + clks[isp] = clk; + + /* pcie */ + clk = tegra_clk_register_periph_gate("pcie", "clk_m", 0, clk_base, 0, + 70, &periph_u_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "pcie", "tegra-pcie"); + clks[pcie] = clk; + + /* afi */ + clk = tegra_clk_register_periph_gate("afi", "clk_m", 0, clk_base, 0, 72, + &periph_u_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "afi", "tegra-pcie"); + clks[afi] = clk; + + /* kfuse */ + clk = tegra_clk_register_periph_gate("kfuse", "clk_m", + TEGRA_PERIPH_ON_APB, + clk_base, 0, 40, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "kfuse-tegra"); + clks[kfuse] = clk; + + /* fuse */ + clk = tegra_clk_register_periph_gate("fuse", "clk_m", + TEGRA_PERIPH_ON_APB, + clk_base, 0, 39, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "fuse", "fuse-tegra"); + clks[fuse] = clk; + + /* fuse_burn */ + clk = tegra_clk_register_periph_gate("fuse_burn", "clk_m", + TEGRA_PERIPH_ON_APB, + clk_base, 0, 39, &periph_h_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "fuse_burn", "fuse-tegra"); + clks[fuse_burn] = clk; + + /* apbif */ + clk = tegra_clk_register_periph_gate("apbif", "clk_m", 0, + clk_base, 0, 107, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "apbif", "tegra30-ahub"); + clks[apbif] = clk; + + /* hda2hdmi */ + clk = tegra_clk_register_periph_gate("hda2hdmi", "clk_m", + TEGRA_PERIPH_ON_APB, + clk_base, 0, 128, &periph_w_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "hda2hdmi", "tegra30-hda"); + clks[hda2hdmi] = clk; + + /* sata_cold */ + clk = tegra_clk_register_periph_gate("sata_cold", "clk_m", + TEGRA_PERIPH_ON_APB, + clk_base, 0, 129, &periph_w_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "tegra_sata_cold"); + clks[sata_cold] = clk; + + /* dtv */ + clk = tegra_clk_register_periph_gate("dtv", "clk_m", + TEGRA_PERIPH_ON_APB, + clk_base, 0, 79, &periph_u_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "dtv"); + clks[dtv] = clk; + + /* emc */ + clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, + ARRAY_SIZE(mux_pllmcp_clkm), 0, + clk_base + CLK_SOURCE_EMC, + 30, 2, 0, NULL); + clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, + 57, &periph_h_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "emc", NULL); + clks[emc] = clk; + + /* i2c1-fast */ + clk = clk_register_fixed_factor(NULL, "i2c1-fast", "pll_p_out3", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "fast-clk", "tegra-i2c.0"); + + /* i2c2-fast */ + clk = clk_register_fixed_factor(NULL, "i2c2-fast", "pll_p_out3", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "fast-clk", "tegra-i2c.1"); + + /* i2c3-fast */ + clk = clk_register_fixed_factor(NULL, "i2c3-fast", "pll_p_out3", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "fast-clk", "tegra-i2c.2"); + + /* i2c4-fast */ + clk = clk_register_fixed_factor(NULL, "i2c4-fast", "pll_p_out3", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "fast-clk", "tegra-i2c.3"); + + /* i2c5-fast */ + clk = clk_register_fixed_factor(NULL, "i2c5-fast", "pll_p_out3", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "fast-clk", "tegra-i2c.5"); + + /* dsi1-fixed */ + clk = clk_register_fixed_factor(NULL, "dsi1-fixed", "pll_p_out3", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "dsi-fixed", "tegradc.0"); + + /* dsi2-fixed */ + clk = clk_register_fixed_factor(NULL, "dsi2-fixed", "pll_p_out3", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "dsi-fixed", "tegradc.1"); + + for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { + data = &tegra_periph_clk_list[i]; + clk = tegra_clk_register_periph(data->name, data->parent_names, + data->num_parents, &data->periph, + clk_base, data->offset); + clk_register_clkdev(clk, data->con_id, data->dev_id); + clks[data->clk_id] = clk; + } + + for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) { + data = &tegra_periph_nodiv_clk_list[i]; + clk = tegra_clk_register_periph_nodiv(data->name, + data->parent_names, + data->num_parents, &data->periph, + clk_base, data->offset); + clk_register_clkdev(clk, data->con_id, data->dev_id); + clks[data->clk_id] = clk; + } +} + +static void __init tegra30_fixed_clk_init(void) +{ + struct clk *clk; + + /* clk_32k */ + clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT, + 32768); + clk_register_clkdev(clk, "clk_32k", NULL); + clks[clk_32k] = clk; + + /* clk_m_div2 */ + clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "clk_m_div2", NULL); + clks[clk_m_div2] = clk; + + /* clk_m_div4 */ + clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m", + CLK_SET_RATE_PARENT, 1, 4); + clk_register_clkdev(clk, "clk_m_div4", NULL); + clks[clk_m_div4] = clk; + + /* cml0 */ + clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, + 0, 0, &cml_lock); + clk_register_clkdev(clk, "cml0", NULL); + clks[cml0] = clk; + + /* cml1 */ + clk = clk_register_gate(NULL, "cml1", "pll_e", 0, clk_base + PLLE_AUX, + 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) +{ + struct clk *clk; + unsigned int pll_ref_div; + + tegra30_clk_measure_input_freq(); + + /* clk_m */ + clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT, + input_freq); + clk_register_clkdev(clk, "clk_m", NULL); + clks[clk_m] = clk; + + /* pll_ref */ + pll_ref_div = tegra30_get_pll_ref_div(); + clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m", + CLK_SET_RATE_PARENT, 1, pll_ref_div); + clk_register_clkdev(clk, "pll_ref", NULL); + clks[pll_ref] = clk; +} + +/* Tegra30 CPU clock and reset control functions */ +static void tegra30_wait_cpu_in_reset(u32 cpu) +{ + unsigned int reg; + + do { + reg = readl(clk_base + + TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS); + cpu_relax(); + } while (!(reg & (1 << cpu))); /* check CPU been reset or not */ + + return; +} + +static void tegra30_put_cpu_in_reset(u32 cpu) +{ + writel(CPU_RESET(cpu), + clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + dmb(); +} + +static void tegra30_cpu_out_of_reset(u32 cpu) +{ + writel(CPU_RESET(cpu), + clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); + wmb(); +} + + +static void tegra30_enable_cpu_clock(u32 cpu) +{ + unsigned int reg; + + writel(CPU_CLOCK(cpu), + clk_base + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); + reg = readl(clk_base + + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); +} + +static void tegra30_disable_cpu_clock(u32 cpu) +{ + + unsigned int reg; + + reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + writel(reg | CPU_CLOCK(cpu), + clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); +} + +#ifdef CONFIG_PM_SLEEP +static bool tegra30_cpu_rail_off_ready(void) +{ + unsigned int cpu_rst_status; + int cpu_pwr_status; + + cpu_rst_status = readl(clk_base + + TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS); + cpu_pwr_status = tegra_powergate_is_powered(TEGRA_POWERGATE_CPU1) || + tegra_powergate_is_powered(TEGRA_POWERGATE_CPU2) || + tegra_powergate_is_powered(TEGRA_POWERGATE_CPU3); + + if (((cpu_rst_status & 0xE) != 0xE) || cpu_pwr_status) + return false; + + return true; +} + +static void tegra30_cpu_clock_suspend(void) +{ + /* switch coresite to clk_m, save off original source */ + tegra30_cpu_clk_sctx.clk_csite_src = + readl(clk_base + CLK_RESET_SOURCE_CSITE); + writel(3<<30, clk_base + CLK_RESET_SOURCE_CSITE); + + tegra30_cpu_clk_sctx.cpu_burst = + readl(clk_base + CLK_RESET_CCLK_BURST); + tegra30_cpu_clk_sctx.pllx_base = + readl(clk_base + CLK_RESET_PLLX_BASE); + tegra30_cpu_clk_sctx.pllx_misc = + readl(clk_base + CLK_RESET_PLLX_MISC); + tegra30_cpu_clk_sctx.cclk_divider = + readl(clk_base + CLK_RESET_CCLK_DIVIDER); +} + +static void tegra30_cpu_clock_resume(void) +{ + unsigned int reg, policy; + + /* Is CPU complex already running on PLLX? */ + reg = readl(clk_base + CLK_RESET_CCLK_BURST); + policy = (reg >> CLK_RESET_CCLK_BURST_POLICY_SHIFT) & 0xF; + + if (policy == CLK_RESET_CCLK_IDLE_POLICY) + reg = (reg >> CLK_RESET_CCLK_IDLE_POLICY_SHIFT) & 0xF; + else if (policy == CLK_RESET_CCLK_RUN_POLICY) + reg = (reg >> CLK_RESET_CCLK_RUN_POLICY_SHIFT) & 0xF; + else + BUG(); + + if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) { + /* restore PLLX settings if CPU is on different PLL */ + writel(tegra30_cpu_clk_sctx.pllx_misc, + clk_base + CLK_RESET_PLLX_MISC); + writel(tegra30_cpu_clk_sctx.pllx_base, + clk_base + CLK_RESET_PLLX_BASE); + + /* wait for PLL stabilization if PLLX was enabled */ + if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30)) + udelay(300); + } + + /* + * Restore original burst policy setting for calls resulting from CPU + * LP2 in idle or system suspend. + */ + writel(tegra30_cpu_clk_sctx.cclk_divider, + clk_base + CLK_RESET_CCLK_DIVIDER); + writel(tegra30_cpu_clk_sctx.cpu_burst, + clk_base + CLK_RESET_CCLK_BURST); + + writel(tegra30_cpu_clk_sctx.clk_csite_src, + clk_base + CLK_RESET_SOURCE_CSITE); +} +#endif + +static struct tegra_cpu_car_ops tegra30_cpu_car_ops = { + .wait_for_reset = tegra30_wait_cpu_in_reset, + .put_in_reset = tegra30_put_cpu_in_reset, + .out_of_reset = tegra30_cpu_out_of_reset, + .enable_clock = tegra30_enable_cpu_clock, + .disable_clock = tegra30_disable_cpu_clock, +#ifdef CONFIG_PM_SLEEP + .rail_off_ready = tegra30_cpu_rail_off_ready, + .suspend = tegra30_cpu_clock_suspend, + .resume = tegra30_cpu_clock_resume, +#endif +}; + +static __initdata struct tegra_clk_init_table init_table[] = { + {uarta, pll_p, 408000000, 1}, + {pll_a, clk_max, 564480000, 1}, + {pll_a_out0, clk_max, 11289600, 1}, + {extern1, pll_a_out0, 0, 1}, + {clk_out_1_mux, extern1, 0, 0}, + {clk_out_1, clk_max, 0, 1}, + {blink, clk_max, 0, 1}, + {i2s0, pll_a_out0, 11289600, 0}, + {i2s1, pll_a_out0, 11289600, 0}, + {i2s2, pll_a_out0, 11289600, 0}, + {i2s3, pll_a_out0, 11289600, 0}, + {i2s4, pll_a_out0, 11289600, 0}, + {sdmmc1, pll_p, 48000000, 0}, + {sdmmc2, pll_p, 48000000, 0}, + {sdmmc3, pll_p, 48000000, 0}, + {pll_m, clk_max, 0, 1}, + {pclk, clk_max, 0, 1}, + {csite, clk_max, 0, 1}, + {emc, clk_max, 0, 1}, + {mselect, clk_max, 0, 1}, + {sbc1, pll_p, 100000000, 0}, + {sbc2, pll_p, 100000000, 0}, + {sbc3, pll_p, 100000000, 0}, + {sbc4, pll_p, 100000000, 0}, + {sbc5, pll_p, 100000000, 0}, + {sbc6, pll_p, 100000000, 0}, + {host1x, pll_c, 150000000, 0}, + {disp1, pll_p, 600000000, 0}, + {disp2, pll_p, 600000000, 0}, + {twd, clk_max, 0, 1}, + {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ +}; + +/* + * Some clocks may be used by different drivers depending on the board + * configuration. List those here to register them twice in the clock lookup + * table under two names. + */ +static struct tegra_clk_duplicate tegra_clk_duplicates[] = { + TEGRA_CLK_DUPLICATE(uarta, "serial8250.0", NULL), + TEGRA_CLK_DUPLICATE(uartb, "serial8250.1", NULL), + TEGRA_CLK_DUPLICATE(uartc, "serial8250.2", NULL), + TEGRA_CLK_DUPLICATE(uartd, "serial8250.3", NULL), + TEGRA_CLK_DUPLICATE(uarte, "serial8250.4", NULL), + TEGRA_CLK_DUPLICATE(usbd, "utmip-pad", NULL), + TEGRA_CLK_DUPLICATE(usbd, "tegra-ehci.0", NULL), + TEGRA_CLK_DUPLICATE(usbd, "tegra-otg", NULL), + TEGRA_CLK_DUPLICATE(pll_p, "tegradc.0", "parent"), + TEGRA_CLK_DUPLICATE(pll_p, "tegradc.1", "parent"), + TEGRA_CLK_DUPLICATE(pll_d2_out0, "hdmi", "parent"), + TEGRA_CLK_DUPLICATE(dsib, "tegradc.0", "dsib"), + TEGRA_CLK_DUPLICATE(dsia, "tegradc.1", "dsia"), + TEGRA_CLK_DUPLICATE(bsev, "tegra-avp", "bsev"), + TEGRA_CLK_DUPLICATE(bsev, "nvavp", "bsev"), + TEGRA_CLK_DUPLICATE(vde, "tegra-aes", "vde"), + TEGRA_CLK_DUPLICATE(bsea, "tegra-aes", "bsea"), + TEGRA_CLK_DUPLICATE(bsea, "nvavp", "bsea"), + TEGRA_CLK_DUPLICATE(cml1, "tegra_sata_cml", NULL), + TEGRA_CLK_DUPLICATE(cml0, "tegra_pcie", "cml"), + TEGRA_CLK_DUPLICATE(pciex, "tegra_pcie", "pciex"), + TEGRA_CLK_DUPLICATE(i2c1, "tegra-i2c-slave.0", NULL), + TEGRA_CLK_DUPLICATE(i2c2, "tegra-i2c-slave.1", NULL), + TEGRA_CLK_DUPLICATE(i2c3, "tegra-i2c-slave.2", NULL), + TEGRA_CLK_DUPLICATE(i2c4, "tegra-i2c-slave.3", NULL), + TEGRA_CLK_DUPLICATE(i2c5, "tegra-i2c-slave.4", NULL), + TEGRA_CLK_DUPLICATE(sbc1, "spi_slave_tegra.0", NULL), + TEGRA_CLK_DUPLICATE(sbc2, "spi_slave_tegra.1", NULL), + TEGRA_CLK_DUPLICATE(sbc3, "spi_slave_tegra.2", NULL), + TEGRA_CLK_DUPLICATE(sbc4, "spi_slave_tegra.3", NULL), + TEGRA_CLK_DUPLICATE(sbc5, "spi_slave_tegra.4", NULL), + TEGRA_CLK_DUPLICATE(sbc6, "spi_slave_tegra.5", NULL), + TEGRA_CLK_DUPLICATE(twd, "smp_twd", NULL), + TEGRA_CLK_DUPLICATE(vcp, "nvavp", "vcp"), + TEGRA_CLK_DUPLICATE(i2s0, NULL, "i2s0"), + TEGRA_CLK_DUPLICATE(i2s1, NULL, "i2s1"), + TEGRA_CLK_DUPLICATE(i2s2, NULL, "i2s2"), + TEGRA_CLK_DUPLICATE(i2s3, NULL, "i2s3"), + TEGRA_CLK_DUPLICATE(i2s4, NULL, "i2s4"), + TEGRA_CLK_DUPLICATE(dam0, NULL, "dam0"), + TEGRA_CLK_DUPLICATE(dam1, NULL, "dam1"), + TEGRA_CLK_DUPLICATE(dam2, NULL, "dam2"), + TEGRA_CLK_DUPLICATE(spdif_in, NULL, "spdif_in"), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.0", "fast-clk"), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.1", "fast-clk"), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.2", "fast-clk"), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.3", "fast-clk"), + TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.4", "fast-clk"), + TEGRA_CLK_DUPLICATE(clk_max, NULL, NULL), /* MUST be the last entry */ +}; + +static const struct of_device_id pmc_match[] __initconst = { + { .compatible = "nvidia,tegra30-pmc" }, + {}, +}; + +void __init tegra30_clock_init(struct device_node *np) +{ + struct device_node *node; + int i; + + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("ioremap tegra30 CAR failed\n"); + return; + } + + node = of_find_matching_node(NULL, pmc_match); + if (!node) { + pr_err("Failed to find pmc node\n"); + BUG(); + } + + pmc_base = of_iomap(node, 0); + if (!pmc_base) { + pr_err("Can't map pmc registers\n"); + BUG(); + } + + tegra30_osc_clk_init(); + tegra30_fixed_clk_init(); + tegra30_pll_init(); + tegra30_super_clk_init(); + tegra30_periph_clk_init(); + tegra30_audio_clk_init(); + tegra30_pmc_clk_init(); + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + if (IS_ERR(clks[i])) { + pr_err("Tegra30 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + BUG(); + } + if (!clks[i]) + clks[i] = ERR_PTR(-EINVAL); + } + + tegra_init_dup_clks(tegra_clk_duplicates, clks, clk_max); + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + tegra_init_from_table(init_table, clks, clk_max); + + tegra_cpu_car_ops = &tegra30_cpu_car_ops; +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 82835f24533c..0744731c6229 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -493,4 +493,10 @@ void tegra20_clock_init(struct device_node *np); 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 */ + #endif /* TEGRA_CLK_H */ -- cgit v1.2.3 From 61fd290d213e25d5a119b8ca25644001ed9f8f2d Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Fri, 11 Jan 2013 13:16:26 +0530 Subject: ARM: tegra: migrate to new clock code Migrate Tegra clock support to drivers/clk/tegra, this involves moving: 1. definition of tegra_cpu_car_ops to clk.c 2. definition of reset functions to clk-peripheral.c 3. change parent of cpu clock. 4. Remove legacy clock initialization. 5. Initialize clocks using DT. 6. Remove all instance of mach/clk.h Signed-off-by: Prashant Gaikwad [swarren: use to_clk_periph_gate().] Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/clk/tegra/clk.c | 16 ++++++++++++++++ drivers/dma/tegra20-apb-dma.c | 2 +- drivers/gpu/drm/tegra/dc.c | 3 +-- drivers/gpu/drm/tegra/drm.c | 1 - drivers/gpu/drm/tegra/hdmi.c | 3 +-- drivers/i2c/busses/i2c-tegra.c | 3 +-- drivers/input/keyboard/tegra-kbc.c | 2 +- drivers/spi/spi-tegra20-sflash.c | 2 +- drivers/spi/spi-tegra20-slink.c | 2 +- drivers/staging/nvec/nvec.c | 3 +-- 11 files changed, 62 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 5978e81b175b..788486e6331a 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -110,6 +110,44 @@ static void clk_periph_disable(struct clk_hw *hw) gate_ops->disable(gate_hw); } +void tegra_periph_reset_deassert(struct clk *c) +{ + struct clk_hw *hw = __clk_get_hw(c); + struct tegra_clk_periph *periph = to_clk_periph(hw); + struct tegra_clk_periph_gate *gate; + + if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) { + gate = to_clk_periph_gate(hw); + if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) { + WARN_ON(1); + return; + } + } else { + gate = &periph->gate; + } + + tegra_periph_reset(gate, 0); +} + +void tegra_periph_reset_assert(struct clk *c) +{ + struct clk_hw *hw = __clk_get_hw(c); + struct tegra_clk_periph *periph = to_clk_periph(hw); + struct tegra_clk_periph_gate *gate; + + if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) { + gate = to_clk_periph_gate(hw); + if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) { + WARN_ON(1); + return; + } + } else { + gate = &periph->gate; + } + + tegra_periph_reset(gate, 1); +} + const struct clk_ops tegra_clk_periph_ops = { .get_parent = clk_periph_get_parent, .set_parent = clk_periph_set_parent, diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index cf023a937208..a603b9af0ad3 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -16,9 +16,14 @@ #include #include +#include +#include #include "clk.h" +/* Global data of Tegra CPU CAR ops */ +struct tegra_cpu_car_ops *tegra_cpu_car_ops; + void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, struct clk *clks[], int clk_max) { @@ -67,3 +72,14 @@ 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 }, + { } +}; + +void __init tegra_clocks_init(void) +{ + of_clk_init(tegra_dt_clk_match); +} diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index c39e61bc8172..afc9b89e20f4 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -31,8 +31,8 @@ #include #include #include +#include -#include #include "dmaengine.h" #define TEGRA_APBDMA_GENERAL 0x0 diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 656b2e3334a6..56813f967c8f 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -12,8 +12,7 @@ #include #include #include - -#include +#include #include "drm.h" #include "dc.h" diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 3a503c9e4686..d980dc75788c 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index e060c7e6434d..92ad276cc5e0 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -14,8 +14,7 @@ #include #include #include - -#include +#include #include "hdmi.h" #include "drm.h" diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 7b38877ffec1..c7aca35e38fd 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -29,11 +29,10 @@ #include #include #include +#include #include -#include - #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) #define BYTES_PER_FIFO_WORD 4 diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index c76f96872d31..54ac1dc7d477 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #define KBC_MAX_DEBOUNCE_CNT 0x3ffu diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 448a8cc71df3..02feaa51a0fa 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #define SPI_COMMAND 0x000 #define SPI_GO BIT(30) diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 651167f2e0af..fa208a5cc612 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #define SLINK_COMMAND 0x000 #define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 2830946860d1..d51615b19797 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -37,8 +37,7 @@ #include #include #include - -#include +#include #include "nvec.h" -- cgit v1.2.3 From 3cb919022a7ab7628fcd69c28d475d2dbb1cb150 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Fri, 11 Jan 2013 13:31:20 +0530 Subject: spi: tegra: do not use clock name to get clock Since Tegra spi devices do not have multiple clocks, no need to use clock name to get the clock. Signed-off-by: Prashant Gaikwad Acked-by: Grant Likely Signed-off-by: Stephen Warren --- drivers/spi/spi-tegra20-sflash.c | 2 +- drivers/spi/spi-tegra20-slink.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 02feaa51a0fa..e5dce91b74ca 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -525,7 +525,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) goto exit_free_master; } - tsd->clk = devm_clk_get(&pdev->dev, "spi"); + tsd->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(tsd->clk)) { dev_err(&pdev->dev, "can not get clock\n"); ret = PTR_ERR(tsd->clk); diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index fa208a5cc612..e255e7a35678 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1191,7 +1191,7 @@ static int tegra_slink_probe(struct platform_device *pdev) goto exit_free_master; } - tspi->clk = devm_clk_get(&pdev->dev, "slink"); + tspi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(tspi->clk)) { dev_err(&pdev->dev, "can not get clock\n"); ret = PTR_ERR(tspi->clk); -- cgit v1.2.3 From 1e54f0a21e7debcd8acb0fe39adc6f81751e1b57 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Fri, 11 Jan 2013 13:31:24 +0530 Subject: staging: nvec: remove use of clk_get_sys As clock information is added to device tree clock can be looked up using clk_get. Remove use of clk_get_sys. Signed-off-by: Prashant Gaikwad Acked-by: Greg Kroah-Hartman Acked-by: Marc Dietrich Acked-by: Julian Andres Klode [swarren: updated TODO file to remove entry that requested this change] Signed-off-by: Stephen Warren --- drivers/staging/nvec/TODO | 4 ---- drivers/staging/nvec/nvec.c | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/nvec/TODO b/drivers/staging/nvec/TODO index f950ab890e2e..e5ae42a0b44a 100644 --- a/drivers/staging/nvec/TODO +++ b/drivers/staging/nvec/TODO @@ -1,9 +1,5 @@ ToDo list (incomplete, unordered) - add compile as module support - - fix clk usage - should not be using clk_get_sys(), but clk_get(&pdev->dev, conn) - where conn is either NULL if the device only has one clock, or - the device specific name if it has multiple clocks. - move half of the nvec init stuff to i2c-tegra.c - move event handling to nvec_events - finish suspend/resume support diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index d51615b19797..9417941974f7 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -770,7 +770,7 @@ static int tegra_nvec_probe(struct platform_device *pdev) return -ENODEV; } - i2c_clk = clk_get_sys("tegra-i2c.2", "div-clk"); + i2c_clk = clk_get(&pdev->dev, "div-clk"); if (IS_ERR(i2c_clk)) { dev_err(nvec->dev, "failed to get controller clock\n"); return -ENODEV; -- cgit v1.2.3 From e5dd26302275562d8bedf885e7aa91bd73bfa041 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Fri, 11 Jan 2013 13:31:28 +0530 Subject: clk: tegra20: remove unused TEGRA_CLK_DUPLICATE()s With device tree support added for Tegra clocks look up is done from device tree, remove unused TEGRA_CLK_DUPLICATE()s. Signed-off-by: Prashant Gaikwad Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra20.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 4ae8287b0f81..077d5b9ffe22 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -1189,28 +1189,11 @@ static __initdata struct tegra_clk_init_table init_table[] = { * table under two names. */ static struct tegra_clk_duplicate tegra_clk_duplicates[] = { - TEGRA_CLK_DUPLICATE(uarta, "serial8250.0", NULL), - TEGRA_CLK_DUPLICATE(uartb, "serial8250.1", NULL), - TEGRA_CLK_DUPLICATE(uartc, "serial8250.2", NULL), - TEGRA_CLK_DUPLICATE(uartd, "serial8250.3", NULL), - TEGRA_CLK_DUPLICATE(uarte, "serial8250.4", NULL), TEGRA_CLK_DUPLICATE(usbd, "utmip-pad", NULL), TEGRA_CLK_DUPLICATE(usbd, "tegra-ehci.0", NULL), TEGRA_CLK_DUPLICATE(usbd, "tegra-otg", NULL), - TEGRA_CLK_DUPLICATE(pll_p, "tegradc.0", "parent"), - TEGRA_CLK_DUPLICATE(pll_p, "tegradc.1", "parent"), - TEGRA_CLK_DUPLICATE(pll_d_out0, "hdmi", "parent"), - TEGRA_CLK_DUPLICATE(gr2d, "tegra_grhost", "gr2d"), - TEGRA_CLK_DUPLICATE(gr3d, "tegra_grhost", "gr3d"), - TEGRA_CLK_DUPLICATE(epp, "tegra_grhost", "epp"), - TEGRA_CLK_DUPLICATE(mpe, "tegra_grhost", "mpe"), - TEGRA_CLK_DUPLICATE(vde, "tegra-aes", "vde"), TEGRA_CLK_DUPLICATE(cclk, NULL, "cpu"), TEGRA_CLK_DUPLICATE(twd, "smp_twd", NULL), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.0", "fast-clk"), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.1", "fast-clk"), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.2", "fast-clk"), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.3", "fast-clk"), TEGRA_CLK_DUPLICATE(clk_max, NULL, NULL), /* Must be the last entry */ }; -- cgit v1.2.3 From ef3ffe5a0458606c488def757bb7f6dd013c2db5 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Fri, 11 Jan 2013 13:31:29 +0530 Subject: clk: tegra30: remove unused TEGRA_CLK_DUPLICATE()s With device tree support added for Tegra clocks look up is done from device tree, remove unused TEGRA_CLK_DUPLICATE()s. Signed-off-by: Prashant Gaikwad Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra30.c | 70 ----------------------------------------- 1 file changed, 70 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 1c2765db1199..a1638129eba4 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1654,41 +1654,6 @@ static void __init tegra30_periph_clk_init(void) clk_register_clkdev(clk, "emc", NULL); clks[emc] = clk; - /* i2c1-fast */ - clk = clk_register_fixed_factor(NULL, "i2c1-fast", "pll_p_out3", - CLK_SET_RATE_PARENT, 1, 1); - clk_register_clkdev(clk, "fast-clk", "tegra-i2c.0"); - - /* i2c2-fast */ - clk = clk_register_fixed_factor(NULL, "i2c2-fast", "pll_p_out3", - CLK_SET_RATE_PARENT, 1, 1); - clk_register_clkdev(clk, "fast-clk", "tegra-i2c.1"); - - /* i2c3-fast */ - clk = clk_register_fixed_factor(NULL, "i2c3-fast", "pll_p_out3", - CLK_SET_RATE_PARENT, 1, 1); - clk_register_clkdev(clk, "fast-clk", "tegra-i2c.2"); - - /* i2c4-fast */ - clk = clk_register_fixed_factor(NULL, "i2c4-fast", "pll_p_out3", - CLK_SET_RATE_PARENT, 1, 1); - clk_register_clkdev(clk, "fast-clk", "tegra-i2c.3"); - - /* i2c5-fast */ - clk = clk_register_fixed_factor(NULL, "i2c5-fast", "pll_p_out3", - CLK_SET_RATE_PARENT, 1, 1); - clk_register_clkdev(clk, "fast-clk", "tegra-i2c.5"); - - /* dsi1-fixed */ - clk = clk_register_fixed_factor(NULL, "dsi1-fixed", "pll_p_out3", - CLK_SET_RATE_PARENT, 1, 1); - clk_register_clkdev(clk, "dsi-fixed", "tegradc.0"); - - /* dsi2-fixed */ - clk = clk_register_fixed_factor(NULL, "dsi2-fixed", "pll_p_out3", - CLK_SET_RATE_PARENT, 1, 1); - clk_register_clkdev(clk, "dsi-fixed", "tegradc.1"); - for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { data = &tegra_periph_clk_list[i]; clk = tegra_clk_register_periph(data->name, data->parent_names, @@ -1948,19 +1913,9 @@ static __initdata struct tegra_clk_init_table init_table[] = { * table under two names. */ static struct tegra_clk_duplicate tegra_clk_duplicates[] = { - TEGRA_CLK_DUPLICATE(uarta, "serial8250.0", NULL), - TEGRA_CLK_DUPLICATE(uartb, "serial8250.1", NULL), - TEGRA_CLK_DUPLICATE(uartc, "serial8250.2", NULL), - TEGRA_CLK_DUPLICATE(uartd, "serial8250.3", NULL), - TEGRA_CLK_DUPLICATE(uarte, "serial8250.4", NULL), TEGRA_CLK_DUPLICATE(usbd, "utmip-pad", NULL), TEGRA_CLK_DUPLICATE(usbd, "tegra-ehci.0", NULL), TEGRA_CLK_DUPLICATE(usbd, "tegra-otg", NULL), - TEGRA_CLK_DUPLICATE(pll_p, "tegradc.0", "parent"), - TEGRA_CLK_DUPLICATE(pll_p, "tegradc.1", "parent"), - TEGRA_CLK_DUPLICATE(pll_d2_out0, "hdmi", "parent"), - TEGRA_CLK_DUPLICATE(dsib, "tegradc.0", "dsib"), - TEGRA_CLK_DUPLICATE(dsia, "tegradc.1", "dsia"), TEGRA_CLK_DUPLICATE(bsev, "tegra-avp", "bsev"), TEGRA_CLK_DUPLICATE(bsev, "nvavp", "bsev"), TEGRA_CLK_DUPLICATE(vde, "tegra-aes", "vde"), @@ -1969,33 +1924,8 @@ static struct tegra_clk_duplicate tegra_clk_duplicates[] = { TEGRA_CLK_DUPLICATE(cml1, "tegra_sata_cml", NULL), TEGRA_CLK_DUPLICATE(cml0, "tegra_pcie", "cml"), TEGRA_CLK_DUPLICATE(pciex, "tegra_pcie", "pciex"), - TEGRA_CLK_DUPLICATE(i2c1, "tegra-i2c-slave.0", NULL), - TEGRA_CLK_DUPLICATE(i2c2, "tegra-i2c-slave.1", NULL), - TEGRA_CLK_DUPLICATE(i2c3, "tegra-i2c-slave.2", NULL), - TEGRA_CLK_DUPLICATE(i2c4, "tegra-i2c-slave.3", NULL), - TEGRA_CLK_DUPLICATE(i2c5, "tegra-i2c-slave.4", NULL), - TEGRA_CLK_DUPLICATE(sbc1, "spi_slave_tegra.0", NULL), - TEGRA_CLK_DUPLICATE(sbc2, "spi_slave_tegra.1", NULL), - TEGRA_CLK_DUPLICATE(sbc3, "spi_slave_tegra.2", NULL), - TEGRA_CLK_DUPLICATE(sbc4, "spi_slave_tegra.3", NULL), - TEGRA_CLK_DUPLICATE(sbc5, "spi_slave_tegra.4", NULL), - TEGRA_CLK_DUPLICATE(sbc6, "spi_slave_tegra.5", NULL), TEGRA_CLK_DUPLICATE(twd, "smp_twd", NULL), TEGRA_CLK_DUPLICATE(vcp, "nvavp", "vcp"), - TEGRA_CLK_DUPLICATE(i2s0, NULL, "i2s0"), - TEGRA_CLK_DUPLICATE(i2s1, NULL, "i2s1"), - TEGRA_CLK_DUPLICATE(i2s2, NULL, "i2s2"), - TEGRA_CLK_DUPLICATE(i2s3, NULL, "i2s3"), - TEGRA_CLK_DUPLICATE(i2s4, NULL, "i2s4"), - TEGRA_CLK_DUPLICATE(dam0, NULL, "dam0"), - TEGRA_CLK_DUPLICATE(dam1, NULL, "dam1"), - TEGRA_CLK_DUPLICATE(dam2, NULL, "dam2"), - TEGRA_CLK_DUPLICATE(spdif_in, NULL, "spdif_in"), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.0", "fast-clk"), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.1", "fast-clk"), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.2", "fast-clk"), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.3", "fast-clk"), - TEGRA_CLK_DUPLICATE(pll_p_out3, "tegra-i2c.4", "fast-clk"), TEGRA_CLK_DUPLICATE(clk_max, NULL, NULL), /* MUST be the last entry */ }; -- cgit v1.2.3 From 5b26c94cdc1a201f3ae1aa75bf8963da53e1aa71 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Fri, 11 Jan 2013 17:31:29 -0700 Subject: usb: phy: use kzalloc to allocate struct tegra_usb_phy Use kzalloc instead of kmalloc to allocate struct tegra_usb_phy. This ensures that all function pointers in member u_phy are initialized to NULL. Signed-off-by: Venu Byravarasu Acked-by: Greg Kroah-Hartman Signed-off-by: Stephen Warren --- drivers/usb/phy/tegra_usb_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c index 9d13c81754e0..2d3cae9a785d 100644 --- a/drivers/usb/phy/tegra_usb_phy.c +++ b/drivers/usb/phy/tegra_usb_phy.c @@ -740,7 +740,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, int i; int err; - phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); + phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); if (!phy) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 585355c5b54e2ecdcd6463aec264d907bc416727 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 13 Dec 2012 20:59:08 +0000 Subject: usb: host: tegra: Resetting PORT0 based on information received via DT. Tegra USB host driver is using port instance number, to handle some of the hardware issues on SOC e.g. reset PORT0 twice etc. As instance number based handling looks ugly, making use of information passed through DT for achieving this. Signed-off-by: Venu Byravarasu Acked-by: Greg Kroah-Hartman Signed-off-by: Stephen Warren --- drivers/usb/host/ehci-tegra.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index acf17556bd87..55a9cdee2949 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -43,6 +43,7 @@ struct tegra_ehci_hcd { struct usb_phy *transceiver; int host_resumed; int port_resuming; + bool needs_double_reset; enum tegra_usb_phy_port_speed port_speed; }; @@ -184,7 +185,7 @@ static int tegra_ehci_hub_control( } /* For USB1 port we need to issue Port Reset twice internally */ - if (tegra->phy->instance == 0 && + if (tegra->needs_double_reset && (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) { spin_unlock_irqrestore(&ehci->lock, flags); return tegra_ehci_internal_port_reset(ehci, status_reg); @@ -666,6 +667,9 @@ static int tegra_ehci_probe(struct platform_device *pdev) clk_prepare_enable(tegra->emc_clk); clk_set_rate(tegra->emc_clk, 400000000); + tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node, + "nvidia,needs-double-reset"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "Failed to get I/O memory\n"); -- cgit v1.2.3 From 5e18150a7b4139e15e1ed7d7c938dd91f7f12354 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Tue, 15 Jan 2013 15:49:30 +0530 Subject: usb: phy: remove unused APIs from Tegra PHY. As tegra_usb_phy_clk_disable/enable() are not being used, removing them. Signed-off-by: Venu Byravarasu Signed-off-by: Stephen Warren --- drivers/usb/phy/tegra_usb_phy.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c index 2d3cae9a785d..3059384326b6 100644 --- a/drivers/usb/phy/tegra_usb_phy.c +++ b/drivers/usb/phy/tegra_usb_phy.c @@ -825,16 +825,3 @@ void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) } EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); -void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) -{ - if (!phy_is_ulpi(phy)) - utmi_phy_clk_disable(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_disable); - -void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy) -{ - if (!phy_is_ulpi(phy)) - utmi_phy_clk_enable(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_enable); -- cgit v1.2.3 From 3a55c6a8b96f055783a1b9d5497f66b56a9ea0d7 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Wed, 16 Jan 2013 03:30:20 +0000 Subject: USB: PHY: tegra: get rid of instance number to differentiate legacy controller Tegra20 USB has 3 PHY instances. Instance 0 is based on legacy PHY interface and other two are standard interfaces. As instance number was used to differentiate legacy from standard interfaces, used DT param to get this info and processed accordingly. Signed-off-by: Venu Byravarasu Acked-by: Felipe Balbi Signed-off-by: Stephen Warren --- drivers/usb/phy/tegra_usb_phy.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c index 3059384326b6..79280fe90893 100644 --- a/drivers/usb/phy/tegra_usb_phy.c +++ b/drivers/usb/phy/tegra_usb_phy.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -221,7 +222,7 @@ static int utmip_pad_open(struct tegra_usb_phy *phy) return PTR_ERR(phy->pad_clk); } - if (phy->instance == 0) { + if (phy->is_legacy_phy) { phy->pad_regs = phy->regs; } else { phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE); @@ -236,7 +237,7 @@ static int utmip_pad_open(struct tegra_usb_phy *phy) static void utmip_pad_close(struct tegra_usb_phy *phy) { - if (phy->instance != 0) + if (!phy->is_legacy_phy) iounmap(phy->pad_regs); clk_put(phy->pad_clk); } @@ -305,7 +306,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) unsigned long val; void __iomem *base = phy->regs; - if (phy->instance == 0) { + if (phy->is_legacy_phy) { val = readl(base + USB_SUSP_CTRL); val |= USB_SUSP_SET; writel(val, base + USB_SUSP_CTRL); @@ -315,9 +316,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_SET; writel(val, base + USB_SUSP_CTRL); - } - - if (phy->instance == 2) { + } else { val = readl(base + USB_PORTSC1); val |= USB_PORTSC1_PHCD; writel(val, base + USB_PORTSC1); @@ -332,7 +331,7 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) unsigned long val; void __iomem *base = phy->regs; - if (phy->instance == 0) { + if (phy->is_legacy_phy) { val = readl(base + USB_SUSP_CTRL); val |= USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); @@ -342,9 +341,7 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); - } - - if (phy->instance == 2) { + } else { val = readl(base + USB_PORTSC1); val &= ~USB_PORTSC1_PHCD; writel(val, base + USB_PORTSC1); @@ -365,7 +362,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val |= UTMIP_RESET; writel(val, base + USB_SUSP_CTRL); - if (phy->instance == 0) { + if (phy->is_legacy_phy) { val = readl(base + USB1_LEGACY_CTRL); val |= USB1_NO_LEGACY_MODE; writel(val, base + USB1_LEGACY_CTRL); @@ -440,16 +437,14 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val |= UTMIP_BIAS_PDTRK_COUNT(0x5); writel(val, base + UTMIP_BIAS_CFG1); - if (phy->instance == 0) { + if (phy->is_legacy_phy) { val = readl(base + UTMIP_SPARE_CFG0); if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) val &= ~FUSE_SETUP_SEL; else val |= FUSE_SETUP_SEL; writel(val, base + UTMIP_SPARE_CFG0); - } - - if (phy->instance == 2) { + } else { val = readl(base + USB_SUSP_CTRL); val |= UTMIP_PHY_ENABLE; writel(val, base + USB_SUSP_CTRL); @@ -459,7 +454,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val &= ~UTMIP_RESET; writel(val, base + USB_SUSP_CTRL); - if (phy->instance == 0) { + if (phy->is_legacy_phy) { val = readl(base + USB1_LEGACY_CTRL); val &= ~USB1_VBUS_SENSE_CTL_MASK; val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD; @@ -472,7 +467,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) utmi_phy_clk_enable(phy); - if (phy->instance == 2) { + if (!phy->is_legacy_phy) { val = readl(base + USB_PORTSC1); val &= ~USB_PORTSC1_PTS(~0); writel(val, base + USB_PORTSC1); @@ -739,6 +734,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, unsigned long parent_rate; int i; int err; + struct device_node *np = dev->of_node; phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); if (!phy) @@ -749,6 +745,8 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, phy->config = config; phy->mode = phy_mode; phy->dev = dev; + phy->is_legacy_phy = + of_property_read_bool(np, "nvidia,has-legacy-mode"); if (!phy->config) { if (phy_is_ulpi(phy)) { -- cgit v1.2.3 From 3f9db1a19a8a17f6000973194c6a1d63c5cebf21 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Wed, 16 Jan 2013 03:30:21 +0000 Subject: USB: PHY: tegra: Get rid of instance number to differentiate PHY type Tegra20 USB has 3 PHY instances: Instance 1 and 3 are UTMI. Instance 2 is ULPI. As instance number was used to differentiate ULPI from UTMI, used DT param to get this info and processed accordingly. Signed-off-by: Venu Byravarasu Acked-by: Felipe Balbi [swarren: moved assignment of phy->is_ulpi_phy into this patch out of next patch.] Signed-off-by: Stephen Warren --- drivers/usb/phy/tegra_usb_phy.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c index 79280fe90893..48fa5258ce0b 100644 --- a/drivers/usb/phy/tegra_usb_phy.c +++ b/drivers/usb/phy/tegra_usb_phy.c @@ -209,11 +209,6 @@ static struct tegra_utmip_config utmip_default[] = { }, }; -static inline bool phy_is_ulpi(struct tegra_usb_phy *phy) -{ - return (phy->instance == 1); -} - static int utmip_pad_open(struct tegra_usb_phy *phy) { phy->pad_clk = clk_get_sys("utmip-pad", NULL); @@ -655,7 +650,7 @@ static int tegra_phy_init(struct usb_phy *x) struct tegra_ulpi_config *ulpi_config; int err; - if (phy_is_ulpi(phy)) { + if (phy->is_ulpi_phy) { ulpi_config = phy->config; phy->clk = clk_get_sys(NULL, ulpi_config->clk); if (IS_ERR(phy->clk)) { @@ -693,7 +688,7 @@ static void tegra_usb_phy_close(struct usb_phy *x) { struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - if (phy_is_ulpi(phy)) + if (phy->is_ulpi_phy) clk_put(phy->clk); else utmip_pad_close(phy); @@ -704,7 +699,7 @@ static void tegra_usb_phy_close(struct usb_phy *x) static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { - if (phy_is_ulpi(phy)) + if (phy->is_ulpi_phy) return ulpi_phy_power_on(phy); else return utmi_phy_power_on(phy); @@ -712,7 +707,7 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) { - if (phy_is_ulpi(phy)) + if (phy->is_ulpi_phy) return ulpi_phy_power_off(phy); else return utmi_phy_power_off(phy); @@ -747,9 +742,14 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, phy->dev = dev; phy->is_legacy_phy = of_property_read_bool(np, "nvidia,has-legacy-mode"); + err = of_property_match_string(np, "phy_type", "ulpi"); + if (err < 0) + phy->is_ulpi_phy = false; + else + phy->is_ulpi_phy = true; if (!phy->config) { - if (phy_is_ulpi(phy)) { + if (phy->is_ulpi_phy) { pr_err("%s: ulpi phy configuration missing", __func__); err = -EINVAL; goto err0; @@ -796,14 +796,14 @@ EXPORT_SYMBOL_GPL(tegra_usb_phy_open); void tegra_usb_phy_preresume(struct tegra_usb_phy *phy) { - if (!phy_is_ulpi(phy)) + if (!phy->is_ulpi_phy) utmi_phy_preresume(phy); } EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); void tegra_usb_phy_postresume(struct tegra_usb_phy *phy) { - if (!phy_is_ulpi(phy)) + if (!phy->is_ulpi_phy) utmi_phy_postresume(phy); } EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); @@ -811,14 +811,14 @@ EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, enum tegra_usb_phy_port_speed port_speed) { - if (!phy_is_ulpi(phy)) + if (!phy->is_ulpi_phy) utmi_phy_restore_start(phy, port_speed); } EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start); void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) { - if (!phy_is_ulpi(phy)) + if (!phy->is_ulpi_phy) utmi_phy_restore_end(phy); } EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); -- cgit v1.2.3 From bbdabdb62d86090511410728644a19291bf300cf Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 17 Jan 2013 20:15:37 +0000 Subject: usb: add APIs to access host registers from Tegra PHY As Tegra PHY driver needs to access one of the host registers, added few APIs. Signed-off-by: Venu Byravarasu Acked-by: Alan Stern [swarren: moved assignment of phy->is_ulpi_phy to previous patch.] Signed-off-by: Stephen Warren --- drivers/usb/host/ehci-tegra.c | 51 +++++++++++++++++++++++++++++++++++++++-- drivers/usb/phy/tegra_usb_phy.c | 47 +++++-------------------------------- 2 files changed, 55 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 55a9cdee2949..1f596fb7cf71 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -2,7 +2,7 @@ * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs * * Copyright (C) 2010 Google, Inc. - * Copyright (C) 2009 NVIDIA Corporation + * Copyright (C) 2009 - 2013 NVIDIA Corporation * * 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 @@ -26,13 +26,18 @@ #include #include #include - +#include #include #define TEGRA_USB_BASE 0xC5000000 #define TEGRA_USB2_BASE 0xC5004000 #define TEGRA_USB3_BASE 0xC5008000 +/* PORTSC registers */ +#define TEGRA_USB_PORTSC1 0x184 +#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) +#define TEGRA_USB_PORTSC1_PHCD (1 << 23) + #define TEGRA_USB_DMA_ALIGN 32 struct tegra_ehci_hcd { @@ -605,6 +610,37 @@ static const struct dev_pm_ops tegra_ehci_pm_ops = { #endif +/* Bits of PORTSC1, which will get cleared by writing 1 into them */ +#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) + +void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val) +{ + unsigned long val; + struct usb_hcd *hcd = bus_to_hcd(x->otg->host); + void __iomem *base = hcd->regs; + + val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; + val &= ~TEGRA_USB_PORTSC1_PTS(3); + val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3); + writel(val, base + TEGRA_USB_PORTSC1); +} +EXPORT_SYMBOL_GPL(tegra_ehci_set_pts); + +void tegra_ehci_set_phcd(struct usb_phy *x, bool enable) +{ + unsigned long val; + struct usb_hcd *hcd = bus_to_hcd(x->otg->host); + void __iomem *base = hcd->regs; + + val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; + if (enable) + val |= TEGRA_USB_PORTSC1_PHCD; + else + val &= ~TEGRA_USB_PORTSC1_PHCD; + writel(val, base + TEGRA_USB_PORTSC1); +} +EXPORT_SYMBOL_GPL(tegra_ehci_set_phcd); + static u64 tegra_ehci_dma_mask = DMA_BIT_MASK(32); static int tegra_ehci_probe(struct platform_device *pdev) @@ -616,6 +652,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) int err = 0; int irq; int instance = pdev->id; + struct usb_phy *u_phy; pdata = pdev->dev.platform_data; if (!pdata) { @@ -718,6 +755,16 @@ static int tegra_ehci_probe(struct platform_device *pdev) usb_phy_init(&tegra->phy->u_phy); + hcd->phy = u_phy = &tegra->phy->u_phy; + u_phy->otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), + GFP_KERNEL); + if (!u_phy->otg) { + dev_err(&pdev->dev, "Failed to alloc memory for otg\n"); + err = -ENOMEM; + goto fail_io; + } + u_phy->otg->host = hcd_to_bus(hcd); + err = usb_phy_set_suspend(&tegra->phy->u_phy, 0); if (err) { dev_err(&pdev->dev, "Failed to power on the phy\n"); diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c index 48fa5258ce0b..d4657045b8b1 100644 --- a/drivers/usb/phy/tegra_usb_phy.c +++ b/drivers/usb/phy/tegra_usb_phy.c @@ -36,19 +36,6 @@ #define ULPI_VIEWPORT 0x170 -#define USB_PORTSC1 0x184 -#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) -#define USB_PORTSC1_PSPD(x) (((x) & 0x3) << 26) -#define USB_PORTSC1_PHCD (1 << 23) -#define USB_PORTSC1_WKOC (1 << 22) -#define USB_PORTSC1_WKDS (1 << 21) -#define USB_PORTSC1_WKCN (1 << 20) -#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) -#define USB_PORTSC1_PP (1 << 12) -#define USB_PORTSC1_SUSP (1 << 7) -#define USB_PORTSC1_PE (1 << 2) -#define USB_PORTSC1_CCS (1 << 0) - #define USB_SUSP_CTRL 0x400 #define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) #define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) @@ -311,11 +298,8 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_SET; writel(val, base + USB_SUSP_CTRL); - } else { - val = readl(base + USB_PORTSC1); - val |= USB_PORTSC1_PHCD; - writel(val, base + USB_PORTSC1); - } + } else + tegra_ehci_set_phcd(&phy->u_phy, true); if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) pr_err("%s: timeout waiting for phy to stabilize\n", __func__); @@ -336,11 +320,8 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); - } else { - val = readl(base + USB_PORTSC1); - val &= ~USB_PORTSC1_PHCD; - writel(val, base + USB_PORTSC1); - } + } else + tegra_ehci_set_phcd(&phy->u_phy, false); if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, USB_PHY_CLK_VALID)) @@ -462,11 +443,8 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) utmi_phy_clk_enable(phy); - if (!phy->is_legacy_phy) { - val = readl(base + USB_PORTSC1); - val &= ~USB_PORTSC1_PTS(~0); - writel(val, base + USB_PORTSC1); - } + if (!phy->is_legacy_phy) + tegra_ehci_set_pts(&phy->u_phy, 0); return 0; } @@ -611,10 +589,6 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) return ret; } - val = readl(base + USB_PORTSC1); - val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN; - writel(val, base + USB_PORTSC1); - val = readl(base + USB_SUSP_CTRL); val |= USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); @@ -629,17 +603,8 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) static int ulpi_phy_power_off(struct tegra_usb_phy *phy) { - unsigned long val; - void __iomem *base = phy->regs; struct tegra_ulpi_config *config = phy->config; - /* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB - * Controller to immediately bring the ULPI PHY out of low power - */ - val = readl(base + USB_PORTSC1); - val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN); - writel(val, base + USB_PORTSC1); - clk_disable(phy->clk); return gpio_direction_output(config->reset_gpio, 0); } -- cgit v1.2.3 From 9304512151b0933c454f0842cdb19bec23422bc5 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 22 Jan 2013 17:06:32 -0700 Subject: usb: host: tegra: don't touch EMC clock Clock "emc" is for the External Memory Controller. The USB driver has no business touching this clock directly. Remove the code that does so. Acked-by: Greg Kroah-Hartman Acked-by: Alan Stern Signed-off-by: Stephen Warren --- drivers/usb/host/ehci-tegra.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 1f596fb7cf71..b02622a936c2 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -44,7 +44,6 @@ struct tegra_ehci_hcd { struct ehci_hcd *ehci; struct tegra_usb_phy *phy; struct clk *clk; - struct clk *emc_clk; struct usb_phy *transceiver; int host_resumed; int port_resuming; @@ -56,7 +55,6 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - clk_prepare_enable(tegra->emc_clk); clk_prepare_enable(tegra->clk); usb_phy_set_suspend(&tegra->phy->u_phy, 0); tegra->host_resumed = 1; @@ -69,7 +67,6 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd) tegra->host_resumed = 0; usb_phy_set_suspend(&tegra->phy->u_phy, 1); clk_disable_unprepare(tegra->clk); - clk_disable_unprepare(tegra->emc_clk); } static int tegra_ehci_internal_port_reset( @@ -694,16 +691,6 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (err) goto fail_clk; - tegra->emc_clk = devm_clk_get(&pdev->dev, "emc"); - if (IS_ERR(tegra->emc_clk)) { - dev_err(&pdev->dev, "Can't get emc clock\n"); - err = PTR_ERR(tegra->emc_clk); - goto fail_emc_clk; - } - - clk_prepare_enable(tegra->emc_clk); - clk_set_rate(tegra->emc_clk, 400000000); - tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node, "nvidia,needs-double-reset"); @@ -813,8 +800,6 @@ fail: #endif usb_phy_shutdown(&tegra->phy->u_phy); fail_io: - clk_disable_unprepare(tegra->emc_clk); -fail_emc_clk: clk_disable_unprepare(tegra->clk); fail_clk: usb_put_hcd(hcd); @@ -842,8 +827,6 @@ static int tegra_ehci_remove(struct platform_device *pdev) clk_disable_unprepare(tegra->clk); - clk_disable_unprepare(tegra->emc_clk); - return 0; } -- cgit v1.2.3 From 4a2e32794e71db679b91df1d9c98921b2e32ec4e Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Tue, 15 Jan 2013 22:10:48 +0000 Subject: clk: tegra20: Implementing CPU low-power function for tegra_cpu_car_ops Implementing suspend, resume and rail_off_ready API for tegra_cpu_car_ops. These functions were used for CPU powered-down state maintenance. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra20.c | 93 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 077d5b9ffe22..5d41569883a7 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "clk.h" @@ -104,6 +105,13 @@ #define SUPER_SCLK_DIVIDER 0x2c #define CLK_SYSTEM_RATE 0x30 +#define CCLK_BURST_POLICY_SHIFT 28 +#define CCLK_RUN_POLICY_SHIFT 4 +#define CCLK_IDLE_POLICY_SHIFT 0 +#define CCLK_IDLE_POLICY 1 +#define CCLK_RUN_POLICY 2 +#define CCLK_BURST_POLICY_PLLX 8 + #define CLK_SOURCE_I2S1 0x100 #define CLK_SOURCE_I2S2 0x104 #define CLK_SOURCE_SPDIF_OUT 0x108 @@ -169,6 +177,17 @@ #define CPU_CLOCK(cpu) (0x1 << (8 + cpu)) #define CPU_RESET(cpu) (0x1111ul << (cpu)) +#ifdef CONFIG_PM_SLEEP +static struct cpu_clk_suspend_context { + u32 pllx_misc; + u32 pllx_base; + + u32 cpu_burst; + u32 clk_csite_src; + u32 cclk_divider; +} tegra20_cpu_clk_sctx; +#endif + static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; static void __iomem *clk_base; @@ -1136,12 +1155,86 @@ static void tegra20_disable_cpu_clock(u32 cpu) clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); } +#ifdef CONFIG_PM_SLEEP +static bool tegra20_cpu_rail_off_ready(void) +{ + unsigned int cpu_rst_status; + + cpu_rst_status = readl(clk_base + + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + + return !!(cpu_rst_status & 0x2); +} + +static void tegra20_cpu_clock_suspend(void) +{ + /* switch coresite to clk_m, save off original source */ + tegra20_cpu_clk_sctx.clk_csite_src = + readl(clk_base + CLK_SOURCE_CSITE); + writel(3<<30, clk_base + CLK_SOURCE_CSITE); + + tegra20_cpu_clk_sctx.cpu_burst = + readl(clk_base + CCLK_BURST_POLICY); + tegra20_cpu_clk_sctx.pllx_base = + readl(clk_base + PLLX_BASE); + tegra20_cpu_clk_sctx.pllx_misc = + readl(clk_base + PLLX_MISC); + tegra20_cpu_clk_sctx.cclk_divider = + readl(clk_base + SUPER_CCLK_DIVIDER); +} + +static void tegra20_cpu_clock_resume(void) +{ + unsigned int reg, policy; + + /* Is CPU complex already running on PLLX? */ + reg = readl(clk_base + CCLK_BURST_POLICY); + policy = (reg >> CCLK_BURST_POLICY_SHIFT) & 0xF; + + if (policy == CCLK_IDLE_POLICY) + reg = (reg >> CCLK_IDLE_POLICY_SHIFT) & 0xF; + else if (policy == CCLK_RUN_POLICY) + reg = (reg >> CCLK_RUN_POLICY_SHIFT) & 0xF; + else + BUG(); + + if (reg != CCLK_BURST_POLICY_PLLX) { + /* restore PLLX settings if CPU is on different PLL */ + writel(tegra20_cpu_clk_sctx.pllx_misc, + clk_base + PLLX_MISC); + writel(tegra20_cpu_clk_sctx.pllx_base, + clk_base + PLLX_BASE); + + /* wait for PLL stabilization if PLLX was enabled */ + if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30)) + udelay(300); + } + + /* + * Restore original burst policy setting for calls resulting from CPU + * LP2 in idle or system suspend. + */ + writel(tegra20_cpu_clk_sctx.cclk_divider, + clk_base + SUPER_CCLK_DIVIDER); + writel(tegra20_cpu_clk_sctx.cpu_burst, + clk_base + CCLK_BURST_POLICY); + + writel(tegra20_cpu_clk_sctx.clk_csite_src, + clk_base + CLK_SOURCE_CSITE); +} +#endif + static struct tegra_cpu_car_ops tegra20_cpu_car_ops = { .wait_for_reset = tegra20_wait_cpu_in_reset, .put_in_reset = tegra20_put_cpu_in_reset, .out_of_reset = tegra20_cpu_out_of_reset, .enable_clock = tegra20_enable_cpu_clock, .disable_clock = tegra20_disable_cpu_clock, +#ifdef CONFIG_PM_SLEEP + .rail_off_ready = tegra20_cpu_rail_off_ready, + .suspend = tegra20_cpu_clock_suspend, + .resume = tegra20_cpu_clock_resume, +#endif }; static __initdata struct tegra_clk_init_table init_table[] = { -- cgit v1.2.3 From ab137d04db5a4b32250ce5ef1b288ce6cf06adf6 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 24 Jan 2013 15:57:03 +0530 Subject: usb: host: tegra: make use of PHY pointer of HCD As pointer to PHY structure can be stored in struct usb_hcd making use of it, to call Tegra PHY APIs. Call to usb_phy_shutdown() is moved up in tegra_ehci_remove(), so that to avoid dereferencing of hcd after its freed up. Signed-off-by: Venu Byravarasu Acked-by: Alan Stern Acked-by: Felipe Balbi Acked-by: Greg Kroah-Hartman Signed-off-by: Stephen Warren --- drivers/usb/host/ehci-tegra.c | 27 +++++++++++++-------------- drivers/usb/phy/tegra_usb_phy.c | 16 ++++++++++++---- 2 files changed, 25 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index b02622a936c2..568aecc7075b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -56,7 +56,7 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd) struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); clk_prepare_enable(tegra->clk); - usb_phy_set_suspend(&tegra->phy->u_phy, 0); + usb_phy_set_suspend(hcd->phy, 0); tegra->host_resumed = 1; } @@ -65,7 +65,7 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd) struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); tegra->host_resumed = 0; - usb_phy_set_suspend(&tegra->phy->u_phy, 1); + usb_phy_set_suspend(hcd->phy, 1); clk_disable_unprepare(tegra->clk); } @@ -159,7 +159,7 @@ static int tegra_ehci_hub_control( if (tegra->port_resuming && !(temp & PORT_SUSPEND)) { /* Resume completed, re-enable disconnect detection */ tegra->port_resuming = 0; - tegra_usb_phy_postresume(tegra->phy); + tegra_usb_phy_postresume(hcd->phy); } } @@ -212,7 +212,7 @@ static int tegra_ehci_hub_control( goto done; /* Disable disconnect detection during port resume */ - tegra_usb_phy_preresume(tegra->phy); + tegra_usb_phy_preresume(hcd->phy); ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); @@ -476,7 +476,7 @@ static int controller_resume(struct device *dev) } /* Force the phy to keep data lines in suspend state */ - tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); + tegra_ehci_phy_restore_start(hcd->phy, tegra->port_speed); /* Enable host mode */ tdi_reset(ehci); @@ -543,17 +543,17 @@ static int controller_resume(struct device *dev) } } - tegra_ehci_phy_restore_end(tegra->phy); + tegra_ehci_phy_restore_end(hcd->phy); goto done; restart: if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) - tegra_ehci_phy_restore_end(tegra->phy); + tegra_ehci_phy_restore_end(hcd->phy); tegra_ehci_restart(hcd); done: - tegra_usb_phy_preresume(tegra->phy); + tegra_usb_phy_preresume(hcd->phy); tegra->port_resuming = 1; return 0; } @@ -740,9 +740,9 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_io; } - usb_phy_init(&tegra->phy->u_phy); - hcd->phy = u_phy = &tegra->phy->u_phy; + usb_phy_init(hcd->phy); + u_phy->otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), GFP_KERNEL); if (!u_phy->otg) { @@ -752,7 +752,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) } u_phy->otg->host = hcd_to_bus(hcd); - err = usb_phy_set_suspend(&tegra->phy->u_phy, 0); + err = usb_phy_set_suspend(hcd->phy, 0); if (err) { dev_err(&pdev->dev, "Failed to power on the phy\n"); goto fail; @@ -798,7 +798,7 @@ fail: if (!IS_ERR_OR_NULL(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, NULL); #endif - usb_phy_shutdown(&tegra->phy->u_phy); + usb_phy_shutdown(hcd->phy); fail_io: clk_disable_unprepare(tegra->clk); fail_clk: @@ -820,11 +820,10 @@ static int tegra_ehci_remove(struct platform_device *pdev) otg_set_host(tegra->transceiver->otg, NULL); #endif + usb_phy_shutdown(hcd->phy); usb_remove_hcd(hcd); usb_put_hcd(hcd); - usb_phy_shutdown(&tegra->phy->u_phy); - clk_disable_unprepare(tegra->clk); return 0; diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c index d4657045b8b1..5487d38481af 100644 --- a/drivers/usb/phy/tegra_usb_phy.c +++ b/drivers/usb/phy/tegra_usb_phy.c @@ -759,30 +759,38 @@ err0: } EXPORT_SYMBOL_GPL(tegra_usb_phy_open); -void tegra_usb_phy_preresume(struct tegra_usb_phy *phy) +void tegra_usb_phy_preresume(struct usb_phy *x) { + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + if (!phy->is_ulpi_phy) utmi_phy_preresume(phy); } EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); -void tegra_usb_phy_postresume(struct tegra_usb_phy *phy) +void tegra_usb_phy_postresume(struct usb_phy *x) { + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + if (!phy->is_ulpi_phy) utmi_phy_postresume(phy); } EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); -void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, +void tegra_ehci_phy_restore_start(struct usb_phy *x, enum tegra_usb_phy_port_speed port_speed) { + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + if (!phy->is_ulpi_phy) utmi_phy_restore_start(phy, port_speed); } EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start); -void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) +void tegra_ehci_phy_restore_end(struct usb_phy *x) { + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + if (!phy->is_ulpi_phy) utmi_phy_restore_end(phy); } -- cgit v1.2.3 From a1671da560df1f6bd20d8c6934d1cfa2aacc4f32 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 12 Dec 2012 10:06:23 +0100 Subject: rtc: Add support of rtc-mv for MVEBU SoCs The Armada 370 and Armada XP Socs have the same controller that the one used in the orion platforms. This patch enables the selection of rtc-mv with mvebu platforms. Signed-off-by: Gregory CLEMENT Acked-by: Andrew Lunn Tested-by: Florian Fainelli Acked-by: Arnd Bergmann Signed-off-by: Jason Cooper --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 923a9da9c829..20354b43932e 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1023,7 +1023,7 @@ config RTC_DRV_TX4939 config RTC_DRV_MV tristate "Marvell SoC RTC" - depends on ARCH_KIRKWOOD || ARCH_DOVE + depends on ARCH_KIRKWOOD || ARCH_DOVE || ARCH_MVEBU help If you say yes here you will get support for the in-chip RTC that can be found in some of Marvell's SoC devices, such as -- cgit v1.2.3 From 9cfc94eb0f4843af5d1141a37d7b7ca5d3b27220 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 9 Jan 2013 13:22:15 +0100 Subject: cpuidle: kirkwood: Move out of mach directory Move the Kirkwood cpuidle driver out of arch/arm/mach-kirkwood and into drivers/cpuidle. Convert the driver into a platform driver. Signed-off-by: Andrew Lunn Signed-off-by: Jason Cooper --- drivers/cpuidle/Kconfig | 6 +++ drivers/cpuidle/Makefile | 1 + drivers/cpuidle/cpuidle-kirkwood.c | 106 +++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 drivers/cpuidle/cpuidle-kirkwood.c (limited to 'drivers') diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index c4cc27e5c8a5..071e2c3eec4f 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -39,4 +39,10 @@ config CPU_IDLE_CALXEDA help Select this to enable cpuidle on Calxeda processors. +config CPU_IDLE_KIRKWOOD + bool "CPU Idle Driver for Kirkwood processors" + depends on ARCH_KIRKWOOD + help + Select this to enable cpuidle on Kirkwood processors. + endif diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 03ee87482c71..24c6e7d945ed 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -6,3 +6,4 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o +obj-$(CONFIG_CPU_IDLE_KIRKWOOD) += cpuidle-kirkwood.o diff --git a/drivers/cpuidle/cpuidle-kirkwood.c b/drivers/cpuidle/cpuidle-kirkwood.c new file mode 100644 index 000000000000..670aa1e55cd6 --- /dev/null +++ b/drivers/cpuidle/cpuidle-kirkwood.c @@ -0,0 +1,106 @@ +/* + * arch/arm/mach-kirkwood/cpuidle.c + * + * CPU idle Marvell Kirkwood SoCs + * + * 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. + * + * The cpu idle uses wait-for-interrupt and DDR self refresh in order + * to implement two idle states - + * #1 wait-for-interrupt + * #2 wait-for-interrupt and DDR self refresh + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KIRKWOOD_MAX_STATES 2 + +static void __iomem *ddr_operation_base; + +/* Actual code that puts the SoC in different idle states */ +static int kirkwood_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + writel(0x7, ddr_operation_base); + cpu_do_idle(); + + return index; +} + +static struct cpuidle_driver kirkwood_idle_driver = { + .name = "kirkwood_idle", + .owner = THIS_MODULE, + .en_core_tk_irqen = 1, + .states[0] = ARM_CPUIDLE_WFI_STATE, + .states[1] = { + .enter = kirkwood_enter_idle, + .exit_latency = 10, + .target_residency = 100000, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "DDR SR", + .desc = "WFI and DDR Self Refresh", + }, + .state_count = KIRKWOOD_MAX_STATES, +}; +static struct cpuidle_device *device; + +static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device); + +/* Initialize CPU idle by registering the idle states */ +static int kirkwood_cpuidle_probe(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -EINVAL; + + ddr_operation_base = devm_request_and_ioremap(&pdev->dev, res); + if (!ddr_operation_base) + return -EADDRNOTAVAIL; + + device = &per_cpu(kirkwood_cpuidle_device, smp_processor_id()); + device->state_count = KIRKWOOD_MAX_STATES; + + cpuidle_register_driver(&kirkwood_idle_driver); + if (cpuidle_register_device(device)) { + pr_err("kirkwood_init_cpuidle: Failed registering\n"); + return -EIO; + } + return 0; +} + +int kirkwood_cpuidle_remove(struct platform_device *pdev) +{ + cpuidle_unregister_device(device); + cpuidle_unregister_driver(&kirkwood_idle_driver); + + return 0; +} + +static struct platform_driver kirkwood_cpuidle_driver = { + .probe = kirkwood_cpuidle_probe, + .remove = kirkwood_cpuidle_remove, + .driver = { + .name = "kirkwood_cpuidle", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(kirkwood_cpuidle_driver); + +MODULE_AUTHOR("Andrew Lunn "); +MODULE_DESCRIPTION("Kirkwood cpu idle driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kirkwood-cpuidle"); -- cgit v1.2.3 From 2fb318ff75d7c4d5ccc418ab4dcce19aa40fc059 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Fri, 21 Dec 2012 15:06:15 +0100 Subject: ARM: mxs: use apbx bus clock to drive the timers on timrotv2 timer resolution of ~32us is pretty low. v2 has 32bits resolution, so we have quite some headroom, and can use the 24MHz clock. v1 has only 16bits, so we only increase v2. So we just exchange the timrot clock in imx28. On imx23 we have timrotv1 and everything stays the same. Signed-off-by: Torben Hohn Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx28.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index db3af0874121..0174270abfe7 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -238,7 +238,7 @@ int __init mx28_clocks_init(void) of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } - clk_register_clkdev(clks[clk32k], NULL, "timrot"); + clk_register_clkdev(clks[xbus], NULL, "timrot"); clk_register_clkdev(clks[enet_out], NULL, "enet_out"); for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) -- cgit v1.2.3