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