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> |
97991657 MD |
24 | #include <asm/system.h> |
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> |
97991657 | 30 | |
cf33835c MD |
31 | /* DBG */ |
32 | #define DBGREG1 0xe6100020 | |
33 | #define DBGREG9 0xe6100040 | |
97991657 | 34 | |
cf33835c MD |
35 | /* CPGA */ |
36 | #define SYSTBCR 0xe6150024 | |
37 | #define MSTPSR0 0xe6150030 | |
38 | #define MSTPSR1 0xe6150038 | |
39 | #define MSTPSR2 0xe6150040 | |
40 | #define MSTPSR3 0xe6150048 | |
41 | #define MSTPSR4 0xe615004c | |
42 | #define PLLC01STPCR 0xe61500c8 | |
43 | ||
44 | /* SYSC */ | |
e3e01091 RW |
45 | #define SPDCR 0xe6180008 |
46 | #define SWUCR 0xe6180014 | |
cf33835c | 47 | #define SBAR 0xe6180020 |
382414b9 | 48 | #define WUPRMSK 0xe6180028 |
cf33835c MD |
49 | #define WUPSMSK 0xe618002c |
50 | #define WUPSMSK2 0xe6180048 | |
e3e01091 | 51 | #define PSTR 0xe6180080 |
cf33835c MD |
52 | #define WUPSFAC 0xe6180098 |
53 | #define IRQCR 0xe618022c | |
54 | #define IRQCR2 0xe6180238 | |
55 | #define IRQCR3 0xe6180244 | |
56 | #define IRQCR4 0xe6180248 | |
57 | #define PDNSEL 0xe6180254 | |
58 | ||
59 | /* INTC */ | |
60 | #define ICR1A 0xe6900000 | |
61 | #define ICR2A 0xe6900004 | |
62 | #define ICR3A 0xe6900008 | |
63 | #define ICR4A 0xe690000c | |
64 | #define INTMSK00A 0xe6900040 | |
65 | #define INTMSK10A 0xe6900044 | |
66 | #define INTMSK20A 0xe6900048 | |
67 | #define INTMSK30A 0xe690004c | |
68 | ||
69 | /* MFIS */ | |
70 | #define SMFRAM 0xe6a70000 | |
71 | ||
72 | /* AP-System Core */ | |
73 | #define APARMBAREA 0xe6f10020 | |
e3e01091 RW |
74 | |
75 | #define PSTR_RETRIES 100 | |
76 | #define PSTR_DELAY_US 10 | |
77 | ||
78 | #ifdef CONFIG_PM | |
79 | ||
80 | static int pd_power_down(struct generic_pm_domain *genpd) | |
81 | { | |
82 | struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd); | |
83 | unsigned int mask = 1 << sh7372_pd->bit_shift; | |
84 | ||
767c0f3a RW |
85 | if (sh7372_pd->suspend) { |
86 | int ret = sh7372_pd->suspend(); | |
382414b9 | 87 | |
767c0f3a RW |
88 | if (ret) |
89 | return ret; | |
90 | } | |
382414b9 | 91 | |
e3e01091 RW |
92 | if (__raw_readl(PSTR) & mask) { |
93 | unsigned int retry_count; | |
94 | ||
95 | __raw_writel(mask, SPDCR); | |
96 | ||
97 | for (retry_count = PSTR_RETRIES; retry_count; retry_count--) { | |
98 | if (!(__raw_readl(SPDCR) & mask)) | |
99 | break; | |
100 | cpu_relax(); | |
101 | } | |
102 | } | |
103 | ||
d93f5cde | 104 | if (!sh7372_pd->no_debug) |
e84b2c20 RW |
105 | pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", |
106 | genpd->name, mask, __raw_readl(PSTR)); | |
e3e01091 RW |
107 | |
108 | return 0; | |
109 | } | |
110 | ||
bc9f5449 | 111 | static int __pd_power_up(struct sh7372_pm_domain *sh7372_pd, bool do_resume) |
e3e01091 | 112 | { |
e3e01091 RW |
113 | unsigned int mask = 1 << sh7372_pd->bit_shift; |
114 | unsigned int retry_count; | |
115 | int ret = 0; | |
116 | ||
117 | if (__raw_readl(PSTR) & mask) | |
118 | goto out; | |
119 | ||
120 | __raw_writel(mask, SWUCR); | |
121 | ||
122 | for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) { | |
123 | if (!(__raw_readl(SWUCR) & mask)) | |
be2658ff | 124 | break; |
e3e01091 RW |
125 | if (retry_count > PSTR_RETRIES) |
126 | udelay(PSTR_DELAY_US); | |
127 | else | |
128 | cpu_relax(); | |
129 | } | |
be2658ff | 130 | if (!retry_count) |
e3e01091 RW |
131 | ret = -EIO; |
132 | ||
d93f5cde | 133 | if (!sh7372_pd->no_debug) |
e84b2c20 RW |
134 | pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n", |
135 | sh7372_pd->genpd.name, mask, __raw_readl(PSTR)); | |
e3e01091 | 136 | |
382414b9 | 137 | out: |
bc9f5449 | 138 | if (ret == 0 && sh7372_pd->resume && do_resume) |
382414b9 MD |
139 | sh7372_pd->resume(); |
140 | ||
e3e01091 RW |
141 | return ret; |
142 | } | |
143 | ||
bc9f5449 RW |
144 | static int pd_power_up(struct generic_pm_domain *genpd) |
145 | { | |
146 | return __pd_power_up(to_sh7372_pd(genpd), true); | |
147 | } | |
148 | ||
767c0f3a | 149 | static int sh7372_a4r_suspend(void) |
382414b9 MD |
150 | { |
151 | sh7372_intcs_suspend(); | |
152 | __raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */ | |
767c0f3a | 153 | return 0; |
382414b9 MD |
154 | } |
155 | ||
e3e01091 RW |
156 | static bool pd_active_wakeup(struct device *dev) |
157 | { | |
d5e4cbfe RW |
158 | bool (*active_wakeup)(struct device *dev); |
159 | ||
160 | active_wakeup = dev_gpd_data(dev)->ops.active_wakeup; | |
161 | return active_wakeup ? active_wakeup(dev) : true; | |
e3e01091 RW |
162 | } |
163 | ||
d5e4cbfe RW |
164 | static int sh7372_stop_dev(struct device *dev) |
165 | { | |
166 | int (*stop)(struct device *dev); | |
167 | ||
168 | stop = dev_gpd_data(dev)->ops.stop; | |
169 | if (stop) { | |
170 | int ret = stop(dev); | |
171 | if (ret) | |
172 | return ret; | |
173 | } | |
174 | return pm_clk_suspend(dev); | |
175 | } | |
176 | ||
177 | static int sh7372_start_dev(struct device *dev) | |
178 | { | |
179 | int (*start)(struct device *dev); | |
180 | int ret; | |
181 | ||
182 | ret = pm_clk_resume(dev); | |
183 | if (ret) | |
184 | return ret; | |
185 | ||
186 | start = dev_gpd_data(dev)->ops.start; | |
187 | if (start) | |
188 | ret = start(dev); | |
189 | ||
190 | return ret; | |
191 | } | |
192 | ||
e3e01091 RW |
193 | void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) |
194 | { | |
195 | struct generic_pm_domain *genpd = &sh7372_pd->genpd; | |
b02c999a | 196 | struct dev_power_governor *gov = sh7372_pd->gov; |
e3e01091 | 197 | |
b02c999a | 198 | pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); |
d5e4cbfe RW |
199 | genpd->dev_ops.stop = sh7372_stop_dev; |
200 | genpd->dev_ops.start = sh7372_start_dev; | |
201 | genpd->dev_ops.active_wakeup = pd_active_wakeup; | |
0aa2a221 | 202 | genpd->dev_irq_safe = true; |
111058c3 RW |
203 | genpd->power_off = pd_power_down; |
204 | genpd->power_on = pd_power_up; | |
bc9f5449 | 205 | __pd_power_up(sh7372_pd, false); |
e3e01091 RW |
206 | } |
207 | ||
208 | void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd, | |
209 | struct platform_device *pdev) | |
210 | { | |
211 | struct device *dev = &pdev->dev; | |
212 | ||
e3e01091 | 213 | pm_genpd_add_device(&sh7372_pd->genpd, dev); |
4605ab65 RW |
214 | if (pm_clk_no_clocks(dev)) |
215 | pm_clk_add(dev, NULL); | |
e3e01091 RW |
216 | } |
217 | ||
111058c3 RW |
218 | void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd, |
219 | struct sh7372_pm_domain *sh7372_sd) | |
220 | { | |
221 | pm_genpd_add_subdomain(&sh7372_pd->genpd, &sh7372_sd->genpd); | |
222 | } | |
223 | ||
e3e01091 | 224 | struct sh7372_pm_domain sh7372_a4lc = { |
e84b2c20 | 225 | .genpd.name = "A4LC", |
e3e01091 RW |
226 | .bit_shift = 1, |
227 | }; | |
228 | ||
c1ba5bb5 | 229 | struct sh7372_pm_domain sh7372_a4mp = { |
e84b2c20 | 230 | .genpd.name = "A4MP", |
c1ba5bb5 KM |
231 | .bit_shift = 2, |
232 | }; | |
233 | ||
d24771de | 234 | struct sh7372_pm_domain sh7372_d4 = { |
e84b2c20 | 235 | .genpd.name = "D4", |
d24771de MD |
236 | .bit_shift = 3, |
237 | }; | |
238 | ||
382414b9 | 239 | struct sh7372_pm_domain sh7372_a4r = { |
e84b2c20 | 240 | .genpd.name = "A4R", |
382414b9 | 241 | .bit_shift = 5, |
a87dc8fd | 242 | .gov = &pm_domain_always_on_gov, |
382414b9 MD |
243 | .suspend = sh7372_a4r_suspend, |
244 | .resume = sh7372_intcs_resume, | |
382414b9 MD |
245 | }; |
246 | ||
33afebf3 | 247 | struct sh7372_pm_domain sh7372_a3rv = { |
e84b2c20 | 248 | .genpd.name = "A3RV", |
33afebf3 MD |
249 | .bit_shift = 6, |
250 | }; | |
251 | ||
082517aa | 252 | struct sh7372_pm_domain sh7372_a3ri = { |
e84b2c20 | 253 | .genpd.name = "A3RI", |
082517aa MD |
254 | .bit_shift = 8, |
255 | }; | |
256 | ||
767c0f3a RW |
257 | static int sh7372_a4s_suspend(void) |
258 | { | |
259 | /* | |
260 | * The A4S domain contains the CPU core and therefore it should | |
261 | * only be turned off if the CPU is in use. | |
262 | */ | |
263 | return -EBUSY; | |
264 | } | |
265 | ||
f7dadb37 MD |
266 | struct sh7372_pm_domain sh7372_a4s = { |
267 | .genpd.name = "A4S", | |
268 | .bit_shift = 10, | |
269 | .gov = &pm_domain_always_on_gov, | |
270 | .no_debug = true, | |
767c0f3a | 271 | .suspend = sh7372_a4s_suspend, |
f7dadb37 MD |
272 | }; |
273 | ||
767c0f3a RW |
274 | static int sh7372_a3sp_suspend(void) |
275 | { | |
276 | /* | |
277 | * Serial consoles make use of SCIF hardware located in A3SP, | |
278 | * keep such power domain on if "no_console_suspend" is set. | |
279 | */ | |
280 | return console_suspend_enabled ? -EBUSY : 0; | |
281 | } | |
282 | ||
d93f5cde | 283 | struct sh7372_pm_domain sh7372_a3sp = { |
e84b2c20 | 284 | .genpd.name = "A3SP", |
d93f5cde | 285 | .bit_shift = 11, |
a87dc8fd | 286 | .gov = &pm_domain_always_on_gov, |
d93f5cde | 287 | .no_debug = true, |
767c0f3a | 288 | .suspend = sh7372_a3sp_suspend, |
d93f5cde MD |
289 | }; |
290 | ||
c47586b6 | 291 | struct sh7372_pm_domain sh7372_a3sg = { |
e84b2c20 | 292 | .genpd.name = "A3SG", |
c47586b6 MD |
293 | .bit_shift = 13, |
294 | }; | |
295 | ||
88995e80 RW |
296 | #else /* !CONFIG_PM */ |
297 | ||
298 | static inline void sh7372_a3sp_init(void) {} | |
299 | ||
300 | #endif /* !CONFIG_PM */ | |
e3e01091 | 301 | |
a0089bd6 | 302 | #if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE) |
06b84166 | 303 | static int sh7372_do_idle_core_standby(unsigned long unused) |
97991657 | 304 | { |
06b84166 MD |
305 | cpu_do_idle(); /* WFI when SYSTBCR == 0x10 -> Core Standby */ |
306 | return 0; | |
307 | } | |
97991657 | 308 | |
f7dadb37 | 309 | static void sh7372_set_reset_vector(unsigned long address) |
06b84166 MD |
310 | { |
311 | /* set reset vector, translate 4k */ | |
f7dadb37 | 312 | __raw_writel(address, SBAR); |
06b84166 | 313 | __raw_writel(0, APARMBAREA); |
f7dadb37 MD |
314 | } |
315 | ||
316 | static void sh7372_enter_core_standby(void) | |
317 | { | |
318 | sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc)); | |
97991657 | 319 | |
06b84166 MD |
320 | /* enter sleep mode with SYSTBCR to 0x10 */ |
321 | __raw_writel(0x10, SYSTBCR); | |
322 | cpu_suspend(0, sh7372_do_idle_core_standby); | |
323 | __raw_writel(0, SYSTBCR); | |
97991657 | 324 | |
06b84166 MD |
325 | /* disable reset vector translation */ |
326 | __raw_writel(0, SBAR); | |
97991657 | 327 | } |
a0089bd6 | 328 | #endif |
97991657 | 329 | |
a0089bd6 | 330 | #ifdef CONFIG_SUSPEND |
f7dadb37 | 331 | static void sh7372_enter_sysc(int pllc0_on, unsigned long sleep_mode) |
cf33835c | 332 | { |
cf33835c MD |
333 | if (pllc0_on) |
334 | __raw_writel(0, PLLC01STPCR); | |
335 | else | |
336 | __raw_writel(1 << 28, PLLC01STPCR); | |
337 | ||
cf33835c | 338 | __raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */ |
f7dadb37 | 339 | cpu_suspend(sleep_mode, sh7372_do_idle_sysc); |
cf33835c MD |
340 | __raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */ |
341 | ||
342 | /* disable reset vector translation */ | |
343 | __raw_writel(0, SBAR); | |
344 | } | |
345 | ||
f7dadb37 | 346 | static int sh7372_sysc_valid(unsigned long *mskp, unsigned long *msk2p) |
cf33835c MD |
347 | { |
348 | unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4; | |
349 | unsigned long msk, msk2; | |
350 | ||
351 | /* check active clocks to determine potential wakeup sources */ | |
352 | ||
353 | mstpsr0 = __raw_readl(MSTPSR0); | |
354 | if ((mstpsr0 & 0x00000003) != 0x00000003) { | |
355 | pr_debug("sh7372 mstpsr0 0x%08lx\n", mstpsr0); | |
356 | return 0; | |
357 | } | |
358 | ||
359 | mstpsr1 = __raw_readl(MSTPSR1); | |
360 | if ((mstpsr1 & 0xff079b7f) != 0xff079b7f) { | |
361 | pr_debug("sh7372 mstpsr1 0x%08lx\n", mstpsr1); | |
362 | return 0; | |
363 | } | |
364 | ||
365 | mstpsr2 = __raw_readl(MSTPSR2); | |
366 | if ((mstpsr2 & 0x000741ff) != 0x000741ff) { | |
367 | pr_debug("sh7372 mstpsr2 0x%08lx\n", mstpsr2); | |
368 | return 0; | |
369 | } | |
370 | ||
371 | mstpsr3 = __raw_readl(MSTPSR3); | |
372 | if ((mstpsr3 & 0x1a60f010) != 0x1a60f010) { | |
373 | pr_debug("sh7372 mstpsr3 0x%08lx\n", mstpsr3); | |
374 | return 0; | |
375 | } | |
376 | ||
377 | mstpsr4 = __raw_readl(MSTPSR4); | |
378 | if ((mstpsr4 & 0x00008cf0) != 0x00008cf0) { | |
379 | pr_debug("sh7372 mstpsr4 0x%08lx\n", mstpsr4); | |
380 | return 0; | |
381 | } | |
382 | ||
383 | msk = 0; | |
384 | msk2 = 0; | |
385 | ||
386 | /* make bitmaps of limited number of wakeup sources */ | |
387 | ||
388 | if ((mstpsr2 & (1 << 23)) == 0) /* SPU2 */ | |
389 | msk |= 1 << 31; | |
390 | ||
391 | if ((mstpsr2 & (1 << 12)) == 0) /* MFI_MFIM */ | |
392 | msk |= 1 << 21; | |
393 | ||
394 | if ((mstpsr4 & (1 << 3)) == 0) /* KEYSC */ | |
395 | msk |= 1 << 2; | |
396 | ||
397 | if ((mstpsr1 & (1 << 24)) == 0) /* CMT0 */ | |
398 | msk |= 1 << 1; | |
399 | ||
400 | if ((mstpsr3 & (1 << 29)) == 0) /* CMT1 */ | |
401 | msk |= 1 << 1; | |
402 | ||
403 | if ((mstpsr4 & (1 << 0)) == 0) /* CMT2 */ | |
404 | msk |= 1 << 1; | |
405 | ||
406 | if ((mstpsr2 & (1 << 13)) == 0) /* MFI_MFIS */ | |
407 | msk2 |= 1 << 17; | |
408 | ||
409 | *mskp = msk; | |
410 | *msk2p = msk2; | |
411 | ||
412 | return 1; | |
413 | } | |
414 | ||
415 | static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p) | |
416 | { | |
417 | u16 tmp, irqcr1, irqcr2; | |
418 | int k; | |
419 | ||
420 | irqcr1 = 0; | |
421 | irqcr2 = 0; | |
422 | ||
423 | /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */ | |
424 | for (k = 0; k <= 7; k++) { | |
425 | tmp = (icr >> ((7 - k) * 4)) & 0xf; | |
426 | irqcr1 |= (tmp & 0x03) << (k * 2); | |
427 | irqcr2 |= (tmp >> 2) << (k * 2); | |
428 | } | |
429 | ||
430 | *irqcr1p = irqcr1; | |
431 | *irqcr2p = irqcr2; | |
432 | } | |
433 | ||
f7dadb37 | 434 | static void sh7372_setup_sysc(unsigned long msk, unsigned long msk2) |
cf33835c MD |
435 | { |
436 | u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high; | |
437 | unsigned long tmp; | |
438 | ||
439 | /* read IRQ0A -> IRQ15A mask */ | |
440 | tmp = bitrev8(__raw_readb(INTMSK00A)); | |
441 | tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8; | |
442 | ||
443 | /* setup WUPSMSK from clocks and external IRQ mask */ | |
444 | msk = (~msk & 0xc030000f) | (tmp << 4); | |
445 | __raw_writel(msk, WUPSMSK); | |
446 | ||
447 | /* propage level/edge trigger for external IRQ 0->15 */ | |
448 | sh7372_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low); | |
449 | sh7372_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high); | |
450 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR); | |
451 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2); | |
452 | ||
453 | /* read IRQ16A -> IRQ31A mask */ | |
454 | tmp = bitrev8(__raw_readb(INTMSK20A)); | |
455 | tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8; | |
456 | ||
457 | /* setup WUPSMSK2 from clocks and external IRQ mask */ | |
458 | msk2 = (~msk2 & 0x00030000) | tmp; | |
459 | __raw_writel(msk2, WUPSMSK2); | |
460 | ||
461 | /* propage level/edge trigger for external IRQ 16->31 */ | |
462 | sh7372_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low); | |
463 | sh7372_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high); | |
464 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3); | |
465 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4); | |
466 | } | |
f7dadb37 MD |
467 | |
468 | static void sh7372_enter_a3sm_common(int pllc0_on) | |
469 | { | |
470 | sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc)); | |
471 | sh7372_enter_sysc(pllc0_on, 1 << 12); | |
472 | } | |
473 | ||
474 | static void sh7372_enter_a4s_common(int pllc0_on) | |
475 | { | |
476 | sh7372_intca_suspend(); | |
477 | memcpy((void *)SMFRAM, sh7372_resume_core_standby_sysc, 0x100); | |
478 | sh7372_set_reset_vector(SMFRAM); | |
479 | sh7372_enter_sysc(pllc0_on, 1 << 10); | |
480 | sh7372_intca_resume(); | |
481 | } | |
482 | ||
a0089bd6 | 483 | #endif |
cf33835c | 484 | |
082a8ca1 | 485 | #ifdef CONFIG_CPU_IDLE |
cf33835c | 486 | |
b73b5c49 | 487 | static void sh7372_cpuidle_setup(struct cpuidle_driver *drv) |
082a8ca1 | 488 | { |
b73b5c49 | 489 | struct cpuidle_state *state = &drv->states[drv->state_count]; |
082a8ca1 | 490 | |
082a8ca1 MD |
491 | snprintf(state->name, CPUIDLE_NAME_LEN, "C2"); |
492 | strncpy(state->desc, "Core Standby Mode", CPUIDLE_DESC_LEN); | |
493 | state->exit_latency = 10; | |
494 | state->target_residency = 20 + 10; | |
b73b5c49 MD |
495 | state->flags = CPUIDLE_FLAG_TIME_VALID; |
496 | shmobile_cpuidle_modes[drv->state_count] = sh7372_enter_core_standby; | |
082a8ca1 | 497 | |
b73b5c49 | 498 | drv->state_count++; |
082a8ca1 MD |
499 | } |
500 | ||
501 | static void sh7372_cpuidle_init(void) | |
502 | { | |
503 | shmobile_cpuidle_setup = sh7372_cpuidle_setup; | |
504 | } | |
505 | #else | |
506 | static void sh7372_cpuidle_init(void) {} | |
507 | #endif | |
508 | ||
509 | #ifdef CONFIG_SUSPEND | |
cf33835c | 510 | |
97991657 MD |
511 | static int sh7372_enter_suspend(suspend_state_t suspend_state) |
512 | { | |
cf33835c MD |
513 | unsigned long msk, msk2; |
514 | ||
515 | /* check active clocks to determine potential wakeup sources */ | |
f7dadb37 | 516 | if (sh7372_sysc_valid(&msk, &msk2)) { |
cf33835c | 517 | /* convert INTC mask and sense to SYSC mask and sense */ |
f7dadb37 MD |
518 | sh7372_setup_sysc(msk, msk2); |
519 | ||
767c0f3a | 520 | if (!console_suspend_enabled && |
f7dadb37 MD |
521 | sh7372_a4s.genpd.status == GPD_STATE_POWER_OFF) { |
522 | /* enter A4S sleep with PLLC0 off */ | |
523 | pr_debug("entering A4S\n"); | |
524 | sh7372_enter_a4s_common(0); | |
525 | } else { | |
526 | /* enter A3SM sleep with PLLC0 off */ | |
527 | pr_debug("entering A3SM\n"); | |
528 | sh7372_enter_a3sm_common(0); | |
529 | } | |
cf33835c MD |
530 | } else { |
531 | /* default to Core Standby that supports all wakeup sources */ | |
532 | pr_debug("entering Core Standby\n"); | |
533 | sh7372_enter_core_standby(); | |
534 | } | |
97991657 MD |
535 | return 0; |
536 | } | |
537 | ||
538 | static void sh7372_suspend_init(void) | |
539 | { | |
540 | shmobile_suspend_ops.enter = sh7372_enter_suspend; | |
541 | } | |
542 | #else | |
543 | static void sh7372_suspend_init(void) {} | |
544 | #endif | |
545 | ||
97991657 MD |
546 | void __init sh7372_pm_init(void) |
547 | { | |
548 | /* enable DBG hardware block to kick SYSC */ | |
549 | __raw_writel(0x0000a500, DBGREG9); | |
550 | __raw_writel(0x0000a501, DBGREG9); | |
551 | __raw_writel(0x00000000, DBGREG1); | |
552 | ||
d93f5cde MD |
553 | /* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */ |
554 | __raw_writel(0, PDNSEL); | |
555 | ||
97991657 | 556 | sh7372_suspend_init(); |
082a8ca1 | 557 | sh7372_cpuidle_init(); |
97991657 | 558 | } |