diff options
author | Jaecheol Lee <jc.lee@samsung.com> | 2011-07-18 06:25:13 -0400 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2011-07-20 10:38:44 -0400 |
commit | 56c03d91f7f44991870f3f79b3067d40249b7bb9 (patch) | |
tree | d059f8066adfa44fbf1333843e8f225370e024a9 /arch/arm/mach-exynos4 | |
parent | f4ba4b01ef28100070608d915feda173d2a61c0c (diff) |
ARM: EXYNOS4: Add save/restore function for PLL
The PLL restore routine supports waiting pll locking. If PLL is
enabled in restoring sequence, it should wait until PLL is locked.
Signed-off-by: Jaecheol Lee <jc.lee@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'arch/arm/mach-exynos4')
-rw-r--r-- | arch/arm/mach-exynos4/include/mach/regs-clock.h | 9 | ||||
-rw-r--r-- | arch/arm/mach-exynos4/pm.c | 96 |
2 files changed, 101 insertions, 4 deletions
diff --git a/arch/arm/mach-exynos4/include/mach/regs-clock.h b/arch/arm/mach-exynos4/include/mach/regs-clock.h index 6e311c1157f5..64bdd240f88c 100644 --- a/arch/arm/mach-exynos4/include/mach/regs-clock.h +++ b/arch/arm/mach-exynos4/include/mach/regs-clock.h | |||
@@ -25,6 +25,9 @@ | |||
25 | #define S5P_CLKDIV_STAT_RIGHTBUS S5P_CLKREG(0x08600) | 25 | #define S5P_CLKDIV_STAT_RIGHTBUS S5P_CLKREG(0x08600) |
26 | #define S5P_CLKGATE_IP_RIGHTBUS S5P_CLKREG(0x08800) | 26 | #define S5P_CLKGATE_IP_RIGHTBUS S5P_CLKREG(0x08800) |
27 | 27 | ||
28 | #define S5P_EPLL_LOCK S5P_CLKREG(0x0C010) | ||
29 | #define S5P_VPLL_LOCK S5P_CLKREG(0x0C020) | ||
30 | |||
28 | #define S5P_EPLL_CON0 S5P_CLKREG(0x0C110) | 31 | #define S5P_EPLL_CON0 S5P_CLKREG(0x0C110) |
29 | #define S5P_EPLL_CON1 S5P_CLKREG(0x0C114) | 32 | #define S5P_EPLL_CON1 S5P_CLKREG(0x0C114) |
30 | #define S5P_VPLL_CON0 S5P_CLKREG(0x0C120) | 33 | #define S5P_VPLL_CON0 S5P_CLKREG(0x0C120) |
@@ -120,6 +123,12 @@ | |||
120 | #define S5P_APLL_VAL_1000 ((250 << 16) | (6 << 8) | 1) | 123 | #define S5P_APLL_VAL_1000 ((250 << 16) | (6 << 8) | 1) |
121 | #define S5P_APLL_VAL_800 ((200 << 16) | (6 << 8) | 1) | 124 | #define S5P_APLL_VAL_800 ((200 << 16) | (6 << 8) | 1) |
122 | 125 | ||
126 | #define S5P_EPLLCON0_ENABLE_SHIFT (31) | ||
127 | #define S5P_EPLLCON0_LOCKED_SHIFT (29) | ||
128 | |||
129 | #define S5P_VPLLCON0_ENABLE_SHIFT (31) | ||
130 | #define S5P_VPLLCON0_LOCKED_SHIFT (29) | ||
131 | |||
123 | #define S5P_CLKSRC_CPU_MUXCORE_SHIFT (16) | 132 | #define S5P_CLKSRC_CPU_MUXCORE_SHIFT (16) |
124 | #define S5P_CLKMUX_STATCPU_MUXCORE_MASK (0x7 << S5P_CLKSRC_CPU_MUXCORE_SHIFT) | 133 | #define S5P_CLKMUX_STATCPU_MUXCORE_MASK (0x7 << S5P_CLKSRC_CPU_MUXCORE_SHIFT) |
125 | 134 | ||
diff --git a/arch/arm/mach-exynos4/pm.c b/arch/arm/mach-exynos4/pm.c index e978e76beed7..3da1716afc5c 100644 --- a/arch/arm/mach-exynos4/pm.c +++ b/arch/arm/mach-exynos4/pm.c | |||
@@ -18,12 +18,15 @@ | |||
18 | #include <linux/suspend.h> | 18 | #include <linux/suspend.h> |
19 | #include <linux/syscore_ops.h> | 19 | #include <linux/syscore_ops.h> |
20 | #include <linux/io.h> | 20 | #include <linux/io.h> |
21 | #include <linux/err.h> | ||
22 | #include <linux/clk.h> | ||
21 | 23 | ||
22 | #include <asm/cacheflush.h> | 24 | #include <asm/cacheflush.h> |
23 | #include <asm/hardware/cache-l2x0.h> | 25 | #include <asm/hardware/cache-l2x0.h> |
24 | 26 | ||
25 | #include <plat/cpu.h> | 27 | #include <plat/cpu.h> |
26 | #include <plat/pm.h> | 28 | #include <plat/pm.h> |
29 | #include <plat/pll.h> | ||
27 | 30 | ||
28 | #include <mach/regs-irq.h> | 31 | #include <mach/regs-irq.h> |
29 | #include <mach/regs-gpio.h> | 32 | #include <mach/regs-gpio.h> |
@@ -45,16 +48,22 @@ static struct sleep_save exynos4_set_clksrc[] = { | |||
45 | { .reg = S5P_CLKSRC_MASK_DMC , .val = 0x00010000, }, | 48 | { .reg = S5P_CLKSRC_MASK_DMC , .val = 0x00010000, }, |
46 | }; | 49 | }; |
47 | 50 | ||
51 | static struct sleep_save exynos4_epll_save[] = { | ||
52 | SAVE_ITEM(S5P_EPLL_CON0), | ||
53 | SAVE_ITEM(S5P_EPLL_CON1), | ||
54 | }; | ||
55 | |||
56 | static struct sleep_save exynos4_vpll_save[] = { | ||
57 | SAVE_ITEM(S5P_VPLL_CON0), | ||
58 | SAVE_ITEM(S5P_VPLL_CON1), | ||
59 | }; | ||
60 | |||
48 | static struct sleep_save exynos4_core_save[] = { | 61 | static struct sleep_save exynos4_core_save[] = { |
49 | /* CMU side */ | 62 | /* CMU side */ |
50 | SAVE_ITEM(S5P_CLKDIV_LEFTBUS), | 63 | SAVE_ITEM(S5P_CLKDIV_LEFTBUS), |
51 | SAVE_ITEM(S5P_CLKGATE_IP_LEFTBUS), | 64 | SAVE_ITEM(S5P_CLKGATE_IP_LEFTBUS), |
52 | SAVE_ITEM(S5P_CLKDIV_RIGHTBUS), | 65 | SAVE_ITEM(S5P_CLKDIV_RIGHTBUS), |
53 | SAVE_ITEM(S5P_CLKGATE_IP_RIGHTBUS), | 66 | SAVE_ITEM(S5P_CLKGATE_IP_RIGHTBUS), |
54 | SAVE_ITEM(S5P_EPLL_CON0), | ||
55 | SAVE_ITEM(S5P_EPLL_CON1), | ||
56 | SAVE_ITEM(S5P_VPLL_CON0), | ||
57 | SAVE_ITEM(S5P_VPLL_CON1), | ||
58 | SAVE_ITEM(S5P_CLKSRC_TOP0), | 67 | SAVE_ITEM(S5P_CLKSRC_TOP0), |
59 | SAVE_ITEM(S5P_CLKSRC_TOP1), | 68 | SAVE_ITEM(S5P_CLKSRC_TOP1), |
60 | SAVE_ITEM(S5P_CLKSRC_CAM), | 69 | SAVE_ITEM(S5P_CLKSRC_CAM), |
@@ -227,6 +236,8 @@ static void exynos4_pm_prepare(void) | |||
227 | 236 | ||
228 | s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); | 237 | s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); |
229 | s3c_pm_do_save(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); | 238 | s3c_pm_do_save(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); |
239 | s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save)); | ||
240 | s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save)); | ||
230 | 241 | ||
231 | tmp = __raw_readl(S5P_INFORM1); | 242 | tmp = __raw_readl(S5P_INFORM1); |
232 | 243 | ||
@@ -274,12 +285,80 @@ void exynos4_scu_enable(void __iomem *scu_base) | |||
274 | flush_cache_all(); | 285 | flush_cache_all(); |
275 | } | 286 | } |
276 | 287 | ||
288 | static unsigned long pll_base_rate; | ||
289 | |||
290 | static void exynos4_restore_pll(void) | ||
291 | { | ||
292 | unsigned long pll_con, locktime, lockcnt; | ||
293 | unsigned long pll_in_rate; | ||
294 | unsigned int p_div, epll_wait = 0, vpll_wait = 0; | ||
295 | |||
296 | if (pll_base_rate == 0) | ||
297 | return; | ||
298 | |||
299 | pll_in_rate = pll_base_rate; | ||
300 | |||
301 | /* EPLL */ | ||
302 | pll_con = exynos4_epll_save[0].val; | ||
303 | |||
304 | if (pll_con & (1 << 31)) { | ||
305 | pll_con &= (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT); | ||
306 | p_div = (pll_con >> PLL46XX_PDIV_SHIFT); | ||
307 | |||
308 | pll_in_rate /= 1000000; | ||
309 | |||
310 | locktime = (3000 / pll_in_rate) * p_div; | ||
311 | lockcnt = locktime * 10000 / (10000 / pll_in_rate); | ||
312 | |||
313 | __raw_writel(lockcnt, S5P_EPLL_LOCK); | ||
314 | |||
315 | s3c_pm_do_restore_core(exynos4_epll_save, | ||
316 | ARRAY_SIZE(exynos4_epll_save)); | ||
317 | epll_wait = 1; | ||
318 | } | ||
319 | |||
320 | pll_in_rate = pll_base_rate; | ||
321 | |||
322 | /* VPLL */ | ||
323 | pll_con = exynos4_vpll_save[0].val; | ||
324 | |||
325 | if (pll_con & (1 << 31)) { | ||
326 | pll_in_rate /= 1000000; | ||
327 | /* 750us */ | ||
328 | locktime = 750; | ||
329 | lockcnt = locktime * 10000 / (10000 / pll_in_rate); | ||
330 | |||
331 | __raw_writel(lockcnt, S5P_VPLL_LOCK); | ||
332 | |||
333 | s3c_pm_do_restore_core(exynos4_vpll_save, | ||
334 | ARRAY_SIZE(exynos4_vpll_save)); | ||
335 | vpll_wait = 1; | ||
336 | } | ||
337 | |||
338 | /* Wait PLL locking */ | ||
339 | |||
340 | do { | ||
341 | if (epll_wait) { | ||
342 | pll_con = __raw_readl(S5P_EPLL_CON0); | ||
343 | if (pll_con & (1 << S5P_EPLLCON0_LOCKED_SHIFT)) | ||
344 | epll_wait = 0; | ||
345 | } | ||
346 | |||
347 | if (vpll_wait) { | ||
348 | pll_con = __raw_readl(S5P_VPLL_CON0); | ||
349 | if (pll_con & (1 << S5P_VPLLCON0_LOCKED_SHIFT)) | ||
350 | vpll_wait = 0; | ||
351 | } | ||
352 | } while (epll_wait || vpll_wait); | ||
353 | } | ||
354 | |||
277 | static struct sysdev_driver exynos4_pm_driver = { | 355 | static struct sysdev_driver exynos4_pm_driver = { |
278 | .add = exynos4_pm_add, | 356 | .add = exynos4_pm_add, |
279 | }; | 357 | }; |
280 | 358 | ||
281 | static __init int exynos4_pm_drvinit(void) | 359 | static __init int exynos4_pm_drvinit(void) |
282 | { | 360 | { |
361 | struct clk *pll_base; | ||
283 | unsigned int tmp; | 362 | unsigned int tmp; |
284 | 363 | ||
285 | s3c_pm_init(); | 364 | s3c_pm_init(); |
@@ -290,6 +369,13 @@ static __init int exynos4_pm_drvinit(void) | |||
290 | tmp |= ((0xFF << 8) | (0x1F << 1)); | 369 | tmp |= ((0xFF << 8) | (0x1F << 1)); |
291 | __raw_writel(tmp, S5P_WAKEUP_MASK); | 370 | __raw_writel(tmp, S5P_WAKEUP_MASK); |
292 | 371 | ||
372 | pll_base = clk_get(NULL, "xtal"); | ||
373 | |||
374 | if (!IS_ERR(pll_base)) { | ||
375 | pll_base_rate = clk_get_rate(pll_base); | ||
376 | clk_put(pll_base); | ||
377 | } | ||
378 | |||
293 | return sysdev_driver_register(&exynos4_sysclass, &exynos4_pm_driver); | 379 | return sysdev_driver_register(&exynos4_sysclass, &exynos4_pm_driver); |
294 | } | 380 | } |
295 | arch_initcall(exynos4_pm_drvinit); | 381 | arch_initcall(exynos4_pm_drvinit); |
@@ -358,6 +444,8 @@ static void exynos4_pm_resume(void) | |||
358 | 444 | ||
359 | s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); | 445 | s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); |
360 | 446 | ||
447 | exynos4_restore_pll(); | ||
448 | |||
361 | exynos4_scu_enable(S5P_VA_SCU); | 449 | exynos4_scu_enable(S5P_VA_SCU); |
362 | 450 | ||
363 | #ifdef CONFIG_CACHE_L2X0 | 451 | #ifdef CONFIG_CACHE_L2X0 |