diff options
author | Rhyland Klein <rklein@nvidia.com> | 2015-06-18 23:28:34 +0200 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2015-11-20 18:07:28 +0100 |
commit | 86c679a52294d4c4b989903a75e31495c04d688a (patch) | |
tree | 784a8f92ecd5315cb7cfddecb4f4a0e4369886f3 /drivers/clk/tegra/clk-pll.c | |
parent | clk: tegra: pll: Add code to handle if resets are supported by PLL (diff) | |
download | linux-86c679a52294d4c4b989903a75e31495c04d688a.tar.xz linux-86c679a52294d4c4b989903a75e31495c04d688a.zip |
clk: tegra: pll: Fix _pll_ramp_calc_pll logic and _calc_dynamic_ramp_rate
This removes the conversion from pdiv to hw, which is already taken
care of by _get_table_rate before this code is run. This avoids
incorrectly converting pdiv to hw twice and getting the wrong hw value.
Also set the input_rate in the freq cfg in _calc_dynamic_ramp_rate while
setting all the other fields.
In order to prevent regressions on earlier SoC generations, all of the
frequency tables need to be updated so that they contain the actual
divider values. If they contain hardware values these would be converted
to hardware values again, yielding the wrong value.
Signed-off-by: Rhyland Klein <rklein@nvidia.com>
[treding@nvidia.com: fix regressions on earlier SoC generations]
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/clk/tegra/clk-pll.c')
-rw-r--r-- | drivers/clk/tegra/clk-pll.c | 91 |
1 files changed, 50 insertions, 41 deletions
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index c645a899deba..b8b3fc6dc39b 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -435,6 +435,7 @@ static int _get_table_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table *sel; + int p; for (sel = pll->params->freq_table; sel->input_rate != 0; sel++) if (sel->input_rate == parent_rate && @@ -444,11 +445,19 @@ static int _get_table_rate(struct clk_hw *hw, if (sel->input_rate == 0) return -EINVAL; + if (pll->params->pdiv_tohw) { + p = _p_div_to_hw(hw, sel->p); + if (p < 0) + return p; + } else { + p = ilog2(sel->p); + } + 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->p = p; cfg->cpcon = sel->cpcon; cfg->sdm_data = sel->sdm_data; @@ -908,10 +917,6 @@ const struct clk_ops tegra_clk_plle_ops = { .enable = clk_plle_enable, }; -#if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ - defined(CONFIG_ARCH_TEGRA_124_SOC) || \ - defined(CONFIG_ARCH_TEGRA_132_SOC) - static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params, unsigned long parent_rate) { @@ -930,6 +935,39 @@ static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params, return 1; } +static int _calc_dynamic_ramp_rate(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned int p; + int p_div; + + if (!rate) + return -EINVAL; + + p = DIV_ROUND_UP(pll->params->vco_min, rate); + cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); + cfg->output_rate = rate * p; + cfg->n = cfg->output_rate * cfg->m / parent_rate; + cfg->input_rate = parent_rate; + + p_div = _p_div_to_hw(hw, p); + if (p_div < 0) + return p_div; + + cfg->p = p_div; + + if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) + return -EINVAL; + + return 0; +} + +#if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ + defined(CONFIG_ARCH_TEGRA_124_SOC) || \ + defined(CONFIG_ARCH_TEGRA_132_SOC) + u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); @@ -979,40 +1017,12 @@ static int _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params, return 0; } -static int _calc_dynamic_ramp_rate(struct clk_hw *hw, - struct tegra_clk_pll_freq_table *cfg, - unsigned long rate, unsigned long parent_rate) -{ - struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned int p; - int p_div; - - if (!rate) - return -EINVAL; - - p = DIV_ROUND_UP(pll->params->vco_min, rate); - cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); - cfg->output_rate = rate * p; - cfg->n = cfg->output_rate * cfg->m / parent_rate; - - p_div = _p_div_to_hw(hw, p); - if (p_div < 0) - return p_div; - - cfg->p = p_div; - - if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) - return -EINVAL; - - return 0; -} - static int _pll_ramp_calc_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - int err = 0, p_div; + int err = 0; err = _get_table_rate(hw, cfg, rate, parent_rate); if (err < 0) @@ -1023,11 +1033,6 @@ static int _pll_ramp_calc_pll(struct clk_hw *hw, err = -EINVAL; goto out; } - p_div = _p_div_to_hw(hw, cfg->p); - if (p_div < 0) - return p_div; - else - cfg->p = p_div; } if (cfg->p > pll->params->max_p) @@ -1512,8 +1517,12 @@ static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll, init.num_parents = (parent_name ? 1 : 0); /* Default to _calc_rate if unspecified */ - if (!pll->params->calc_rate) - pll->params->calc_rate = _calc_rate; + if (!pll->params->calc_rate) { + if (pll->params->flags & TEGRA_PLLM) + pll->params->calc_rate = _calc_dynamic_ramp_rate; + else + pll->params->calc_rate = _calc_rate; + } /* Data in .init is copied by clk_register(), so stack variable OK */ pll->hw.init = &init; |