aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/samsung/clk-pll.c
diff options
context:
space:
mode:
authorYadwinder Singh Brar <yadi.brar@samsung.com>2013-06-11 05:31:13 -0400
committerMike Turquette <mturquette@linaro.org>2013-08-02 16:22:10 -0400
commitdfa893190d6c3fe2a2fd92bb3b2fb7eab6442e1d (patch)
treedaa72f7e7434f05fcb1dee304a96ebb619a9d386 /drivers/clk/samsung/clk-pll.c
parent3ff6e0d8d64d594a551b5c4904e4b617bf7eee22 (diff)
clk: samsung: Add set_rate() clk_ops for PLL35xx
This patch add set_rate() and round_rate() for PLL35xx Reviewed-by: Doug Anderson <dianders@chromium.org> Reviewed-by: Tomasz Figa <t.figa@samsung.com> Signed-off-by: Yadwinder Singh Brar <yadi.brar@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.c105
1 files changed, 104 insertions, 1 deletions
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 83942318df13..03486e339b64 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -24,16 +24,51 @@ struct samsung_clk_pll {
24 24
25#define to_clk_pll(_hw) container_of(_hw, struct samsung_clk_pll, hw) 25#define to_clk_pll(_hw) container_of(_hw, struct samsung_clk_pll, hw)
26 26
27static const struct samsung_pll_rate_table *samsung_get_pll_settings(
28 struct samsung_clk_pll *pll, unsigned long rate)
29{
30 const struct samsung_pll_rate_table *rate_table = pll->rate_table;
31 int i;
32
33 for (i = 0; i < pll->rate_count; i++) {
34 if (rate == rate_table[i].rate)
35 return &rate_table[i];
36 }
37
38 return NULL;
39}
40
41static long samsung_pll_round_rate(struct clk_hw *hw,
42 unsigned long drate, unsigned long *prate)
43{
44 struct samsung_clk_pll *pll = to_clk_pll(hw);
45 const struct samsung_pll_rate_table *rate_table = pll->rate_table;
46 int i;
47
48 /* Assumming rate_table is in descending order */
49 for (i = 0; i < pll->rate_count; i++) {
50 if (drate >= rate_table[i].rate)
51 return rate_table[i].rate;
52 }
53
54 /* return minimum supported value */
55 return rate_table[i - 1].rate;
56}
57
27/* 58/*
28 * PLL35xx Clock Type 59 * PLL35xx Clock Type
29 */ 60 */
61/* Maximum lock time can be 270 * PDIV cycles */
62#define PLL35XX_LOCK_FACTOR (270)
30 63
31#define PLL35XX_MDIV_MASK (0x3FF) 64#define PLL35XX_MDIV_MASK (0x3FF)
32#define PLL35XX_PDIV_MASK (0x3F) 65#define PLL35XX_PDIV_MASK (0x3F)
33#define PLL35XX_SDIV_MASK (0x7) 66#define PLL35XX_SDIV_MASK (0x7)
67#define PLL35XX_LOCK_STAT_MASK (0x1)
34#define PLL35XX_MDIV_SHIFT (16) 68#define PLL35XX_MDIV_SHIFT (16)
35#define PLL35XX_PDIV_SHIFT (8) 69#define PLL35XX_PDIV_SHIFT (8)
36#define PLL35XX_SDIV_SHIFT (0) 70#define PLL35XX_SDIV_SHIFT (0)
71#define PLL35XX_LOCK_STAT_SHIFT (29)
37 72
38static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, 73static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
39 unsigned long parent_rate) 74 unsigned long parent_rate)
@@ -53,8 +88,73 @@ static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
53 return (unsigned long)fvco; 88 return (unsigned long)fvco;
54} 89}
55 90
91static inline bool samsung_pll35xx_mp_change(
92 const struct samsung_pll_rate_table *rate, u32 pll_con)
93{
94 u32 old_mdiv, old_pdiv;
95
96 old_mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
97 old_pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
98
99 return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv);
100}
101
102static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate,
103 unsigned long prate)
104{
105 struct samsung_clk_pll *pll = to_clk_pll(hw);
106 const struct samsung_pll_rate_table *rate;
107 u32 tmp;
108
109 /* Get required rate settings from table */
110 rate = samsung_get_pll_settings(pll, drate);
111 if (!rate) {
112 pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
113 drate, __clk_get_name(hw->clk));
114 return -EINVAL;
115 }
116
117 tmp = __raw_readl(pll->con_reg);
118
119 if (!(samsung_pll35xx_mp_change(rate, tmp))) {
120 /* If only s change, change just s value only*/
121 tmp &= ~(PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT);
122 tmp |= rate->sdiv << PLL35XX_SDIV_SHIFT;
123 __raw_writel(tmp, pll->con_reg);
124
125 return 0;
126 }
127
128 /* Set PLL lock time. */
129 __raw_writel(rate->pdiv * PLL35XX_LOCK_FACTOR,
130 pll->lock_reg);
131
132 /* Change PLL PMS values */
133 tmp &= ~((PLL35XX_MDIV_MASK << PLL35XX_MDIV_SHIFT) |
134 (PLL35XX_PDIV_MASK << PLL35XX_PDIV_SHIFT) |
135 (PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT));
136 tmp |= (rate->mdiv << PLL35XX_MDIV_SHIFT) |
137 (rate->pdiv << PLL35XX_PDIV_SHIFT) |
138 (rate->sdiv << PLL35XX_SDIV_SHIFT);
139 __raw_writel(tmp, pll->con_reg);
140
141 /* wait_lock_time */
142 do {
143 cpu_relax();
144 tmp = __raw_readl(pll->con_reg);
145 } while (!(tmp & (PLL35XX_LOCK_STAT_MASK
146 << PLL35XX_LOCK_STAT_SHIFT)));
147 return 0;
148}
149
56static const struct clk_ops samsung_pll35xx_clk_ops = { 150static const struct clk_ops samsung_pll35xx_clk_ops = {
57 .recalc_rate = samsung_pll35xx_recalc_rate, 151 .recalc_rate = samsung_pll35xx_recalc_rate,
152 .round_rate = samsung_pll_round_rate,
153 .set_rate = samsung_pll35xx_set_rate,
154};
155
156static const struct clk_ops samsung_pll35xx_clk_min_ops = {
157 .recalc_rate = samsung_pll35xx_recalc_rate,
58}; 158};
59 159
60/* 160/*
@@ -385,7 +485,10 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk,
385 /* clk_ops for 35xx and 2550 are similar */ 485 /* clk_ops for 35xx and 2550 are similar */
386 case pll_35xx: 486 case pll_35xx:
387 case pll_2550: 487 case pll_2550:
388 init.ops = &samsung_pll35xx_clk_ops; 488 if (!pll->rate_table)
489 init.ops = &samsung_pll35xx_clk_min_ops;
490 else
491 init.ops = &samsung_pll35xx_clk_ops;
389 break; 492 break;
390 /* clk_ops for 36xx and 2650 are similar */ 493 /* clk_ops for 36xx and 2650 are similar */
391 case pll_36xx: 494 case pll_36xx: