diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-08 16:10:57 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-08 16:10:57 -0500 |
commit | eb59c505f8a5906ad2e053d14fab50eb8574fd6f (patch) | |
tree | c6e875adc12b481b916e847e8f80b8881a0fb02c /arch/arm/mach-shmobile/pm-sh7372.c | |
parent | 1619ed8f60959829d070d8f39cd2f8ca0e7135ce (diff) | |
parent | c233523b3d392e530033a7587d7970dc62a02361 (diff) |
Merge branch 'pm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
* 'pm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (76 commits)
PM / Hibernate: Implement compat_ioctl for /dev/snapshot
PM / Freezer: fix return value of freezable_schedule_timeout_killable()
PM / shmobile: Allow the A4R domain to be turned off at run time
PM / input / touchscreen: Make st1232 use device PM QoS constraints
PM / QoS: Introduce dev_pm_qos_add_ancestor_request()
PM / shmobile: Remove the stay_on flag from SH7372's PM domains
PM / shmobile: Don't include SH7372's INTCS in syscore suspend/resume
PM / shmobile: Add support for the sh7372 A4S power domain / sleep mode
PM: Drop generic_subsys_pm_ops
PM / Sleep: Remove forward-only callbacks from AMBA bus type
PM / Sleep: Remove forward-only callbacks from platform bus type
PM: Run the driver callback directly if the subsystem one is not there
PM / Sleep: Make pm_op() and pm_noirq_op() return callback pointers
PM/Devfreq: Add Exynos4-bus device DVFS driver for Exynos4210/4212/4412.
PM / Sleep: Merge internal functions in generic_ops.c
PM / Sleep: Simplify generic system suspend callbacks
PM / Hibernate: Remove deprecated hibernation snapshot ioctls
PM / Sleep: Fix freezer failures due to racy usermodehelper_is_disabled()
ARM: S3C64XX: Implement basic power domain support
PM / shmobile: Use common always on power domain governor
...
Fix up trivial conflict in fs/xfs/xfs_buf.c due to removal of unused
XBT_FORCE_SLEEP bit
Diffstat (limited to 'arch/arm/mach-shmobile/pm-sh7372.c')
-rw-r--r-- | arch/arm/mach-shmobile/pm-sh7372.c | 196 |
1 files changed, 146 insertions, 50 deletions
diff --git a/arch/arm/mach-shmobile/pm-sh7372.c b/arch/arm/mach-shmobile/pm-sh7372.c index 34bbcbfb1706..77b8fc12fc2f 100644 --- a/arch/arm/mach-shmobile/pm-sh7372.c +++ b/arch/arm/mach-shmobile/pm-sh7372.c | |||
@@ -82,11 +82,12 @@ static int pd_power_down(struct generic_pm_domain *genpd) | |||
82 | struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd); | 82 | struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd); |
83 | unsigned int mask = 1 << sh7372_pd->bit_shift; | 83 | unsigned int mask = 1 << sh7372_pd->bit_shift; |
84 | 84 | ||
85 | if (sh7372_pd->suspend) | 85 | if (sh7372_pd->suspend) { |
86 | sh7372_pd->suspend(); | 86 | int ret = sh7372_pd->suspend(); |
87 | 87 | ||
88 | if (sh7372_pd->stay_on) | 88 | if (ret) |
89 | return 0; | 89 | return ret; |
90 | } | ||
90 | 91 | ||
91 | if (__raw_readl(PSTR) & mask) { | 92 | if (__raw_readl(PSTR) & mask) { |
92 | unsigned int retry_count; | 93 | unsigned int retry_count; |
@@ -101,8 +102,8 @@ static int pd_power_down(struct generic_pm_domain *genpd) | |||
101 | } | 102 | } |
102 | 103 | ||
103 | if (!sh7372_pd->no_debug) | 104 | if (!sh7372_pd->no_debug) |
104 | pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n", | 105 | pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", |
105 | mask, __raw_readl(PSTR)); | 106 | genpd->name, mask, __raw_readl(PSTR)); |
106 | 107 | ||
107 | return 0; | 108 | return 0; |
108 | } | 109 | } |
@@ -113,9 +114,6 @@ static int __pd_power_up(struct sh7372_pm_domain *sh7372_pd, bool do_resume) | |||
113 | unsigned int retry_count; | 114 | unsigned int retry_count; |
114 | int ret = 0; | 115 | int ret = 0; |
115 | 116 | ||
116 | if (sh7372_pd->stay_on) | ||
117 | goto out; | ||
118 | |||
119 | if (__raw_readl(PSTR) & mask) | 117 | if (__raw_readl(PSTR) & mask) |
120 | goto out; | 118 | goto out; |
121 | 119 | ||
@@ -133,8 +131,8 @@ static int __pd_power_up(struct sh7372_pm_domain *sh7372_pd, bool do_resume) | |||
133 | ret = -EIO; | 131 | ret = -EIO; |
134 | 132 | ||
135 | if (!sh7372_pd->no_debug) | 133 | if (!sh7372_pd->no_debug) |
136 | pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n", | 134 | pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n", |
137 | mask, __raw_readl(PSTR)); | 135 | sh7372_pd->genpd.name, mask, __raw_readl(PSTR)); |
138 | 136 | ||
139 | out: | 137 | out: |
140 | if (ret == 0 && sh7372_pd->resume && do_resume) | 138 | if (ret == 0 && sh7372_pd->resume && do_resume) |
@@ -148,35 +146,60 @@ static int pd_power_up(struct generic_pm_domain *genpd) | |||
148 | return __pd_power_up(to_sh7372_pd(genpd), true); | 146 | return __pd_power_up(to_sh7372_pd(genpd), true); |
149 | } | 147 | } |
150 | 148 | ||
151 | static void sh7372_a4r_suspend(void) | 149 | static int sh7372_a4r_suspend(void) |
152 | { | 150 | { |
153 | sh7372_intcs_suspend(); | 151 | sh7372_intcs_suspend(); |
154 | __raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */ | 152 | __raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */ |
153 | return 0; | ||
155 | } | 154 | } |
156 | 155 | ||
157 | static bool pd_active_wakeup(struct device *dev) | 156 | static bool pd_active_wakeup(struct device *dev) |
158 | { | 157 | { |
159 | return true; | 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; | ||
160 | } | 162 | } |
161 | 163 | ||
162 | static bool sh7372_power_down_forbidden(struct dev_pm_domain *domain) | 164 | static int sh7372_stop_dev(struct device *dev) |
163 | { | 165 | { |
164 | return false; | 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); | ||
165 | } | 175 | } |
166 | 176 | ||
167 | struct dev_power_governor sh7372_always_on_gov = { | 177 | static int sh7372_start_dev(struct device *dev) |
168 | .power_down_ok = sh7372_power_down_forbidden, | 178 | { |
169 | }; | 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 | } | ||
170 | 192 | ||
171 | void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) | 193 | void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) |
172 | { | 194 | { |
173 | struct generic_pm_domain *genpd = &sh7372_pd->genpd; | 195 | struct generic_pm_domain *genpd = &sh7372_pd->genpd; |
196 | struct dev_power_governor *gov = sh7372_pd->gov; | ||
174 | 197 | ||
175 | pm_genpd_init(genpd, sh7372_pd->gov, false); | 198 | pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); |
176 | genpd->stop_device = pm_clk_suspend; | 199 | genpd->dev_ops.stop = sh7372_stop_dev; |
177 | genpd->start_device = pm_clk_resume; | 200 | genpd->dev_ops.start = sh7372_start_dev; |
201 | genpd->dev_ops.active_wakeup = pd_active_wakeup; | ||
178 | genpd->dev_irq_safe = true; | 202 | genpd->dev_irq_safe = true; |
179 | genpd->active_wakeup = pd_active_wakeup; | ||
180 | genpd->power_off = pd_power_down; | 203 | genpd->power_off = pd_power_down; |
181 | genpd->power_on = pd_power_up; | 204 | genpd->power_on = pd_power_up; |
182 | __pd_power_up(sh7372_pd, false); | 205 | __pd_power_up(sh7372_pd, false); |
@@ -199,48 +222,73 @@ void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd, | |||
199 | } | 222 | } |
200 | 223 | ||
201 | struct sh7372_pm_domain sh7372_a4lc = { | 224 | struct sh7372_pm_domain sh7372_a4lc = { |
225 | .genpd.name = "A4LC", | ||
202 | .bit_shift = 1, | 226 | .bit_shift = 1, |
203 | }; | 227 | }; |
204 | 228 | ||
205 | struct sh7372_pm_domain sh7372_a4mp = { | 229 | struct sh7372_pm_domain sh7372_a4mp = { |
230 | .genpd.name = "A4MP", | ||
206 | .bit_shift = 2, | 231 | .bit_shift = 2, |
207 | }; | 232 | }; |
208 | 233 | ||
209 | struct sh7372_pm_domain sh7372_d4 = { | 234 | struct sh7372_pm_domain sh7372_d4 = { |
235 | .genpd.name = "D4", | ||
210 | .bit_shift = 3, | 236 | .bit_shift = 3, |
211 | }; | 237 | }; |
212 | 238 | ||
213 | struct sh7372_pm_domain sh7372_a4r = { | 239 | struct sh7372_pm_domain sh7372_a4r = { |
240 | .genpd.name = "A4R", | ||
214 | .bit_shift = 5, | 241 | .bit_shift = 5, |
215 | .gov = &sh7372_always_on_gov, | ||
216 | .suspend = sh7372_a4r_suspend, | 242 | .suspend = sh7372_a4r_suspend, |
217 | .resume = sh7372_intcs_resume, | 243 | .resume = sh7372_intcs_resume, |
218 | .stay_on = true, | ||
219 | }; | 244 | }; |
220 | 245 | ||
221 | struct sh7372_pm_domain sh7372_a3rv = { | 246 | struct sh7372_pm_domain sh7372_a3rv = { |
247 | .genpd.name = "A3RV", | ||
222 | .bit_shift = 6, | 248 | .bit_shift = 6, |
223 | }; | 249 | }; |
224 | 250 | ||
225 | struct sh7372_pm_domain sh7372_a3ri = { | 251 | struct sh7372_pm_domain sh7372_a3ri = { |
252 | .genpd.name = "A3RI", | ||
226 | .bit_shift = 8, | 253 | .bit_shift = 8, |
227 | }; | 254 | }; |
228 | 255 | ||
229 | struct sh7372_pm_domain sh7372_a3sp = { | 256 | static int sh7372_a4s_suspend(void) |
230 | .bit_shift = 11, | 257 | { |
231 | .gov = &sh7372_always_on_gov, | 258 | /* |
259 | * The A4S domain contains the CPU core and therefore it should | ||
260 | * only be turned off if the CPU is in use. | ||
261 | */ | ||
262 | return -EBUSY; | ||
263 | } | ||
264 | |||
265 | struct sh7372_pm_domain sh7372_a4s = { | ||
266 | .genpd.name = "A4S", | ||
267 | .bit_shift = 10, | ||
268 | .gov = &pm_domain_always_on_gov, | ||
232 | .no_debug = true, | 269 | .no_debug = true, |
270 | .suspend = sh7372_a4s_suspend, | ||
233 | }; | 271 | }; |
234 | 272 | ||
235 | static void sh7372_a3sp_init(void) | 273 | static int sh7372_a3sp_suspend(void) |
236 | { | 274 | { |
237 | /* serial consoles make use of SCIF hardware located in A3SP, | 275 | /* |
276 | * Serial consoles make use of SCIF hardware located in A3SP, | ||
238 | * keep such power domain on if "no_console_suspend" is set. | 277 | * keep such power domain on if "no_console_suspend" is set. |
239 | */ | 278 | */ |
240 | sh7372_a3sp.stay_on = !console_suspend_enabled; | 279 | return console_suspend_enabled ? -EBUSY : 0; |
241 | } | 280 | } |
242 | 281 | ||
282 | struct sh7372_pm_domain sh7372_a3sp = { | ||
283 | .genpd.name = "A3SP", | ||
284 | .bit_shift = 11, | ||
285 | .gov = &pm_domain_always_on_gov, | ||
286 | .no_debug = true, | ||
287 | .suspend = sh7372_a3sp_suspend, | ||
288 | }; | ||
289 | |||
243 | struct sh7372_pm_domain sh7372_a3sg = { | 290 | struct sh7372_pm_domain sh7372_a3sg = { |
291 | .genpd.name = "A3SG", | ||
244 | .bit_shift = 13, | 292 | .bit_shift = 13, |
245 | }; | 293 | }; |
246 | 294 | ||
@@ -257,11 +305,16 @@ static int sh7372_do_idle_core_standby(unsigned long unused) | |||
257 | return 0; | 305 | return 0; |
258 | } | 306 | } |
259 | 307 | ||
260 | static void sh7372_enter_core_standby(void) | 308 | static void sh7372_set_reset_vector(unsigned long address) |
261 | { | 309 | { |
262 | /* set reset vector, translate 4k */ | 310 | /* set reset vector, translate 4k */ |
263 | __raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); | 311 | __raw_writel(address, SBAR); |
264 | __raw_writel(0, APARMBAREA); | 312 | __raw_writel(0, APARMBAREA); |
313 | } | ||
314 | |||
315 | static void sh7372_enter_core_standby(void) | ||
316 | { | ||
317 | sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc)); | ||
265 | 318 | ||
266 | /* enter sleep mode with SYSTBCR to 0x10 */ | 319 | /* enter sleep mode with SYSTBCR to 0x10 */ |
267 | __raw_writel(0x10, SYSTBCR); | 320 | __raw_writel(0x10, SYSTBCR); |
@@ -274,27 +327,22 @@ static void sh7372_enter_core_standby(void) | |||
274 | #endif | 327 | #endif |
275 | 328 | ||
276 | #ifdef CONFIG_SUSPEND | 329 | #ifdef CONFIG_SUSPEND |
277 | static void sh7372_enter_a3sm_common(int pllc0_on) | 330 | static void sh7372_enter_sysc(int pllc0_on, unsigned long sleep_mode) |
278 | { | 331 | { |
279 | /* set reset vector, translate 4k */ | ||
280 | __raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); | ||
281 | __raw_writel(0, APARMBAREA); | ||
282 | |||
283 | if (pllc0_on) | 332 | if (pllc0_on) |
284 | __raw_writel(0, PLLC01STPCR); | 333 | __raw_writel(0, PLLC01STPCR); |
285 | else | 334 | else |
286 | __raw_writel(1 << 28, PLLC01STPCR); | 335 | __raw_writel(1 << 28, PLLC01STPCR); |
287 | 336 | ||
288 | __raw_writel(0, PDNSEL); /* power-down A3SM only, not A4S */ | ||
289 | __raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */ | 337 | __raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */ |
290 | cpu_suspend(0, sh7372_do_idle_a3sm); | 338 | cpu_suspend(sleep_mode, sh7372_do_idle_sysc); |
291 | __raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */ | 339 | __raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */ |
292 | 340 | ||
293 | /* disable reset vector translation */ | 341 | /* disable reset vector translation */ |
294 | __raw_writel(0, SBAR); | 342 | __raw_writel(0, SBAR); |
295 | } | 343 | } |
296 | 344 | ||
297 | static int sh7372_a3sm_valid(unsigned long *mskp, unsigned long *msk2p) | 345 | static int sh7372_sysc_valid(unsigned long *mskp, unsigned long *msk2p) |
298 | { | 346 | { |
299 | unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4; | 347 | unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4; |
300 | unsigned long msk, msk2; | 348 | unsigned long msk, msk2; |
@@ -382,7 +430,7 @@ static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p) | |||
382 | *irqcr2p = irqcr2; | 430 | *irqcr2p = irqcr2; |
383 | } | 431 | } |
384 | 432 | ||
385 | static void sh7372_setup_a3sm(unsigned long msk, unsigned long msk2) | 433 | static void sh7372_setup_sysc(unsigned long msk, unsigned long msk2) |
386 | { | 434 | { |
387 | u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high; | 435 | u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high; |
388 | unsigned long tmp; | 436 | unsigned long tmp; |
@@ -415,6 +463,22 @@ static void sh7372_setup_a3sm(unsigned long msk, unsigned long msk2) | |||
415 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3); | 463 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3); |
416 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4); | 464 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4); |
417 | } | 465 | } |
466 | |||
467 | static void sh7372_enter_a3sm_common(int pllc0_on) | ||
468 | { | ||
469 | sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc)); | ||
470 | sh7372_enter_sysc(pllc0_on, 1 << 12); | ||
471 | } | ||
472 | |||
473 | static void sh7372_enter_a4s_common(int pllc0_on) | ||
474 | { | ||
475 | sh7372_intca_suspend(); | ||
476 | memcpy((void *)SMFRAM, sh7372_resume_core_standby_sysc, 0x100); | ||
477 | sh7372_set_reset_vector(SMFRAM); | ||
478 | sh7372_enter_sysc(pllc0_on, 1 << 10); | ||
479 | sh7372_intca_resume(); | ||
480 | } | ||
481 | |||
418 | #endif | 482 | #endif |
419 | 483 | ||
420 | #ifdef CONFIG_CPU_IDLE | 484 | #ifdef CONFIG_CPU_IDLE |
@@ -448,14 +512,20 @@ static int sh7372_enter_suspend(suspend_state_t suspend_state) | |||
448 | unsigned long msk, msk2; | 512 | unsigned long msk, msk2; |
449 | 513 | ||
450 | /* check active clocks to determine potential wakeup sources */ | 514 | /* check active clocks to determine potential wakeup sources */ |
451 | if (sh7372_a3sm_valid(&msk, &msk2)) { | 515 | if (sh7372_sysc_valid(&msk, &msk2)) { |
452 | |||
453 | /* convert INTC mask and sense to SYSC mask and sense */ | 516 | /* convert INTC mask and sense to SYSC mask and sense */ |
454 | sh7372_setup_a3sm(msk, msk2); | 517 | sh7372_setup_sysc(msk, msk2); |
455 | 518 | ||
456 | /* enter A3SM sleep with PLLC0 off */ | 519 | if (!console_suspend_enabled && |
457 | pr_debug("entering A3SM\n"); | 520 | sh7372_a4s.genpd.status == GPD_STATE_POWER_OFF) { |
458 | sh7372_enter_a3sm_common(0); | 521 | /* enter A4S sleep with PLLC0 off */ |
522 | pr_debug("entering A4S\n"); | ||
523 | sh7372_enter_a4s_common(0); | ||
524 | } else { | ||
525 | /* enter A3SM sleep with PLLC0 off */ | ||
526 | pr_debug("entering A3SM\n"); | ||
527 | sh7372_enter_a3sm_common(0); | ||
528 | } | ||
459 | } else { | 529 | } else { |
460 | /* default to Core Standby that supports all wakeup sources */ | 530 | /* default to Core Standby that supports all wakeup sources */ |
461 | pr_debug("entering Core Standby\n"); | 531 | pr_debug("entering Core Standby\n"); |
@@ -464,9 +534,37 @@ static int sh7372_enter_suspend(suspend_state_t suspend_state) | |||
464 | return 0; | 534 | return 0; |
465 | } | 535 | } |
466 | 536 | ||
537 | /** | ||
538 | * sh7372_pm_notifier_fn - SH7372 PM notifier routine. | ||
539 | * @notifier: Unused. | ||
540 | * @pm_event: Event being handled. | ||
541 | * @unused: Unused. | ||
542 | */ | ||
543 | static int sh7372_pm_notifier_fn(struct notifier_block *notifier, | ||
544 | unsigned long pm_event, void *unused) | ||
545 | { | ||
546 | switch (pm_event) { | ||
547 | case PM_SUSPEND_PREPARE: | ||
548 | /* | ||
549 | * This is necessary, because the A4R domain has to be "on" | ||
550 | * when suspend_device_irqs() and resume_device_irqs() are | ||
551 | * executed during system suspend and resume, respectively, so | ||
552 | * that those functions don't crash while accessing the INTCS. | ||
553 | */ | ||
554 | pm_genpd_poweron(&sh7372_a4r.genpd); | ||
555 | break; | ||
556 | case PM_POST_SUSPEND: | ||
557 | pm_genpd_poweroff_unused(); | ||
558 | break; | ||
559 | } | ||
560 | |||
561 | return NOTIFY_DONE; | ||
562 | } | ||
563 | |||
467 | static void sh7372_suspend_init(void) | 564 | static void sh7372_suspend_init(void) |
468 | { | 565 | { |
469 | shmobile_suspend_ops.enter = sh7372_enter_suspend; | 566 | shmobile_suspend_ops.enter = sh7372_enter_suspend; |
567 | pm_notifier(sh7372_pm_notifier_fn, 0); | ||
470 | } | 568 | } |
471 | #else | 569 | #else |
472 | static void sh7372_suspend_init(void) {} | 570 | static void sh7372_suspend_init(void) {} |
@@ -482,8 +580,6 @@ void __init sh7372_pm_init(void) | |||
482 | /* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */ | 580 | /* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */ |
483 | __raw_writel(0, PDNSEL); | 581 | __raw_writel(0, PDNSEL); |
484 | 582 | ||
485 | sh7372_a3sp_init(); | ||
486 | |||
487 | sh7372_suspend_init(); | 583 | sh7372_suspend_init(); |
488 | sh7372_cpuidle_init(); | 584 | sh7372_cpuidle_init(); |
489 | } | 585 | } |