Commit | Line | Data |
---|---|---|
97991657 MD |
1 | /* |
2 | * sh7372 Power management support | |
3 | * | |
4 | * Copyright (C) 2011 Magnus Damm | |
5 | * | |
6 | * This file is subject to the terms and conditions of the GNU General Public | |
7 | * License. See the file "COPYING" in the main directory of this archive | |
8 | * for more details. | |
9 | */ | |
10 | ||
11 | #include <linux/pm.h> | |
12 | #include <linux/suspend.h> | |
082a8ca1 | 13 | #include <linux/cpuidle.h> |
97991657 MD |
14 | #include <linux/module.h> |
15 | #include <linux/list.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/slab.h> | |
b5e8d269 | 18 | #include <linux/pm_clock.h> |
e3e01091 RW |
19 | #include <linux/platform_device.h> |
20 | #include <linux/delay.h> | |
cf33835c MD |
21 | #include <linux/irq.h> |
22 | #include <linux/bitrev.h> | |
056879d2 | 23 | #include <linux/console.h> |
5b41147c | 24 | #include <asm/cpuidle.h> |
97991657 MD |
25 | #include <asm/io.h> |
26 | #include <asm/tlbflush.h> | |
06b84166 | 27 | #include <asm/suspend.h> |
97991657 | 28 | #include <mach/common.h> |
e3e01091 | 29 | #include <mach/sh7372.h> |
b9299a72 | 30 | #include <mach/pm-rmobile.h> |
97991657 | 31 | |
cf33835c | 32 | /* DBG */ |
0a4b04dc AB |
33 | #define DBGREG1 IOMEM(0xe6100020) |
34 | #define DBGREG9 IOMEM(0xe6100040) | |
97991657 | 35 | |
cf33835c | 36 | /* CPGA */ |
0a4b04dc AB |
37 | #define SYSTBCR IOMEM(0xe6150024) |
38 | #define MSTPSR0 IOMEM(0xe6150030) | |
39 | #define MSTPSR1 IOMEM(0xe6150038) | |
40 | #define MSTPSR2 IOMEM(0xe6150040) | |
41 | #define MSTPSR3 IOMEM(0xe6150048) | |
42 | #define MSTPSR4 IOMEM(0xe615004c) | |
43 | #define PLLC01STPCR IOMEM(0xe61500c8) | |
cf33835c MD |
44 | |
45 | /* SYSC */ | |
0a4b04dc AB |
46 | #define SBAR IOMEM(0xe6180020) |
47 | #define WUPRMSK IOMEM(0xe6180028) | |
48 | #define WUPSMSK IOMEM(0xe618002c) | |
49 | #define WUPSMSK2 IOMEM(0xe6180048) | |
50 | #define WUPSFAC IOMEM(0xe6180098) | |
51 | #define IRQCR IOMEM(0xe618022c) | |
52 | #define IRQCR2 IOMEM(0xe6180238) | |
53 | #define IRQCR3 IOMEM(0xe6180244) | |
54 | #define IRQCR4 IOMEM(0xe6180248) | |
55 | #define PDNSEL IOMEM(0xe6180254) | |
cf33835c MD |
56 | |
57 | /* INTC */ | |
0a4b04dc AB |
58 | #define ICR1A IOMEM(0xe6900000) |
59 | #define ICR2A IOMEM(0xe6900004) | |
60 | #define ICR3A IOMEM(0xe6900008) | |
61 | #define ICR4A IOMEM(0xe690000c) | |
62 | #define INTMSK00A IOMEM(0xe6900040) | |
63 | #define INTMSK10A IOMEM(0xe6900044) | |
64 | #define INTMSK20A IOMEM(0xe6900048) | |
65 | #define INTMSK30A IOMEM(0xe690004c) | |
cf33835c MD |
66 | |
67 | /* MFIS */ | |
0a4b04dc | 68 | /* FIXME: pointing where? */ |
cf33835c MD |
69 | #define SMFRAM 0xe6a70000 |
70 | ||
71 | /* AP-System Core */ | |
0a4b04dc | 72 | #define APARMBAREA IOMEM(0xe6f10020) |
e3e01091 | 73 | |
e3e01091 RW |
74 | #ifdef CONFIG_PM |
75 | ||
8ae28ecb | 76 | #define PM_DOMAIN_ON_OFF_LATENCY_NS 250000 |
b9299a72 KM |
77 | |
78 | static int sh7372_a4r_pd_suspend(void) | |
79 | { | |
80 | sh7372_intcs_suspend(); | |
81 | __raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */ | |
82 | return 0; | |
83 | } | |
84 | ||
70fe7b24 | 85 | static bool a4s_suspend_ready; |
b9299a72 | 86 | |
e7e59a4b | 87 | static int sh7372_a4s_pd_suspend(void) |
b9299a72 KM |
88 | { |
89 | /* | |
90 | * The A4S domain contains the CPU core and therefore it should | |
70fe7b24 RW |
91 | * only be turned off if the CPU is not in use. This may happen |
92 | * during system suspend, when SYSC is going to be used for generating | |
93 | * resume signals and a4s_suspend_ready is set to let | |
94 | * sh7372_enter_suspend() know that it can turn A4S off. | |
b9299a72 | 95 | */ |
70fe7b24 | 96 | a4s_suspend_ready = true; |
b9299a72 KM |
97 | return -EBUSY; |
98 | } | |
99 | ||
e7e59a4b | 100 | static void sh7372_a4s_pd_resume(void) |
70fe7b24 RW |
101 | { |
102 | a4s_suspend_ready = false; | |
103 | } | |
b9299a72 KM |
104 | |
105 | static int sh7372_a3sp_pd_suspend(void) | |
106 | { | |
107 | /* | |
108 | * Serial consoles make use of SCIF hardware located in A3SP, | |
109 | * keep such power domain on if "no_console_suspend" is set. | |
110 | */ | |
111 | return console_suspend_enabled ? 0 : -EBUSY; | |
112 | } | |
113 | ||
e7e59a4b RW |
114 | static struct rmobile_pm_domain sh7372_pm_domains[] = { |
115 | { | |
116 | .genpd.name = "A4LC", | |
8ae28ecb RW |
117 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
118 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
119 | .bit_shift = 1, |
120 | }, | |
121 | { | |
122 | .genpd.name = "A4MP", | |
8ae28ecb RW |
123 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
124 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
125 | .bit_shift = 2, |
126 | }, | |
127 | { | |
128 | .genpd.name = "D4", | |
8ae28ecb RW |
129 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
130 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
131 | .bit_shift = 3, |
132 | }, | |
133 | { | |
134 | .genpd.name = "A4R", | |
8ae28ecb RW |
135 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
136 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
137 | .bit_shift = 5, |
138 | .suspend = sh7372_a4r_pd_suspend, | |
139 | .resume = sh7372_intcs_resume, | |
140 | }, | |
141 | { | |
142 | .genpd.name = "A3RV", | |
8ae28ecb RW |
143 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
144 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
145 | .bit_shift = 6, |
146 | }, | |
147 | { | |
148 | .genpd.name = "A3RI", | |
8ae28ecb RW |
149 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
150 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
151 | .bit_shift = 8, |
152 | }, | |
153 | { | |
154 | .genpd.name = "A4S", | |
8ae28ecb RW |
155 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
156 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
157 | .bit_shift = 10, |
158 | .gov = &pm_domain_always_on_gov, | |
159 | .no_debug = true, | |
160 | .suspend = sh7372_a4s_pd_suspend, | |
161 | .resume = sh7372_a4s_pd_resume, | |
162 | }, | |
163 | { | |
164 | .genpd.name = "A3SP", | |
8ae28ecb RW |
165 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
166 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
167 | .bit_shift = 11, |
168 | .gov = &pm_domain_always_on_gov, | |
169 | .no_debug = true, | |
170 | .suspend = sh7372_a3sp_pd_suspend, | |
171 | }, | |
172 | { | |
173 | .genpd.name = "A3SG", | |
8ae28ecb RW |
174 | .genpd.power_on_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, |
175 | .genpd.power_off_latency_ns = PM_DOMAIN_ON_OFF_LATENCY_NS, | |
e7e59a4b RW |
176 | .bit_shift = 13, |
177 | }, | |
b9299a72 KM |
178 | }; |
179 | ||
e7e59a4b RW |
180 | void __init sh7372_init_pm_domains(void) |
181 | { | |
182 | rmobile_init_domains(sh7372_pm_domains, ARRAY_SIZE(sh7372_pm_domains)); | |
183 | pm_genpd_add_subdomain_names("A4LC", "A3RV"); | |
184 | pm_genpd_add_subdomain_names("A4R", "A4LC"); | |
185 | pm_genpd_add_subdomain_names("A4S", "A3SG"); | |
186 | pm_genpd_add_subdomain_names("A4S", "A3SP"); | |
187 | } | |
b9299a72 | 188 | |
1645b767 | 189 | #endif /* CONFIG_PM */ |
e3e01091 | 190 | |
a0089bd6 | 191 | #if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE) |
f7dadb37 | 192 | static void sh7372_set_reset_vector(unsigned long address) |
06b84166 MD |
193 | { |
194 | /* set reset vector, translate 4k */ | |
f7dadb37 | 195 | __raw_writel(address, SBAR); |
06b84166 | 196 | __raw_writel(0, APARMBAREA); |
f7dadb37 MD |
197 | } |
198 | ||
f7dadb37 | 199 | static void sh7372_enter_sysc(int pllc0_on, unsigned long sleep_mode) |
cf33835c | 200 | { |
cf33835c MD |
201 | if (pllc0_on) |
202 | __raw_writel(0, PLLC01STPCR); | |
203 | else | |
204 | __raw_writel(1 << 28, PLLC01STPCR); | |
205 | ||
cf33835c | 206 | __raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */ |
f7dadb37 | 207 | cpu_suspend(sleep_mode, sh7372_do_idle_sysc); |
cf33835c MD |
208 | __raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */ |
209 | ||
210 | /* disable reset vector translation */ | |
211 | __raw_writel(0, SBAR); | |
212 | } | |
213 | ||
f7dadb37 | 214 | static int sh7372_sysc_valid(unsigned long *mskp, unsigned long *msk2p) |
cf33835c MD |
215 | { |
216 | unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4; | |
217 | unsigned long msk, msk2; | |
218 | ||
219 | /* check active clocks to determine potential wakeup sources */ | |
220 | ||
221 | mstpsr0 = __raw_readl(MSTPSR0); | |
222 | if ((mstpsr0 & 0x00000003) != 0x00000003) { | |
223 | pr_debug("sh7372 mstpsr0 0x%08lx\n", mstpsr0); | |
224 | return 0; | |
225 | } | |
226 | ||
227 | mstpsr1 = __raw_readl(MSTPSR1); | |
228 | if ((mstpsr1 & 0xff079b7f) != 0xff079b7f) { | |
229 | pr_debug("sh7372 mstpsr1 0x%08lx\n", mstpsr1); | |
230 | return 0; | |
231 | } | |
232 | ||
233 | mstpsr2 = __raw_readl(MSTPSR2); | |
234 | if ((mstpsr2 & 0x000741ff) != 0x000741ff) { | |
235 | pr_debug("sh7372 mstpsr2 0x%08lx\n", mstpsr2); | |
236 | return 0; | |
237 | } | |
238 | ||
239 | mstpsr3 = __raw_readl(MSTPSR3); | |
240 | if ((mstpsr3 & 0x1a60f010) != 0x1a60f010) { | |
241 | pr_debug("sh7372 mstpsr3 0x%08lx\n", mstpsr3); | |
242 | return 0; | |
243 | } | |
244 | ||
245 | mstpsr4 = __raw_readl(MSTPSR4); | |
246 | if ((mstpsr4 & 0x00008cf0) != 0x00008cf0) { | |
247 | pr_debug("sh7372 mstpsr4 0x%08lx\n", mstpsr4); | |
248 | return 0; | |
249 | } | |
250 | ||
251 | msk = 0; | |
252 | msk2 = 0; | |
253 | ||
254 | /* make bitmaps of limited number of wakeup sources */ | |
255 | ||
256 | if ((mstpsr2 & (1 << 23)) == 0) /* SPU2 */ | |
257 | msk |= 1 << 31; | |
258 | ||
259 | if ((mstpsr2 & (1 << 12)) == 0) /* MFI_MFIM */ | |
260 | msk |= 1 << 21; | |
261 | ||
262 | if ((mstpsr4 & (1 << 3)) == 0) /* KEYSC */ | |
263 | msk |= 1 << 2; | |
264 | ||
265 | if ((mstpsr1 & (1 << 24)) == 0) /* CMT0 */ | |
266 | msk |= 1 << 1; | |
267 | ||
268 | if ((mstpsr3 & (1 << 29)) == 0) /* CMT1 */ | |
269 | msk |= 1 << 1; | |
270 | ||
271 | if ((mstpsr4 & (1 << 0)) == 0) /* CMT2 */ | |
272 | msk |= 1 << 1; | |
273 | ||
274 | if ((mstpsr2 & (1 << 13)) == 0) /* MFI_MFIS */ | |
275 | msk2 |= 1 << 17; | |
276 | ||
277 | *mskp = msk; | |
278 | *msk2p = msk2; | |
279 | ||
280 | return 1; | |
281 | } | |
282 | ||
283 | static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p) | |
284 | { | |
285 | u16 tmp, irqcr1, irqcr2; | |
286 | int k; | |
287 | ||
288 | irqcr1 = 0; | |
289 | irqcr2 = 0; | |
290 | ||
291 | /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */ | |
292 | for (k = 0; k <= 7; k++) { | |
293 | tmp = (icr >> ((7 - k) * 4)) & 0xf; | |
294 | irqcr1 |= (tmp & 0x03) << (k * 2); | |
295 | irqcr2 |= (tmp >> 2) << (k * 2); | |
296 | } | |
297 | ||
298 | *irqcr1p = irqcr1; | |
299 | *irqcr2p = irqcr2; | |
300 | } | |
301 | ||
f7dadb37 | 302 | static void sh7372_setup_sysc(unsigned long msk, unsigned long msk2) |
cf33835c MD |
303 | { |
304 | u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high; | |
305 | unsigned long tmp; | |
306 | ||
307 | /* read IRQ0A -> IRQ15A mask */ | |
308 | tmp = bitrev8(__raw_readb(INTMSK00A)); | |
309 | tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8; | |
310 | ||
311 | /* setup WUPSMSK from clocks and external IRQ mask */ | |
312 | msk = (~msk & 0xc030000f) | (tmp << 4); | |
313 | __raw_writel(msk, WUPSMSK); | |
314 | ||
315 | /* propage level/edge trigger for external IRQ 0->15 */ | |
316 | sh7372_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low); | |
317 | sh7372_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high); | |
318 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR); | |
319 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2); | |
320 | ||
321 | /* read IRQ16A -> IRQ31A mask */ | |
322 | tmp = bitrev8(__raw_readb(INTMSK20A)); | |
323 | tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8; | |
324 | ||
325 | /* setup WUPSMSK2 from clocks and external IRQ mask */ | |
326 | msk2 = (~msk2 & 0x00030000) | tmp; | |
327 | __raw_writel(msk2, WUPSMSK2); | |
328 | ||
329 | /* propage level/edge trigger for external IRQ 16->31 */ | |
330 | sh7372_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low); | |
331 | sh7372_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high); | |
332 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3); | |
333 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4); | |
334 | } | |
f7dadb37 MD |
335 | |
336 | static void sh7372_enter_a3sm_common(int pllc0_on) | |
337 | { | |
591e2ac4 MD |
338 | /* use INTCA together with SYSC for wakeup */ |
339 | sh7372_setup_sysc(1 << 0, 0); | |
f7dadb37 MD |
340 | sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc)); |
341 | sh7372_enter_sysc(pllc0_on, 1 << 12); | |
342 | } | |
caaca999 RW |
343 | |
344 | static void sh7372_enter_a4s_common(int pllc0_on) | |
345 | { | |
346 | sh7372_intca_suspend(); | |
347 | sh7372_set_reset_vector(SMFRAM); | |
348 | sh7372_enter_sysc(pllc0_on, 1 << 10); | |
349 | sh7372_intca_resume(); | |
350 | } | |
351 | ||
352 | static void sh7372_pm_setup_smfram(void) | |
353 | { | |
354 | memcpy((void *)SMFRAM, sh7372_resume_core_standby_sysc, 0x100); | |
355 | } | |
356 | #else | |
357 | static inline void sh7372_pm_setup_smfram(void) {} | |
911a472a | 358 | #endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */ |
f7dadb37 | 359 | |
082a8ca1 | 360 | #ifdef CONFIG_CPU_IDLE |
591e2ac4 MD |
361 | static int sh7372_do_idle_core_standby(unsigned long unused) |
362 | { | |
363 | cpu_do_idle(); /* WFI when SYSTBCR == 0x10 -> Core Standby */ | |
364 | return 0; | |
365 | } | |
366 | ||
5b41147c RW |
367 | static int sh7372_enter_core_standby(struct cpuidle_device *dev, |
368 | struct cpuidle_driver *drv, int index) | |
591e2ac4 MD |
369 | { |
370 | sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc)); | |
371 | ||
372 | /* enter sleep mode with SYSTBCR to 0x10 */ | |
373 | __raw_writel(0x10, SYSTBCR); | |
374 | cpu_suspend(0, sh7372_do_idle_core_standby); | |
375 | __raw_writel(0, SYSTBCR); | |
376 | ||
377 | /* disable reset vector translation */ | |
378 | __raw_writel(0, SBAR); | |
5b41147c RW |
379 | |
380 | return 1; | |
591e2ac4 | 381 | } |
cf33835c | 382 | |
5b41147c RW |
383 | static int sh7372_enter_a3sm_pll_on(struct cpuidle_device *dev, |
384 | struct cpuidle_driver *drv, int index) | |
3abd69d2 MD |
385 | { |
386 | sh7372_enter_a3sm_common(1); | |
5b41147c | 387 | return 2; |
3abd69d2 MD |
388 | } |
389 | ||
5b41147c RW |
390 | static int sh7372_enter_a3sm_pll_off(struct cpuidle_device *dev, |
391 | struct cpuidle_driver *drv, int index) | |
3abd69d2 MD |
392 | { |
393 | sh7372_enter_a3sm_common(0); | |
5b41147c | 394 | return 3; |
3abd69d2 MD |
395 | } |
396 | ||
caaca999 RW |
397 | static int sh7372_enter_a4s(struct cpuidle_device *dev, |
398 | struct cpuidle_driver *drv, int index) | |
082a8ca1 | 399 | { |
caaca999 RW |
400 | unsigned long msk, msk2; |
401 | ||
402 | if (!sh7372_sysc_valid(&msk, &msk2)) | |
403 | return sh7372_enter_a3sm_pll_off(dev, drv, index); | |
404 | ||
405 | sh7372_setup_sysc(msk, msk2); | |
406 | sh7372_enter_a4s_common(0); | |
407 | return 4; | |
082a8ca1 MD |
408 | } |
409 | ||
5b41147c RW |
410 | static struct cpuidle_driver sh7372_cpuidle_driver = { |
411 | .name = "sh7372_cpuidle", | |
412 | .owner = THIS_MODULE, | |
caaca999 | 413 | .state_count = 5, |
5b41147c RW |
414 | .safe_state_index = 0, /* C1 */ |
415 | .states[0] = ARM_CPUIDLE_WFI_STATE, | |
5b41147c RW |
416 | .states[1] = { |
417 | .name = "C2", | |
418 | .desc = "Core Standby Mode", | |
419 | .exit_latency = 10, | |
420 | .target_residency = 20 + 10, | |
421 | .flags = CPUIDLE_FLAG_TIME_VALID, | |
422 | .enter = sh7372_enter_core_standby, | |
423 | }, | |
424 | .states[2] = { | |
425 | .name = "C3", | |
426 | .desc = "A3SM PLL ON", | |
427 | .exit_latency = 20, | |
428 | .target_residency = 30 + 20, | |
429 | .flags = CPUIDLE_FLAG_TIME_VALID, | |
430 | .enter = sh7372_enter_a3sm_pll_on, | |
431 | }, | |
432 | .states[3] = { | |
433 | .name = "C4", | |
434 | .desc = "A3SM PLL OFF", | |
435 | .exit_latency = 120, | |
436 | .target_residency = 30 + 120, | |
437 | .flags = CPUIDLE_FLAG_TIME_VALID, | |
438 | .enter = sh7372_enter_a3sm_pll_off, | |
439 | }, | |
caaca999 RW |
440 | .states[4] = { |
441 | .name = "C5", | |
442 | .desc = "A4S PLL OFF", | |
443 | .exit_latency = 240, | |
444 | .target_residency = 30 + 240, | |
445 | .flags = CPUIDLE_FLAG_TIME_VALID, | |
446 | .enter = sh7372_enter_a4s, | |
447 | .disabled = true, | |
448 | }, | |
5b41147c | 449 | }; |
082a8ca1 | 450 | |
d5047097 | 451 | static void __init sh7372_cpuidle_init(void) |
082a8ca1 | 452 | { |
5b41147c | 453 | shmobile_cpuidle_set_driver(&sh7372_cpuidle_driver); |
082a8ca1 MD |
454 | } |
455 | #else | |
d5047097 | 456 | static void __init sh7372_cpuidle_init(void) {} |
082a8ca1 MD |
457 | #endif |
458 | ||
459 | #ifdef CONFIG_SUSPEND | |
97991657 MD |
460 | static int sh7372_enter_suspend(suspend_state_t suspend_state) |
461 | { | |
cf33835c MD |
462 | unsigned long msk, msk2; |
463 | ||
464 | /* check active clocks to determine potential wakeup sources */ | |
18c081e2 RW |
465 | if (sh7372_sysc_valid(&msk, &msk2) && a4s_suspend_ready) { |
466 | /* convert INTC mask/sense to SYSC mask/sense */ | |
467 | sh7372_setup_sysc(msk, msk2); | |
468 | ||
469 | /* enter A4S sleep with PLLC0 off */ | |
470 | pr_debug("entering A4S\n"); | |
471 | sh7372_enter_a4s_common(0); | |
472 | return 0; | |
cf33835c | 473 | } |
591e2ac4 MD |
474 | |
475 | /* default to enter A3SM sleep with PLLC0 off */ | |
476 | pr_debug("entering A3SM\n"); | |
477 | sh7372_enter_a3sm_common(0); | |
97991657 MD |
478 | return 0; |
479 | } | |
480 | ||
a8cf27be RW |
481 | /** |
482 | * sh7372_pm_notifier_fn - SH7372 PM notifier routine. | |
483 | * @notifier: Unused. | |
484 | * @pm_event: Event being handled. | |
485 | * @unused: Unused. | |
486 | */ | |
487 | static int sh7372_pm_notifier_fn(struct notifier_block *notifier, | |
488 | unsigned long pm_event, void *unused) | |
489 | { | |
490 | switch (pm_event) { | |
491 | case PM_SUSPEND_PREPARE: | |
492 | /* | |
493 | * This is necessary, because the A4R domain has to be "on" | |
494 | * when suspend_device_irqs() and resume_device_irqs() are | |
495 | * executed during system suspend and resume, respectively, so | |
496 | * that those functions don't crash while accessing the INTCS. | |
497 | */ | |
e7e59a4b | 498 | pm_genpd_name_poweron("A4R"); |
a8cf27be RW |
499 | break; |
500 | case PM_POST_SUSPEND: | |
501 | pm_genpd_poweroff_unused(); | |
502 | break; | |
503 | } | |
504 | ||
505 | return NOTIFY_DONE; | |
506 | } | |
507 | ||
97991657 MD |
508 | static void sh7372_suspend_init(void) |
509 | { | |
510 | shmobile_suspend_ops.enter = sh7372_enter_suspend; | |
a8cf27be | 511 | pm_notifier(sh7372_pm_notifier_fn, 0); |
97991657 MD |
512 | } |
513 | #else | |
514 | static void sh7372_suspend_init(void) {} | |
515 | #endif | |
516 | ||
97991657 MD |
517 | void __init sh7372_pm_init(void) |
518 | { | |
519 | /* enable DBG hardware block to kick SYSC */ | |
520 | __raw_writel(0x0000a500, DBGREG9); | |
521 | __raw_writel(0x0000a501, DBGREG9); | |
522 | __raw_writel(0x00000000, DBGREG1); | |
523 | ||
d93f5cde MD |
524 | /* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */ |
525 | __raw_writel(0, PDNSEL); | |
526 | ||
caaca999 RW |
527 | sh7372_pm_setup_smfram(); |
528 | ||
97991657 | 529 | sh7372_suspend_init(); |
082a8ca1 | 530 | sh7372_cpuidle_init(); |
97991657 | 531 | } |
caaca999 RW |
532 | |
533 | void __init sh7372_pm_init_late(void) | |
534 | { | |
535 | shmobile_init_late(); | |
536 | pm_genpd_name_attach_cpuidle("A4S", 4); | |
537 | } |