diff options
Diffstat (limited to 'arch/arm/mach-exynos4/pm.c')
-rw-r--r-- | arch/arm/mach-exynos4/pm.c | 273 |
1 files changed, 165 insertions, 108 deletions
diff --git a/arch/arm/mach-exynos4/pm.c b/arch/arm/mach-exynos4/pm.c index 533c28f758ca..bc6ca9482de1 100644 --- a/arch/arm/mach-exynos4/pm.c +++ b/arch/arm/mach-exynos4/pm.c | |||
@@ -18,92 +18,23 @@ | |||
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> | ||
30 | #include <plat/regs-srom.h> | ||
27 | 31 | ||
28 | #include <mach/regs-irq.h> | 32 | #include <mach/regs-irq.h> |
29 | #include <mach/regs-gpio.h> | 33 | #include <mach/regs-gpio.h> |
30 | #include <mach/regs-clock.h> | 34 | #include <mach/regs-clock.h> |
31 | #include <mach/regs-pmu.h> | 35 | #include <mach/regs-pmu.h> |
32 | #include <mach/pm-core.h> | 36 | #include <mach/pm-core.h> |
33 | 37 | #include <mach/pmu.h> | |
34 | static struct sleep_save exynos4_sleep[] = { | ||
35 | { .reg = S5P_ARM_CORE0_LOWPWR , .val = 0x2, }, | ||
36 | { .reg = S5P_DIS_IRQ_CORE0 , .val = 0x0, }, | ||
37 | { .reg = S5P_DIS_IRQ_CENTRAL0 , .val = 0x0, }, | ||
38 | { .reg = S5P_ARM_CORE1_LOWPWR , .val = 0x2, }, | ||
39 | { .reg = S5P_DIS_IRQ_CORE1 , .val = 0x0, }, | ||
40 | { .reg = S5P_DIS_IRQ_CENTRAL1 , .val = 0x0, }, | ||
41 | { .reg = S5P_ARM_COMMON_LOWPWR , .val = 0x2, }, | ||
42 | { .reg = S5P_L2_0_LOWPWR , .val = 0x3, }, | ||
43 | { .reg = S5P_L2_1_LOWPWR , .val = 0x3, }, | ||
44 | { .reg = S5P_CMU_ACLKSTOP_LOWPWR , .val = 0x0, }, | ||
45 | { .reg = S5P_CMU_SCLKSTOP_LOWPWR , .val = 0x0, }, | ||
46 | { .reg = S5P_CMU_RESET_LOWPWR , .val = 0x0, }, | ||
47 | { .reg = S5P_APLL_SYSCLK_LOWPWR , .val = 0x0, }, | ||
48 | { .reg = S5P_MPLL_SYSCLK_LOWPWR , .val = 0x0, }, | ||
49 | { .reg = S5P_VPLL_SYSCLK_LOWPWR , .val = 0x0, }, | ||
50 | { .reg = S5P_EPLL_SYSCLK_LOWPWR , .val = 0x0, }, | ||
51 | { .reg = S5P_CMU_CLKSTOP_GPS_ALIVE_LOWPWR , .val = 0x0, }, | ||
52 | { .reg = S5P_CMU_RESET_GPSALIVE_LOWPWR , .val = 0x0, }, | ||
53 | { .reg = S5P_CMU_CLKSTOP_CAM_LOWPWR , .val = 0x0, }, | ||
54 | { .reg = S5P_CMU_CLKSTOP_TV_LOWPWR , .val = 0x0, }, | ||
55 | { .reg = S5P_CMU_CLKSTOP_MFC_LOWPWR , .val = 0x0, }, | ||
56 | { .reg = S5P_CMU_CLKSTOP_G3D_LOWPWR , .val = 0x0, }, | ||
57 | { .reg = S5P_CMU_CLKSTOP_LCD0_LOWPWR , .val = 0x0, }, | ||
58 | { .reg = S5P_CMU_CLKSTOP_LCD1_LOWPWR , .val = 0x0, }, | ||
59 | { .reg = S5P_CMU_CLKSTOP_MAUDIO_LOWPWR , .val = 0x0, }, | ||
60 | { .reg = S5P_CMU_CLKSTOP_GPS_LOWPWR , .val = 0x0, }, | ||
61 | { .reg = S5P_CMU_RESET_CAM_LOWPWR , .val = 0x0, }, | ||
62 | { .reg = S5P_CMU_RESET_TV_LOWPWR , .val = 0x0, }, | ||
63 | { .reg = S5P_CMU_RESET_MFC_LOWPWR , .val = 0x0, }, | ||
64 | { .reg = S5P_CMU_RESET_G3D_LOWPWR , .val = 0x0, }, | ||
65 | { .reg = S5P_CMU_RESET_LCD0_LOWPWR , .val = 0x0, }, | ||
66 | { .reg = S5P_CMU_RESET_LCD1_LOWPWR , .val = 0x0, }, | ||
67 | { .reg = S5P_CMU_RESET_MAUDIO_LOWPWR , .val = 0x0, }, | ||
68 | { .reg = S5P_CMU_RESET_GPS_LOWPWR , .val = 0x0, }, | ||
69 | { .reg = S5P_TOP_BUS_LOWPWR , .val = 0x0, }, | ||
70 | { .reg = S5P_TOP_RETENTION_LOWPWR , .val = 0x1, }, | ||
71 | { .reg = S5P_TOP_PWR_LOWPWR , .val = 0x3, }, | ||
72 | { .reg = S5P_LOGIC_RESET_LOWPWR , .val = 0x0, }, | ||
73 | { .reg = S5P_ONENAND_MEM_LOWPWR , .val = 0x0, }, | ||
74 | { .reg = S5P_MODIMIF_MEM_LOWPWR , .val = 0x0, }, | ||
75 | { .reg = S5P_G2D_ACP_MEM_LOWPWR , .val = 0x0, }, | ||
76 | { .reg = S5P_USBOTG_MEM_LOWPWR , .val = 0x0, }, | ||
77 | { .reg = S5P_HSMMC_MEM_LOWPWR , .val = 0x0, }, | ||
78 | { .reg = S5P_CSSYS_MEM_LOWPWR , .val = 0x0, }, | ||
79 | { .reg = S5P_SECSS_MEM_LOWPWR , .val = 0x0, }, | ||
80 | { .reg = S5P_PCIE_MEM_LOWPWR , .val = 0x0, }, | ||
81 | { .reg = S5P_SATA_MEM_LOWPWR , .val = 0x0, }, | ||
82 | { .reg = S5P_PAD_RETENTION_DRAM_LOWPWR , .val = 0x0, }, | ||
83 | { .reg = S5P_PAD_RETENTION_MAUDIO_LOWPWR , .val = 0x0, }, | ||
84 | { .reg = S5P_PAD_RETENTION_GPIO_LOWPWR , .val = 0x0, }, | ||
85 | { .reg = S5P_PAD_RETENTION_UART_LOWPWR , .val = 0x0, }, | ||
86 | { .reg = S5P_PAD_RETENTION_MMCA_LOWPWR , .val = 0x0, }, | ||
87 | { .reg = S5P_PAD_RETENTION_MMCB_LOWPWR , .val = 0x0, }, | ||
88 | { .reg = S5P_PAD_RETENTION_EBIA_LOWPWR , .val = 0x0, }, | ||
89 | { .reg = S5P_PAD_RETENTION_EBIB_LOWPWR , .val = 0x0, }, | ||
90 | { .reg = S5P_PAD_RETENTION_ISOLATION_LOWPWR , .val = 0x0, }, | ||
91 | { .reg = S5P_PAD_RETENTION_ALV_SEL_LOWPWR , .val = 0x0, }, | ||
92 | { .reg = S5P_XUSBXTI_LOWPWR , .val = 0x0, }, | ||
93 | { .reg = S5P_XXTI_LOWPWR , .val = 0x0, }, | ||
94 | { .reg = S5P_EXT_REGULATOR_LOWPWR , .val = 0x0, }, | ||
95 | { .reg = S5P_GPIO_MODE_LOWPWR , .val = 0x0, }, | ||
96 | { .reg = S5P_GPIO_MODE_MAUDIO_LOWPWR , .val = 0x0, }, | ||
97 | { .reg = S5P_CAM_LOWPWR , .val = 0x0, }, | ||
98 | { .reg = S5P_TV_LOWPWR , .val = 0x0, }, | ||
99 | { .reg = S5P_MFC_LOWPWR , .val = 0x0, }, | ||
100 | { .reg = S5P_G3D_LOWPWR , .val = 0x0, }, | ||
101 | { .reg = S5P_LCD0_LOWPWR , .val = 0x0, }, | ||
102 | { .reg = S5P_LCD1_LOWPWR , .val = 0x0, }, | ||
103 | { .reg = S5P_MAUDIO_LOWPWR , .val = 0x0, }, | ||
104 | { .reg = S5P_GPS_LOWPWR , .val = 0x0, }, | ||
105 | { .reg = S5P_GPS_ALIVE_LOWPWR , .val = 0x0, }, | ||
106 | }; | ||
107 | 38 | ||
108 | static struct sleep_save exynos4_set_clksrc[] = { | 39 | static struct sleep_save exynos4_set_clksrc[] = { |
109 | { .reg = S5P_CLKSRC_MASK_TOP , .val = 0x00000001, }, | 40 | { .reg = S5P_CLKSRC_MASK_TOP , .val = 0x00000001, }, |
@@ -118,20 +49,28 @@ static struct sleep_save exynos4_set_clksrc[] = { | |||
118 | { .reg = S5P_CLKSRC_MASK_DMC , .val = 0x00010000, }, | 49 | { .reg = S5P_CLKSRC_MASK_DMC , .val = 0x00010000, }, |
119 | }; | 50 | }; |
120 | 51 | ||
52 | static struct sleep_save exynos4_epll_save[] = { | ||
53 | SAVE_ITEM(S5P_EPLL_CON0), | ||
54 | SAVE_ITEM(S5P_EPLL_CON1), | ||
55 | }; | ||
56 | |||
57 | static struct sleep_save exynos4_vpll_save[] = { | ||
58 | SAVE_ITEM(S5P_VPLL_CON0), | ||
59 | SAVE_ITEM(S5P_VPLL_CON1), | ||
60 | }; | ||
61 | |||
121 | static struct sleep_save exynos4_core_save[] = { | 62 | static struct sleep_save exynos4_core_save[] = { |
122 | /* CMU side */ | 63 | /* CMU side */ |
123 | SAVE_ITEM(S5P_CLKDIV_LEFTBUS), | 64 | SAVE_ITEM(S5P_CLKDIV_LEFTBUS), |
124 | SAVE_ITEM(S5P_CLKGATE_IP_LEFTBUS), | 65 | SAVE_ITEM(S5P_CLKGATE_IP_LEFTBUS), |
125 | SAVE_ITEM(S5P_CLKDIV_RIGHTBUS), | 66 | SAVE_ITEM(S5P_CLKDIV_RIGHTBUS), |
126 | SAVE_ITEM(S5P_CLKGATE_IP_RIGHTBUS), | 67 | SAVE_ITEM(S5P_CLKGATE_IP_RIGHTBUS), |
127 | SAVE_ITEM(S5P_EPLL_CON0), | ||
128 | SAVE_ITEM(S5P_EPLL_CON1), | ||
129 | SAVE_ITEM(S5P_VPLL_CON0), | ||
130 | SAVE_ITEM(S5P_VPLL_CON1), | ||
131 | SAVE_ITEM(S5P_CLKSRC_TOP0), | 68 | SAVE_ITEM(S5P_CLKSRC_TOP0), |
132 | SAVE_ITEM(S5P_CLKSRC_TOP1), | 69 | SAVE_ITEM(S5P_CLKSRC_TOP1), |
133 | SAVE_ITEM(S5P_CLKSRC_CAM), | 70 | SAVE_ITEM(S5P_CLKSRC_CAM), |
71 | SAVE_ITEM(S5P_CLKSRC_TV), | ||
134 | SAVE_ITEM(S5P_CLKSRC_MFC), | 72 | SAVE_ITEM(S5P_CLKSRC_MFC), |
73 | SAVE_ITEM(S5P_CLKSRC_G3D), | ||
135 | SAVE_ITEM(S5P_CLKSRC_IMAGE), | 74 | SAVE_ITEM(S5P_CLKSRC_IMAGE), |
136 | SAVE_ITEM(S5P_CLKSRC_LCD0), | 75 | SAVE_ITEM(S5P_CLKSRC_LCD0), |
137 | SAVE_ITEM(S5P_CLKSRC_LCD1), | 76 | SAVE_ITEM(S5P_CLKSRC_LCD1), |
@@ -158,6 +97,7 @@ static struct sleep_save exynos4_core_save[] = { | |||
158 | SAVE_ITEM(S5P_CLKDIV_PERIL4), | 97 | SAVE_ITEM(S5P_CLKDIV_PERIL4), |
159 | SAVE_ITEM(S5P_CLKDIV_PERIL5), | 98 | SAVE_ITEM(S5P_CLKDIV_PERIL5), |
160 | SAVE_ITEM(S5P_CLKDIV_TOP), | 99 | SAVE_ITEM(S5P_CLKDIV_TOP), |
100 | SAVE_ITEM(S5P_CLKSRC_MASK_TOP), | ||
161 | SAVE_ITEM(S5P_CLKSRC_MASK_CAM), | 101 | SAVE_ITEM(S5P_CLKSRC_MASK_CAM), |
162 | SAVE_ITEM(S5P_CLKSRC_MASK_TV), | 102 | SAVE_ITEM(S5P_CLKSRC_MASK_TV), |
163 | SAVE_ITEM(S5P_CLKSRC_MASK_LCD0), | 103 | SAVE_ITEM(S5P_CLKSRC_MASK_LCD0), |
@@ -166,6 +106,7 @@ static struct sleep_save exynos4_core_save[] = { | |||
166 | SAVE_ITEM(S5P_CLKSRC_MASK_FSYS), | 106 | SAVE_ITEM(S5P_CLKSRC_MASK_FSYS), |
167 | SAVE_ITEM(S5P_CLKSRC_MASK_PERIL0), | 107 | SAVE_ITEM(S5P_CLKSRC_MASK_PERIL0), |
168 | SAVE_ITEM(S5P_CLKSRC_MASK_PERIL1), | 108 | SAVE_ITEM(S5P_CLKSRC_MASK_PERIL1), |
109 | SAVE_ITEM(S5P_CLKDIV2_RATIO), | ||
169 | SAVE_ITEM(S5P_CLKGATE_SCLKCAM), | 110 | SAVE_ITEM(S5P_CLKGATE_SCLKCAM), |
170 | SAVE_ITEM(S5P_CLKGATE_IP_CAM), | 111 | SAVE_ITEM(S5P_CLKGATE_IP_CAM), |
171 | SAVE_ITEM(S5P_CLKGATE_IP_TV), | 112 | SAVE_ITEM(S5P_CLKGATE_IP_TV), |
@@ -186,8 +127,10 @@ static struct sleep_save exynos4_core_save[] = { | |||
186 | SAVE_ITEM(S5P_CLKGATE_IP_DMC), | 127 | SAVE_ITEM(S5P_CLKGATE_IP_DMC), |
187 | SAVE_ITEM(S5P_CLKSRC_CPU), | 128 | SAVE_ITEM(S5P_CLKSRC_CPU), |
188 | SAVE_ITEM(S5P_CLKDIV_CPU), | 129 | SAVE_ITEM(S5P_CLKDIV_CPU), |
130 | SAVE_ITEM(S5P_CLKDIV_CPU + 0x4), | ||
189 | SAVE_ITEM(S5P_CLKGATE_SCLKCPU), | 131 | SAVE_ITEM(S5P_CLKGATE_SCLKCPU), |
190 | SAVE_ITEM(S5P_CLKGATE_IP_CPU), | 132 | SAVE_ITEM(S5P_CLKGATE_IP_CPU), |
133 | |||
191 | /* GIC side */ | 134 | /* GIC side */ |
192 | SAVE_ITEM(S5P_VA_GIC_CPU + 0x000), | 135 | SAVE_ITEM(S5P_VA_GIC_CPU + 0x000), |
193 | SAVE_ITEM(S5P_VA_GIC_CPU + 0x004), | 136 | SAVE_ITEM(S5P_VA_GIC_CPU + 0x004), |
@@ -270,6 +213,13 @@ static struct sleep_save exynos4_core_save[] = { | |||
270 | SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x070), | 213 | SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x070), |
271 | SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x080), | 214 | SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x080), |
272 | SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x090), | 215 | SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x090), |
216 | |||
217 | /* SROM side */ | ||
218 | SAVE_ITEM(S5P_SROM_BW), | ||
219 | SAVE_ITEM(S5P_SROM_BC0), | ||
220 | SAVE_ITEM(S5P_SROM_BC1), | ||
221 | SAVE_ITEM(S5P_SROM_BC2), | ||
222 | SAVE_ITEM(S5P_SROM_BC3), | ||
273 | }; | 223 | }; |
274 | 224 | ||
275 | static struct sleep_save exynos4_l2cc_save[] = { | 225 | static struct sleep_save exynos4_l2cc_save[] = { |
@@ -280,37 +230,11 @@ static struct sleep_save exynos4_l2cc_save[] = { | |||
280 | SAVE_ITEM(S5P_VA_L2CC + L2X0_AUX_CTRL), | 230 | SAVE_ITEM(S5P_VA_L2CC + L2X0_AUX_CTRL), |
281 | }; | 231 | }; |
282 | 232 | ||
233 | /* For Cortex-A9 Diagnostic and Power control register */ | ||
234 | static unsigned int save_arm_register[2]; | ||
235 | |||
283 | static int exynos4_cpu_suspend(unsigned long arg) | 236 | static int exynos4_cpu_suspend(unsigned long arg) |
284 | { | 237 | { |
285 | unsigned long tmp; | ||
286 | unsigned long mask = 0xFFFFFFFF; | ||
287 | |||
288 | /* Setting Central Sequence Register for power down mode */ | ||
289 | |||
290 | tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
291 | tmp &= ~(S5P_CENTRAL_LOWPWR_CFG); | ||
292 | __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
293 | |||
294 | /* Setting Central Sequence option Register */ | ||
295 | |||
296 | tmp = __raw_readl(S5P_CENTRAL_SEQ_OPTION); | ||
297 | tmp &= ~(S5P_USE_MASK); | ||
298 | tmp |= S5P_USE_STANDBY_WFI0; | ||
299 | __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION); | ||
300 | |||
301 | /* Clear all interrupt pending to avoid early wakeup */ | ||
302 | |||
303 | __raw_writel(mask, (S5P_VA_GIC_DIST + 0x280)); | ||
304 | __raw_writel(mask, (S5P_VA_GIC_DIST + 0x284)); | ||
305 | __raw_writel(mask, (S5P_VA_GIC_DIST + 0x288)); | ||
306 | |||
307 | /* Disable all interrupt */ | ||
308 | |||
309 | __raw_writel(0x0, (S5P_VA_GIC_CPU + 0x000)); | ||
310 | __raw_writel(0x0, (S5P_VA_GIC_DIST + 0x000)); | ||
311 | __raw_writel(mask, (S5P_VA_GIC_DIST + 0x184)); | ||
312 | __raw_writel(mask, (S5P_VA_GIC_DIST + 0x188)); | ||
313 | |||
314 | outer_flush_all(); | 238 | outer_flush_all(); |
315 | 239 | ||
316 | /* issue the standby signal into the pm unit. */ | 240 | /* issue the standby signal into the pm unit. */ |
@@ -326,12 +250,14 @@ static void exynos4_pm_prepare(void) | |||
326 | 250 | ||
327 | s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); | 251 | s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); |
328 | s3c_pm_do_save(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); | 252 | s3c_pm_do_save(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); |
253 | s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save)); | ||
254 | s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save)); | ||
329 | 255 | ||
330 | tmp = __raw_readl(S5P_INFORM1); | 256 | tmp = __raw_readl(S5P_INFORM1); |
331 | 257 | ||
332 | /* Set value of power down register for sleep mode */ | 258 | /* Set value of power down register for sleep mode */ |
333 | 259 | ||
334 | s3c_pm_do_restore_core(exynos4_sleep, ARRAY_SIZE(exynos4_sleep)); | 260 | exynos4_sys_powerdown_conf(SYS_SLEEP); |
335 | __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); | 261 | __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); |
336 | 262 | ||
337 | /* ensure at least INFORM0 has the resume address */ | 263 | /* ensure at least INFORM0 has the resume address */ |
@@ -373,12 +299,80 @@ void exynos4_scu_enable(void __iomem *scu_base) | |||
373 | flush_cache_all(); | 299 | flush_cache_all(); |
374 | } | 300 | } |
375 | 301 | ||
302 | static unsigned long pll_base_rate; | ||
303 | |||
304 | static void exynos4_restore_pll(void) | ||
305 | { | ||
306 | unsigned long pll_con, locktime, lockcnt; | ||
307 | unsigned long pll_in_rate; | ||
308 | unsigned int p_div, epll_wait = 0, vpll_wait = 0; | ||
309 | |||
310 | if (pll_base_rate == 0) | ||
311 | return; | ||
312 | |||
313 | pll_in_rate = pll_base_rate; | ||
314 | |||
315 | /* EPLL */ | ||
316 | pll_con = exynos4_epll_save[0].val; | ||
317 | |||
318 | if (pll_con & (1 << 31)) { | ||
319 | pll_con &= (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT); | ||
320 | p_div = (pll_con >> PLL46XX_PDIV_SHIFT); | ||
321 | |||
322 | pll_in_rate /= 1000000; | ||
323 | |||
324 | locktime = (3000 / pll_in_rate) * p_div; | ||
325 | lockcnt = locktime * 10000 / (10000 / pll_in_rate); | ||
326 | |||
327 | __raw_writel(lockcnt, S5P_EPLL_LOCK); | ||
328 | |||
329 | s3c_pm_do_restore_core(exynos4_epll_save, | ||
330 | ARRAY_SIZE(exynos4_epll_save)); | ||
331 | epll_wait = 1; | ||
332 | } | ||
333 | |||
334 | pll_in_rate = pll_base_rate; | ||
335 | |||
336 | /* VPLL */ | ||
337 | pll_con = exynos4_vpll_save[0].val; | ||
338 | |||
339 | if (pll_con & (1 << 31)) { | ||
340 | pll_in_rate /= 1000000; | ||
341 | /* 750us */ | ||
342 | locktime = 750; | ||
343 | lockcnt = locktime * 10000 / (10000 / pll_in_rate); | ||
344 | |||
345 | __raw_writel(lockcnt, S5P_VPLL_LOCK); | ||
346 | |||
347 | s3c_pm_do_restore_core(exynos4_vpll_save, | ||
348 | ARRAY_SIZE(exynos4_vpll_save)); | ||
349 | vpll_wait = 1; | ||
350 | } | ||
351 | |||
352 | /* Wait PLL locking */ | ||
353 | |||
354 | do { | ||
355 | if (epll_wait) { | ||
356 | pll_con = __raw_readl(S5P_EPLL_CON0); | ||
357 | if (pll_con & (1 << S5P_EPLLCON0_LOCKED_SHIFT)) | ||
358 | epll_wait = 0; | ||
359 | } | ||
360 | |||
361 | if (vpll_wait) { | ||
362 | pll_con = __raw_readl(S5P_VPLL_CON0); | ||
363 | if (pll_con & (1 << S5P_VPLLCON0_LOCKED_SHIFT)) | ||
364 | vpll_wait = 0; | ||
365 | } | ||
366 | } while (epll_wait || vpll_wait); | ||
367 | } | ||
368 | |||
376 | static struct sysdev_driver exynos4_pm_driver = { | 369 | static struct sysdev_driver exynos4_pm_driver = { |
377 | .add = exynos4_pm_add, | 370 | .add = exynos4_pm_add, |
378 | }; | 371 | }; |
379 | 372 | ||
380 | static __init int exynos4_pm_drvinit(void) | 373 | static __init int exynos4_pm_drvinit(void) |
381 | { | 374 | { |
375 | struct clk *pll_base; | ||
382 | unsigned int tmp; | 376 | unsigned int tmp; |
383 | 377 | ||
384 | s3c_pm_init(); | 378 | s3c_pm_init(); |
@@ -389,12 +383,69 @@ static __init int exynos4_pm_drvinit(void) | |||
389 | tmp |= ((0xFF << 8) | (0x1F << 1)); | 383 | tmp |= ((0xFF << 8) | (0x1F << 1)); |
390 | __raw_writel(tmp, S5P_WAKEUP_MASK); | 384 | __raw_writel(tmp, S5P_WAKEUP_MASK); |
391 | 385 | ||
386 | pll_base = clk_get(NULL, "xtal"); | ||
387 | |||
388 | if (!IS_ERR(pll_base)) { | ||
389 | pll_base_rate = clk_get_rate(pll_base); | ||
390 | clk_put(pll_base); | ||
391 | } | ||
392 | |||
392 | return sysdev_driver_register(&exynos4_sysclass, &exynos4_pm_driver); | 393 | return sysdev_driver_register(&exynos4_sysclass, &exynos4_pm_driver); |
393 | } | 394 | } |
394 | arch_initcall(exynos4_pm_drvinit); | 395 | arch_initcall(exynos4_pm_drvinit); |
395 | 396 | ||
397 | static int exynos4_pm_suspend(void) | ||
398 | { | ||
399 | unsigned long tmp; | ||
400 | |||
401 | /* Setting Central Sequence Register for power down mode */ | ||
402 | |||
403 | tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
404 | tmp &= ~S5P_CENTRAL_LOWPWR_CFG; | ||
405 | __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
406 | |||
407 | /* Save Power control register */ | ||
408 | asm ("mrc p15, 0, %0, c15, c0, 0" | ||
409 | : "=r" (tmp) : : "cc"); | ||
410 | save_arm_register[0] = tmp; | ||
411 | |||
412 | /* Save Diagnostic register */ | ||
413 | asm ("mrc p15, 0, %0, c15, c0, 1" | ||
414 | : "=r" (tmp) : : "cc"); | ||
415 | save_arm_register[1] = tmp; | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
396 | static void exynos4_pm_resume(void) | 420 | static void exynos4_pm_resume(void) |
397 | { | 421 | { |
422 | unsigned long tmp; | ||
423 | |||
424 | /* | ||
425 | * If PMU failed while entering sleep mode, WFI will be | ||
426 | * ignored by PMU and then exiting cpu_do_idle(). | ||
427 | * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically | ||
428 | * in this situation. | ||
429 | */ | ||
430 | tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
431 | if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { | ||
432 | tmp |= S5P_CENTRAL_LOWPWR_CFG; | ||
433 | __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
434 | /* No need to perform below restore code */ | ||
435 | goto early_wakeup; | ||
436 | } | ||
437 | /* Restore Power control register */ | ||
438 | tmp = save_arm_register[0]; | ||
439 | asm volatile ("mcr p15, 0, %0, c15, c0, 0" | ||
440 | : : "r" (tmp) | ||
441 | : "cc"); | ||
442 | |||
443 | /* Restore Diagnostic register */ | ||
444 | tmp = save_arm_register[1]; | ||
445 | asm volatile ("mcr p15, 0, %0, c15, c0, 1" | ||
446 | : : "r" (tmp) | ||
447 | : "cc"); | ||
448 | |||
398 | /* For release retention */ | 449 | /* For release retention */ |
399 | 450 | ||
400 | __raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION); | 451 | __raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION); |
@@ -407,6 +458,8 @@ static void exynos4_pm_resume(void) | |||
407 | 458 | ||
408 | s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); | 459 | s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); |
409 | 460 | ||
461 | exynos4_restore_pll(); | ||
462 | |||
410 | exynos4_scu_enable(S5P_VA_SCU); | 463 | exynos4_scu_enable(S5P_VA_SCU); |
411 | 464 | ||
412 | #ifdef CONFIG_CACHE_L2X0 | 465 | #ifdef CONFIG_CACHE_L2X0 |
@@ -415,9 +468,13 @@ static void exynos4_pm_resume(void) | |||
415 | /* enable L2X0*/ | 468 | /* enable L2X0*/ |
416 | writel_relaxed(1, S5P_VA_L2CC + L2X0_CTRL); | 469 | writel_relaxed(1, S5P_VA_L2CC + L2X0_CTRL); |
417 | #endif | 470 | #endif |
471 | |||
472 | early_wakeup: | ||
473 | return; | ||
418 | } | 474 | } |
419 | 475 | ||
420 | static struct syscore_ops exynos4_pm_syscore_ops = { | 476 | static struct syscore_ops exynos4_pm_syscore_ops = { |
477 | .suspend = exynos4_pm_suspend, | ||
421 | .resume = exynos4_pm_resume, | 478 | .resume = exynos4_pm_resume, |
422 | }; | 479 | }; |
423 | 480 | ||