aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/samsung/clk-pll.c
diff options
context:
space:
mode:
authorTomasz Figa <t.figa@samsung.com>2013-08-26 13:09:07 -0400
committerMike Turquette <mturquette@linaro.org>2013-09-06 16:33:47 -0400
commit5c89658a2ef38bace96cf9d4474d59a32d06609d (patch)
tree59083e99d135a101c58599dc3ea4b2334eef7524 /drivers/clk/samsung/clk-pll.c
parentc50d11f35a021fc357922797a7638d4d6ca70b9e (diff)
clk: samsung: pll: Add support for rate configuration of PLL46xx
This patch implements round_rate and set_rate callbacks of PLL46xx driver to allow reconfiguration of PLL at runtime. Signed-off-by: Tomasz Figa <t.figa@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk/samsung/clk-pll.c')
-rw-r--r--drivers/clk/samsung/clk-pll.c111
1 files changed, 110 insertions, 1 deletions
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index d963b3e88615..529e11dc2c6b 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -411,10 +411,13 @@ static const struct clk_ops samsung_pll45xx_clk_min_ops = {
411/* 411/*
412 * PLL46xx Clock Type 412 * PLL46xx Clock Type
413 */ 413 */
414#define PLL46XX_LOCK_FACTOR 3000
414 415
416#define PLL46XX_VSEL_MASK (1)
415#define PLL46XX_MDIV_MASK (0x1FF) 417#define PLL46XX_MDIV_MASK (0x1FF)
416#define PLL46XX_PDIV_MASK (0x3F) 418#define PLL46XX_PDIV_MASK (0x3F)
417#define PLL46XX_SDIV_MASK (0x7) 419#define PLL46XX_SDIV_MASK (0x7)
420#define PLL46XX_VSEL_SHIFT (27)
418#define PLL46XX_MDIV_SHIFT (16) 421#define PLL46XX_MDIV_SHIFT (16)
419#define PLL46XX_PDIV_SHIFT (8) 422#define PLL46XX_PDIV_SHIFT (8)
420#define PLL46XX_SDIV_SHIFT (0) 423#define PLL46XX_SDIV_SHIFT (0)
@@ -422,6 +425,15 @@ static const struct clk_ops samsung_pll45xx_clk_min_ops = {
422#define PLL46XX_KDIV_MASK (0xFFFF) 425#define PLL46XX_KDIV_MASK (0xFFFF)
423#define PLL4650C_KDIV_MASK (0xFFF) 426#define PLL4650C_KDIV_MASK (0xFFF)
424#define PLL46XX_KDIV_SHIFT (0) 427#define PLL46XX_KDIV_SHIFT (0)
428#define PLL46XX_MFR_MASK (0x3F)
429#define PLL46XX_MRR_MASK (0x1F)
430#define PLL46XX_KDIV_SHIFT (0)
431#define PLL46XX_MFR_SHIFT (16)
432#define PLL46XX_MRR_SHIFT (24)
433
434#define PLL46XX_ENABLE BIT(31)
435#define PLL46XX_LOCKED BIT(29)
436#define PLL46XX_VSEL BIT(27)
425 437
426static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, 438static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
427 unsigned long parent_rate) 439 unsigned long parent_rate)
@@ -446,8 +458,102 @@ static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
446 return (unsigned long)fvco; 458 return (unsigned long)fvco;
447} 459}
448 460
461static bool samsung_pll46xx_mpk_change(u32 pll_con0, u32 pll_con1,
462 const struct samsung_pll_rate_table *rate)
463{
464 u32 old_mdiv, old_pdiv, old_kdiv;
465
466 old_mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
467 old_pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
468 old_kdiv = (pll_con1 >> PLL46XX_KDIV_SHIFT) & PLL46XX_KDIV_MASK;
469
470 return (old_mdiv != rate->mdiv || old_pdiv != rate->pdiv
471 || old_kdiv != rate->kdiv);
472}
473
474static int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate,
475 unsigned long prate)
476{
477 struct samsung_clk_pll *pll = to_clk_pll(hw);
478 const struct samsung_pll_rate_table *rate;
479 u32 con0, con1, lock;
480 ktime_t start;
481
482 /* Get required rate settings from table */
483 rate = samsung_get_pll_settings(pll, drate);
484 if (!rate) {
485 pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
486 drate, __clk_get_name(hw->clk));
487 return -EINVAL;
488 }
489
490 con0 = __raw_readl(pll->con_reg);
491 con1 = __raw_readl(pll->con_reg + 0x4);
492
493 if (!(samsung_pll46xx_mpk_change(con0, con1, rate))) {
494 /* If only s change, change just s value only*/
495 con0 &= ~(PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT);
496 con0 |= rate->sdiv << PLL46XX_SDIV_SHIFT;
497 __raw_writel(con0, pll->con_reg);
498
499 return 0;
500 }
501
502 /* Set PLL lock time. */
503 lock = rate->pdiv * PLL46XX_LOCK_FACTOR;
504 if (lock > 0xffff)
505 /* Maximum lock time bitfield is 16-bit. */
506 lock = 0xffff;
507
508 /* Set PLL PMS and VSEL values. */
509 con0 &= ~((PLL46XX_MDIV_MASK << PLL46XX_MDIV_SHIFT) |
510 (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT) |
511 (PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT) |
512 (PLL46XX_VSEL_MASK << PLL46XX_VSEL_SHIFT));
513 con0 |= (rate->mdiv << PLL46XX_MDIV_SHIFT) |
514 (rate->pdiv << PLL46XX_PDIV_SHIFT) |
515 (rate->sdiv << PLL46XX_SDIV_SHIFT) |
516 (rate->vsel << PLL46XX_VSEL_SHIFT);
517
518 /* Set PLL K, MFR and MRR values. */
519 con1 = __raw_readl(pll->con_reg + 0x4);
520 con1 &= ~((PLL46XX_KDIV_MASK << PLL46XX_KDIV_SHIFT) |
521 (PLL46XX_MFR_MASK << PLL46XX_MFR_SHIFT) |
522 (PLL46XX_MRR_MASK << PLL46XX_MRR_SHIFT));
523 con1 |= (rate->kdiv << PLL46XX_KDIV_SHIFT) |
524 (rate->mfr << PLL46XX_MFR_SHIFT) |
525 (rate->mrr << PLL46XX_MRR_SHIFT);
526
527 /* Write configuration to PLL */
528 __raw_writel(lock, pll->lock_reg);
529 __raw_writel(con0, pll->con_reg);
530 __raw_writel(con1, pll->con_reg + 0x4);
531
532 /* Wait for locking. */
533 start = ktime_get();
534 while (!(__raw_readl(pll->con_reg) & PLL46XX_LOCKED)) {
535 ktime_t delta = ktime_sub(ktime_get(), start);
536
537 if (ktime_to_ms(delta) > PLL_TIMEOUT_MS) {
538 pr_err("%s: could not lock PLL %s\n",
539 __func__, __clk_get_name(hw->clk));
540 return -EFAULT;
541 }
542
543 cpu_relax();
544 }
545
546 return 0;
547}
548
449static const struct clk_ops samsung_pll46xx_clk_ops = { 549static const struct clk_ops samsung_pll46xx_clk_ops = {
450 .recalc_rate = samsung_pll46xx_recalc_rate, 550 .recalc_rate = samsung_pll46xx_recalc_rate,
551 .round_rate = samsung_pll_round_rate,
552 .set_rate = samsung_pll46xx_set_rate,
553};
554
555static const struct clk_ops samsung_pll46xx_clk_min_ops = {
556 .recalc_rate = samsung_pll46xx_recalc_rate,
451}; 557};
452 558
453/* 559/*
@@ -675,7 +781,10 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk,
675 case pll_4600: 781 case pll_4600:
676 case pll_4650: 782 case pll_4650:
677 case pll_4650c: 783 case pll_4650c:
678 init.ops = &samsung_pll46xx_clk_ops; 784 if (!pll->rate_table)
785 init.ops = &samsung_pll46xx_clk_min_ops;
786 else
787 init.ops = &samsung_pll46xx_clk_ops;
679 break; 788 break;
680 default: 789 default:
681 pr_warn("%s: Unknown pll type for pll clk %s\n", 790 pr_warn("%s: Unknown pll type for pll clk %s\n",