diff options
author | Tomasz Figa <t.figa@samsung.com> | 2013-08-26 13:09:05 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2013-09-06 16:33:39 -0400 |
commit | b4054ac6a26ee94a3a0f18e9ea585545d69e29c2 (patch) | |
tree | 890bf6d1777040b7457000c9f789e7298ffd7574 | |
parent | 52b0601637ac41845d216c490a75e489a14c4e95 (diff) |
clk: samsung: pll: Add support for rate configuration of PLL45xx
This patch implements round_rate and set_rate callbacks of PLL45xx
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>
-rw-r--r-- | drivers/clk/samsung/clk-pll.c | 110 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-pll.h | 10 |
2 files changed, 119 insertions, 1 deletions
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index f2e92e81173a..4acd06169ae3 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c | |||
@@ -10,9 +10,12 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/errno.h> | 12 | #include <linux/errno.h> |
13 | #include <linux/hrtimer.h> | ||
13 | #include "clk.h" | 14 | #include "clk.h" |
14 | #include "clk-pll.h" | 15 | #include "clk-pll.h" |
15 | 16 | ||
17 | #define PLL_TIMEOUT_MS 10 | ||
18 | |||
16 | struct samsung_clk_pll { | 19 | struct samsung_clk_pll { |
17 | struct clk_hw hw; | 20 | struct clk_hw hw; |
18 | void __iomem *lock_reg; | 21 | void __iomem *lock_reg; |
@@ -272,13 +275,20 @@ static const struct clk_ops samsung_pll36xx_clk_min_ops = { | |||
272 | /* | 275 | /* |
273 | * PLL45xx Clock Type | 276 | * PLL45xx Clock Type |
274 | */ | 277 | */ |
278 | #define PLL4502_LOCK_FACTOR 400 | ||
279 | #define PLL4508_LOCK_FACTOR 240 | ||
275 | 280 | ||
276 | #define PLL45XX_MDIV_MASK (0x3FF) | 281 | #define PLL45XX_MDIV_MASK (0x3FF) |
277 | #define PLL45XX_PDIV_MASK (0x3F) | 282 | #define PLL45XX_PDIV_MASK (0x3F) |
278 | #define PLL45XX_SDIV_MASK (0x7) | 283 | #define PLL45XX_SDIV_MASK (0x7) |
284 | #define PLL45XX_AFC_MASK (0x1F) | ||
279 | #define PLL45XX_MDIV_SHIFT (16) | 285 | #define PLL45XX_MDIV_SHIFT (16) |
280 | #define PLL45XX_PDIV_SHIFT (8) | 286 | #define PLL45XX_PDIV_SHIFT (8) |
281 | #define PLL45XX_SDIV_SHIFT (0) | 287 | #define PLL45XX_SDIV_SHIFT (0) |
288 | #define PLL45XX_AFC_SHIFT (0) | ||
289 | |||
290 | #define PLL45XX_ENABLE BIT(31) | ||
291 | #define PLL45XX_LOCKED BIT(29) | ||
282 | 292 | ||
283 | static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, | 293 | static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, |
284 | unsigned long parent_rate) | 294 | unsigned long parent_rate) |
@@ -301,8 +311,101 @@ static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw, | |||
301 | return (unsigned long)fvco; | 311 | return (unsigned long)fvco; |
302 | } | 312 | } |
303 | 313 | ||
314 | static bool samsung_pll45xx_mp_change(u32 pll_con0, u32 pll_con1, | ||
315 | const struct samsung_pll_rate_table *rate) | ||
316 | { | ||
317 | u32 old_mdiv, old_pdiv, old_afc; | ||
318 | |||
319 | old_mdiv = (pll_con0 >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK; | ||
320 | old_pdiv = (pll_con0 >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK; | ||
321 | old_afc = (pll_con1 >> PLL45XX_AFC_SHIFT) & PLL45XX_AFC_MASK; | ||
322 | |||
323 | return (old_mdiv != rate->mdiv || old_pdiv != rate->pdiv | ||
324 | || old_afc != rate->afc); | ||
325 | } | ||
326 | |||
327 | static int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long drate, | ||
328 | unsigned long prate) | ||
329 | { | ||
330 | struct samsung_clk_pll *pll = to_clk_pll(hw); | ||
331 | const struct samsung_pll_rate_table *rate; | ||
332 | u32 con0, con1; | ||
333 | ktime_t start; | ||
334 | |||
335 | /* Get required rate settings from table */ | ||
336 | rate = samsung_get_pll_settings(pll, drate); | ||
337 | if (!rate) { | ||
338 | pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, | ||
339 | drate, __clk_get_name(hw->clk)); | ||
340 | return -EINVAL; | ||
341 | } | ||
342 | |||
343 | con0 = __raw_readl(pll->con_reg); | ||
344 | con1 = __raw_readl(pll->con_reg + 0x4); | ||
345 | |||
346 | if (!(samsung_pll45xx_mp_change(con0, con1, rate))) { | ||
347 | /* If only s change, change just s value only*/ | ||
348 | con0 &= ~(PLL45XX_SDIV_MASK << PLL45XX_SDIV_SHIFT); | ||
349 | con0 |= rate->sdiv << PLL45XX_SDIV_SHIFT; | ||
350 | __raw_writel(con0, pll->con_reg); | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | /* Set PLL PMS values. */ | ||
356 | con0 &= ~((PLL45XX_MDIV_MASK << PLL45XX_MDIV_SHIFT) | | ||
357 | (PLL45XX_PDIV_MASK << PLL45XX_PDIV_SHIFT) | | ||
358 | (PLL45XX_SDIV_MASK << PLL45XX_SDIV_SHIFT)); | ||
359 | con0 |= (rate->mdiv << PLL45XX_MDIV_SHIFT) | | ||
360 | (rate->pdiv << PLL45XX_PDIV_SHIFT) | | ||
361 | (rate->sdiv << PLL45XX_SDIV_SHIFT); | ||
362 | |||
363 | /* Set PLL AFC value. */ | ||
364 | con1 = __raw_readl(pll->con_reg + 0x4); | ||
365 | con1 &= ~(PLL45XX_AFC_MASK << PLL45XX_AFC_SHIFT); | ||
366 | con1 |= (rate->afc << PLL45XX_AFC_SHIFT); | ||
367 | |||
368 | /* Set PLL lock time. */ | ||
369 | switch (pll->type) { | ||
370 | case pll_4502: | ||
371 | __raw_writel(rate->pdiv * PLL4502_LOCK_FACTOR, pll->lock_reg); | ||
372 | break; | ||
373 | case pll_4508: | ||
374 | __raw_writel(rate->pdiv * PLL4508_LOCK_FACTOR, pll->lock_reg); | ||
375 | break; | ||
376 | default: | ||
377 | break; | ||
378 | }; | ||
379 | |||
380 | /* Set new configuration. */ | ||
381 | __raw_writel(con1, pll->con_reg + 0x4); | ||
382 | __raw_writel(con0, pll->con_reg); | ||
383 | |||
384 | /* Wait for locking. */ | ||
385 | start = ktime_get(); | ||
386 | while (!(__raw_readl(pll->con_reg) & PLL45XX_LOCKED)) { | ||
387 | ktime_t delta = ktime_sub(ktime_get(), start); | ||
388 | |||
389 | if (ktime_to_ms(delta) > PLL_TIMEOUT_MS) { | ||
390 | pr_err("%s: could not lock PLL %s\n", | ||
391 | __func__, __clk_get_name(hw->clk)); | ||
392 | return -EFAULT; | ||
393 | } | ||
394 | |||
395 | cpu_relax(); | ||
396 | } | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
304 | static const struct clk_ops samsung_pll45xx_clk_ops = { | 401 | static const struct clk_ops samsung_pll45xx_clk_ops = { |
305 | .recalc_rate = samsung_pll45xx_recalc_rate, | 402 | .recalc_rate = samsung_pll45xx_recalc_rate, |
403 | .round_rate = samsung_pll_round_rate, | ||
404 | .set_rate = samsung_pll45xx_set_rate, | ||
405 | }; | ||
406 | |||
407 | static const struct clk_ops samsung_pll45xx_clk_min_ops = { | ||
408 | .recalc_rate = samsung_pll45xx_recalc_rate, | ||
306 | }; | 409 | }; |
307 | 410 | ||
308 | /* | 411 | /* |
@@ -591,9 +694,14 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, | |||
591 | init.ops = &samsung_pll35xx_clk_ops; | 694 | init.ops = &samsung_pll35xx_clk_ops; |
592 | break; | 695 | break; |
593 | case pll_4500: | 696 | case pll_4500: |
697 | init.ops = &samsung_pll45xx_clk_min_ops; | ||
698 | break; | ||
594 | case pll_4502: | 699 | case pll_4502: |
595 | case pll_4508: | 700 | case pll_4508: |
596 | init.ops = &samsung_pll45xx_clk_ops; | 701 | if (!pll->rate_table) |
702 | init.ops = &samsung_pll45xx_clk_min_ops; | ||
703 | else | ||
704 | init.ops = &samsung_pll45xx_clk_ops; | ||
597 | break; | 705 | break; |
598 | /* clk_ops for 36xx and 2650 are similar */ | 706 | /* clk_ops for 36xx and 2650 are similar */ |
599 | case pll_36xx: | 707 | case pll_36xx: |
diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index fceb2cb00f2a..2c331291fe40 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h | |||
@@ -41,6 +41,15 @@ enum samsung_pll_type { | |||
41 | .kdiv = (_k), \ | 41 | .kdiv = (_k), \ |
42 | } | 42 | } |
43 | 43 | ||
44 | #define PLL_45XX_RATE(_rate, _m, _p, _s, _afc) \ | ||
45 | { \ | ||
46 | .rate = (_rate), \ | ||
47 | .mdiv = (_m), \ | ||
48 | .pdiv = (_p), \ | ||
49 | .sdiv = (_s), \ | ||
50 | .afc = (_afc), \ | ||
51 | } | ||
52 | |||
44 | /* NOTE: Rate table should be kept sorted in descending order. */ | 53 | /* NOTE: Rate table should be kept sorted in descending order. */ |
45 | 54 | ||
46 | struct samsung_pll_rate_table { | 55 | struct samsung_pll_rate_table { |
@@ -49,6 +58,7 @@ struct samsung_pll_rate_table { | |||
49 | unsigned int mdiv; | 58 | unsigned int mdiv; |
50 | unsigned int sdiv; | 59 | unsigned int sdiv; |
51 | unsigned int kdiv; | 60 | unsigned int kdiv; |
61 | unsigned int afc; | ||
52 | }; | 62 | }; |
53 | 63 | ||
54 | enum pll46xx_type { | 64 | enum pll46xx_type { |