diff options
author | Tomasz Figa <t.figa@samsung.com> | 2013-08-26 13:09:07 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2013-09-06 16:33:47 -0400 |
commit | 5c89658a2ef38bace96cf9d4474d59a32d06609d (patch) | |
tree | 59083e99d135a101c58599dc3ea4b2334eef7524 /drivers/clk/samsung/clk-pll.c | |
parent | c50d11f35a021fc357922797a7638d4d6ca70b9e (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.c | 111 |
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 | ||
426 | static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw, | 438 | static 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 | ||
461 | static 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 | |||
474 | static 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 | |||
449 | static const struct clk_ops samsung_pll46xx_clk_ops = { | 549 | static 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 | |||
555 | static 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", |