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