diff options
author | Peter De Schrijver <pdeschrijver@nvidia.com> | 2013-04-03 10:40:39 -0400 |
---|---|---|
committer | Stephen Warren <swarren@nvidia.com> | 2013-04-04 18:10:45 -0400 |
commit | 0b6525acd13f2d33cd3be86d0dbd2ddd1ffeda8f (patch) | |
tree | 989c920b532d4d5d7372c275ed828356cff9c581 | |
parent | 7ba28813b41120dd67329fd04dc732ea7fef05a0 (diff) |
clk: tegra: Add PLL post divider table
Some PLLs in Tegra114 don't use a power of 2 mapping for the post divider.
Introduce a table based approach and switch PLLU to it.
Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Acked-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
-rw-r--r-- | drivers/clk/tegra/clk-pll.c | 38 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra20.c | 7 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra30.c | 7 | ||||
-rw-r--r-- | drivers/clk/tegra/clk.h | 13 |
4 files changed, 59 insertions, 6 deletions
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index eaab060cda24..ccb367ee7e78 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c | |||
@@ -266,6 +266,7 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, | |||
266 | unsigned long rate, unsigned long parent_rate) | 266 | unsigned long rate, unsigned long parent_rate) |
267 | { | 267 | { |
268 | struct tegra_clk_pll *pll = to_clk_pll(hw); | 268 | struct tegra_clk_pll *pll = to_clk_pll(hw); |
269 | struct pdiv_map *p_tohw = pll->params->pdiv_tohw; | ||
269 | unsigned long cfreq; | 270 | unsigned long cfreq; |
270 | u32 p_div = 0; | 271 | u32 p_div = 0; |
271 | 272 | ||
@@ -299,7 +300,6 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, | |||
299 | cfg->output_rate <<= 1) | 300 | cfg->output_rate <<= 1) |
300 | p_div++; | 301 | p_div++; |
301 | 302 | ||
302 | cfg->p = p_div; | ||
303 | cfg->m = parent_rate / cfreq; | 303 | cfg->m = parent_rate / cfreq; |
304 | cfg->n = cfg->output_rate / cfreq; | 304 | cfg->n = cfg->output_rate / cfreq; |
305 | cfg->cpcon = OUT_OF_TABLE_CPCON; | 305 | cfg->cpcon = OUT_OF_TABLE_CPCON; |
@@ -312,8 +312,19 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, | |||
312 | return -EINVAL; | 312 | return -EINVAL; |
313 | } | 313 | } |
314 | 314 | ||
315 | if (pll->flags & TEGRA_PLLU) | 315 | if (p_tohw) { |
316 | cfg->p ^= 1; | 316 | p_div = 1 << p_div; |
317 | while (p_tohw->pdiv) { | ||
318 | if (p_div <= p_tohw->pdiv) { | ||
319 | cfg->p = p_tohw->hw_val; | ||
320 | break; | ||
321 | } | ||
322 | p_tohw++; | ||
323 | } | ||
324 | if (!p_tohw->pdiv) | ||
325 | return -EINVAL; | ||
326 | } else | ||
327 | cfg->p = p_div; | ||
317 | 328 | ||
318 | return 0; | 329 | return 0; |
319 | } | 330 | } |
@@ -460,8 +471,10 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, | |||
460 | { | 471 | { |
461 | struct tegra_clk_pll *pll = to_clk_pll(hw); | 472 | struct tegra_clk_pll *pll = to_clk_pll(hw); |
462 | struct tegra_clk_pll_freq_table cfg; | 473 | struct tegra_clk_pll_freq_table cfg; |
474 | struct pdiv_map *p_tohw = pll->params->pdiv_tohw; | ||
463 | u32 val; | 475 | u32 val; |
464 | u64 rate = parent_rate; | 476 | u64 rate = parent_rate; |
477 | int pdiv; | ||
465 | 478 | ||
466 | val = pll_readl_base(pll); | 479 | val = pll_readl_base(pll); |
467 | 480 | ||
@@ -480,10 +493,23 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, | |||
480 | 493 | ||
481 | _get_pll_mnp(pll, &cfg); | 494 | _get_pll_mnp(pll, &cfg); |
482 | 495 | ||
483 | if (pll->flags & TEGRA_PLLU) | 496 | if (p_tohw) { |
484 | cfg.p ^= 1; | 497 | while (p_tohw->pdiv) { |
498 | if (cfg.p == p_tohw->hw_val) { | ||
499 | pdiv = p_tohw->pdiv; | ||
500 | break; | ||
501 | } | ||
502 | p_tohw++; | ||
503 | } | ||
504 | |||
505 | if (!p_tohw->pdiv) { | ||
506 | WARN_ON(1); | ||
507 | pdiv = 1; | ||
508 | } | ||
509 | } else | ||
510 | pdiv = 1 << cfg.p; | ||
485 | 511 | ||
486 | cfg.m *= 1 << cfg.p; | 512 | cfg.m *= pdiv; |
487 | 513 | ||
488 | rate *= cfg.n; | 514 | rate *= cfg.n; |
489 | do_div(rate, cfg.m); | 515 | do_div(rate, cfg.m); |
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index c2a1c4cae47c..f215bf10c9ff 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c | |||
@@ -441,6 +441,12 @@ static struct tegra_clk_pll_params pll_d_params = { | |||
441 | .lock_delay = 1000, | 441 | .lock_delay = 1000, |
442 | }; | 442 | }; |
443 | 443 | ||
444 | static struct pdiv_map pllu_p[] = { | ||
445 | { .pdiv = 1, .hw_val = 1 }, | ||
446 | { .pdiv = 2, .hw_val = 0 }, | ||
447 | { .pdiv = 0, .hw_val = 0 }, | ||
448 | }; | ||
449 | |||
444 | static struct tegra_clk_pll_params pll_u_params = { | 450 | static struct tegra_clk_pll_params pll_u_params = { |
445 | .input_min = 2000000, | 451 | .input_min = 2000000, |
446 | .input_max = 40000000, | 452 | .input_max = 40000000, |
@@ -453,6 +459,7 @@ static struct tegra_clk_pll_params pll_u_params = { | |||
453 | .lock_bit_idx = PLL_BASE_LOCK, | 459 | .lock_bit_idx = PLL_BASE_LOCK, |
454 | .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, | 460 | .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, |
455 | .lock_delay = 1000, | 461 | .lock_delay = 1000, |
462 | .pdiv_tohw = pllu_p, | ||
456 | }; | 463 | }; |
457 | 464 | ||
458 | static struct tegra_clk_pll_params pll_x_params = { | 465 | static struct tegra_clk_pll_params pll_x_params = { |
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 02609d125e0e..fe768fe769b2 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c | |||
@@ -467,6 +467,12 @@ static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { | |||
467 | { 0, 0, 0, 0, 0, 0 }, | 467 | { 0, 0, 0, 0, 0, 0 }, |
468 | }; | 468 | }; |
469 | 469 | ||
470 | static struct pdiv_map pllu_p[] = { | ||
471 | { .pdiv = 1, .hw_val = 1 }, | ||
472 | { .pdiv = 2, .hw_val = 0 }, | ||
473 | { .pdiv = 0, .hw_val = 0 }, | ||
474 | }; | ||
475 | |||
470 | static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { | 476 | static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { |
471 | { 12000000, 480000000, 960, 12, 0, 12}, | 477 | { 12000000, 480000000, 960, 12, 0, 12}, |
472 | { 13000000, 480000000, 960, 13, 0, 12}, | 478 | { 13000000, 480000000, 960, 13, 0, 12}, |
@@ -640,6 +646,7 @@ static struct tegra_clk_pll_params pll_u_params = { | |||
640 | .lock_bit_idx = PLL_BASE_LOCK, | 646 | .lock_bit_idx = PLL_BASE_LOCK, |
641 | .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, | 647 | .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, |
642 | .lock_delay = 1000, | 648 | .lock_delay = 1000, |
649 | .pdiv_tohw = pllu_p, | ||
643 | }; | 650 | }; |
644 | 651 | ||
645 | static struct tegra_clk_pll_params pll_x_params = { | 652 | static struct tegra_clk_pll_params pll_x_params = { |
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 17ddb22f7a50..925da451bd19 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h | |||
@@ -117,6 +117,17 @@ struct tegra_clk_pll_freq_table { | |||
117 | }; | 117 | }; |
118 | 118 | ||
119 | /** | 119 | /** |
120 | * struct pdiv_map - map post divider to hw value | ||
121 | * | ||
122 | * @pdiv: post divider | ||
123 | * @hw_val: value to be written to the PLL hw | ||
124 | */ | ||
125 | struct pdiv_map { | ||
126 | u8 pdiv; | ||
127 | u8 hw_val; | ||
128 | }; | ||
129 | |||
130 | /** | ||
120 | * struct clk_pll_params - PLL parameters | 131 | * struct clk_pll_params - PLL parameters |
121 | * | 132 | * |
122 | * @input_min: Minimum input frequency | 133 | * @input_min: Minimum input frequency |
@@ -146,6 +157,8 @@ struct tegra_clk_pll_params { | |||
146 | u32 lock_bit_idx; | 157 | u32 lock_bit_idx; |
147 | u32 lock_enable_bit_idx; | 158 | u32 lock_enable_bit_idx; |
148 | int lock_delay; | 159 | int lock_delay; |
160 | int max_p; | ||
161 | struct pdiv_map *pdiv_tohw; | ||
149 | }; | 162 | }; |
150 | 163 | ||
151 | /** | 164 | /** |