diff options
author | Shashank Sharma <shashanks@nvidia.com> | 2012-03-15 21:01:02 +0530 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-03-23 14:01:10 -0700 |
commit | 042bad603d2acf6a1159be3a713ab5dac8080427 (patch) | |
tree | 814c06712eecbfc172eb0373bd83ec00a6b900b7 /arch/arm/mach-tegra/tegra2_clocks.c | |
parent | 372c0433d4010424d4130788efff7e945cddf716 (diff) |
ARM: tegra2: clock: Dynamic rate configuration
support dynamic clock rate configuration for pll_d. Till now tegra2
used to look into a pll_d frequency table to match input and output
frequencies, resulting fixed pll_d output frequencies. Whereas
tegra3 had code to configure pll_d for any desired rate using
dynamically generated m,n,p values.
Bug: 931908
Change-Id: I15322e2e4ac0aba58502575cdc83ca4a4542d1e4
Signed-off-by: Shashank Sharma <shashanks@nvidia.com>
Reviewed-on: http://git-master/r/90361
Reviewed-by: Kiran Adduri <kadduri@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_clocks.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra2_clocks.c | 144 |
1 files changed, 107 insertions, 37 deletions
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index e1c5f1e4188b..126a1d56591a 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -6,7 +6,7 @@ * Author: * Colin Cross <ccross@google.com> * - * Copyright (C) 2010-2011 NVIDIA Corporation + * Copyright (C) 2010-2012 NVIDIA Corporation * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -156,6 +156,7 @@ static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); static void __iomem *misc_gp_hidrev_base = IO_ADDRESS(TEGRA_APB_MISC_BASE); #define MISC_GP_HIDREV 0x804 +#define PLLDU_LFCON_SET_DIVN 600 static int tegra2_clk_shared_bus_update(struct clk *bus); @@ -748,6 +749,8 @@ static void tegra2_pll_clk_disable(struct clk *c) static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate) { u32 val; + u32 p_div = 0; + u32 old_base = 0; unsigned long input_rate; const struct clk_pll_freq_table *sel; @@ -756,52 +759,119 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate) input_rate = clk_get_rate(c->parent); for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) { if (sel->input_rate == input_rate && sel->output_rate == rate) { - c->mul = sel->n; - c->div = sel->m * sel->p; - - val = clk_readl(c->reg + PLL_BASE); - if (c->flags & PLL_FIXED) - val |= PLL_BASE_OVERRIDE; - val &= ~(PLL_BASE_DIVP_MASK | PLL_BASE_DIVN_MASK | - PLL_BASE_DIVM_MASK); - val |= (sel->m << PLL_BASE_DIVM_SHIFT) | - (sel->n << PLL_BASE_DIVN_SHIFT); - BUG_ON(sel->p < 1 || sel->p > 128); if (c->flags & PLLU) { + BUG_ON(sel->p < 1 || sel->p > 2); if (sel->p == 1) - val |= PLLU_BASE_POST_DIV; + p_div = PLLU_BASE_POST_DIV; } else { - if (sel->p == 2) - val |= 1 << PLL_BASE_DIVP_SHIFT; - else if (sel->p == 4) - val |= 2 << PLL_BASE_DIVP_SHIFT; - else if (sel->p == 8) - val |= 3 << PLL_BASE_DIVP_SHIFT; - else if (sel->p == 16) - val |= 4 << PLL_BASE_DIVP_SHIFT; - else if (sel->p == 32) - val |= 5 << PLL_BASE_DIVP_SHIFT; - else if (sel->p == 64) - val |= 6 << PLL_BASE_DIVP_SHIFT; - else if (sel->p == 128) - val |= 7 << PLL_BASE_DIVP_SHIFT; + BUG_ON(sel->p < 1); + for (val = sel->p; + val > 1; val >>= 1, p_div++) + ; + p_div <<= PLL_BASE_DIVP_SHIFT; } - clk_writel(val, c->reg + PLL_BASE); + break; + } + } - if (c->flags & PLL_HAS_CPCON) { - val = clk_readl(c->reg + PLL_MISC(c)); - val &= ~PLL_MISC_CPCON_MASK; - val |= sel->cpcon << PLL_MISC_CPCON_SHIFT; - clk_writel(val, c->reg + PLL_MISC(c)); + /*If required rate is not available in pll's frequency table, prepare + parameters manually */ + + if (sel->input_rate == 0) { + unsigned long cfreq; + BUG_ON(c->flags & PLLU); + struct clk_pll_freq_table cfg; + sel = &cfg; + + switch (input_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; + default: + if (c->parent->flags & DIV_U71_FIXED) { + /* PLLP_OUT1 rate is not in PLLA table */ + pr_warn("%s: failed %s ref/out rates %lu/%lu\n", + __func__, c->name, input_rate, rate); + cfreq = input_rate/(input_rate/1000000); + break; } + pr_err("%s: Unexpected reference rate %lu\n", + __func__, input_rate); + BUG(); + } - if (c->state == ON) - tegra2_pll_clk_enable(c); + /* 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 = 0x1 << p_div; + cfg.m = input_rate / cfreq; + cfg.n = cfg.output_rate / cfreq; + cfg.cpcon = 0x08; /* OUT_OF_TABLE_CPCON */ + + if ((cfg.m > (PLL_BASE_DIVM_MASK >> PLL_BASE_DIVM_SHIFT)) || + (cfg.n > (PLL_BASE_DIVN_MASK >> PLL_BASE_DIVN_SHIFT)) || + (p_div > (PLL_BASE_DIVP_MASK >> PLL_BASE_DIVP_SHIFT)) || + (cfg.output_rate > c->u.pll.vco_max)) { + pr_err("%s: Failed to set %s out-of-table rate %lu\n", + __func__, c->name, rate); + return -EINVAL; + } + p_div <<= PLL_BASE_DIVP_SHIFT; + } - return 0; + /*Setup multipliers and divisors, then setup rate*/ + + c->mul = sel->n; + c->div = sel->m * sel->p; + + old_base = val = clk_readl(c->reg + PLL_BASE); + if (c->flags & PLL_FIXED) + val |= PLL_BASE_OVERRIDE; + val &= ~(PLL_BASE_DIVM_MASK | PLL_BASE_DIVN_MASK | + ((c->flags & PLLU) ? PLLU_BASE_POST_DIV : PLL_BASE_DIVP_MASK)); + val |= (sel->m << PLL_BASE_DIVM_SHIFT) | + (sel->n << PLL_BASE_DIVN_SHIFT) | p_div; + if (val == old_base) + return 0; + + if (c->state == ON) { + tegra2_pll_clk_disable(c); + val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); + } + clk_writel(val, c->reg + PLL_BASE); + + if (c->flags & PLL_HAS_CPCON) { + val = clk_readl(c->reg + PLL_MISC(c)); + val &= ~PLL_MISC_CPCON_MASK; + val |= sel->cpcon << PLL_MISC_CPCON_SHIFT; + if (c->flags & (PLLU | PLLD)) { + val &= ~PLL_MISC_LFCON_MASK; + if (sel->n >= PLLDU_LFCON_SET_DIVN) + val |= 0x1 << PLL_MISC_LFCON_SHIFT; + } else if (c->flags & (PLLX | PLLM)) { + val &= ~(0x1 << PLL_MISC_DCCON_SHIFT); + if (rate >= (c->u.pll.vco_max >> 1)) + val |= 0x1 << PLL_MISC_DCCON_SHIFT; } + clk_writel(val, c->reg + PLL_MISC(c)); } - return -EINVAL; + + if (c->state == ON) + tegra2_pll_clk_enable(c); + + return 0; + } static struct clk_ops tegra_pll_ops = { |