diff options
author | Olof Johansson <olof@lixom.net> | 2018-05-25 17:13:22 -0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2018-05-25 17:13:22 -0400 |
commit | d900f5ce12f96c608153d7cc954631c8c03a0d27 (patch) | |
tree | 04fa09859af607248ead90cfa277acd89ee7be7f | |
parent | 280b0471fd2c6823e486919bef01e20c7c503297 (diff) | |
parent | 3bb3799cd4233b7c24622ae8c41455fb27a55c0f (diff) |
Merge tag 'omap-for-v4.18/soc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/soc
SoC changes for omap variants for v4.18 merge window
This series mostly adds saving of power and clock domain registers for
am335x/am437x suspend to RTC only mode. There is also a non-urgent fix
for omap4 PM where we could end up losing GPIO interrupts if bootloader
has LOGICRETSTATE cleared for domains. And there is a clean-up patch for
omap1 to use device properties for at24 eeprom.
* tag 'omap-for-v4.18/soc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
ARM: OMAP2+: Make sure LOGICRETSTATE bits are not cleared
ARM: OMAP2+: prm44xx: Inroduce cpu_pm notifiers for context save/restore
ARM: OMAP2+: prm44xx: Introduce context save/restore for am43 PRCM IO
ARM: OMAP2+: powerdomain: Introduce cpu_pm notifiers for context save/restore
ARM: OMAP2+: Add functions to save and restore powerdomain context
ARM: OMAP2+: clockdomain: Inroduce cpu_pm notifiers for context save/restore
ARM: OMAP2+: Add functions to save and restore clockdomain context en-masse.
ARM: omap1: osk: use device properties for at24 eeprom
Signed-off-by: Olof Johansson <olof@lixom.net>
-rw-r--r-- | arch/arm/mach-omap1/board-osk.c | 10 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.c | 73 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.h | 8 | ||||
-rw-r--r-- | arch/arm/mach-omap2/cm33xx.c | 53 | ||||
-rw-r--r-- | arch/arm/mach-omap2/cminst44xx.c | 43 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm44xx.c | 13 | ||||
-rw-r--r-- | arch/arm/mach-omap2/powerdomain.c | 87 | ||||
-rw-r--r-- | arch/arm/mach-omap2/powerdomain.h | 7 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm33xx.c | 31 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm44xx.c | 104 |
10 files changed, 424 insertions, 5 deletions
diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index c66372ed29e2..9ffa8d755a59 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c | |||
@@ -303,22 +303,22 @@ static const struct omap_lcd_config osk_lcd_config __initconst = { | |||
303 | #ifdef CONFIG_OMAP_OSK_MISTRAL | 303 | #ifdef CONFIG_OMAP_OSK_MISTRAL |
304 | 304 | ||
305 | #include <linux/input.h> | 305 | #include <linux/input.h> |
306 | #include <linux/platform_data/at24.h> | 306 | #include <linux/property.h> |
307 | #include <linux/spi/spi.h> | 307 | #include <linux/spi/spi.h> |
308 | #include <linux/spi/ads7846.h> | 308 | #include <linux/spi/ads7846.h> |
309 | 309 | ||
310 | #include <linux/platform_data/keypad-omap.h> | 310 | #include <linux/platform_data/keypad-omap.h> |
311 | 311 | ||
312 | static struct at24_platform_data at24c04 = { | 312 | static const struct property_entry mistral_at24_properties[] = { |
313 | .byte_len = SZ_4K / 8, | 313 | PROPERTY_ENTRY_U32("pagesize", 16), |
314 | .page_size = 16, | 314 | { } |
315 | }; | 315 | }; |
316 | 316 | ||
317 | static struct i2c_board_info __initdata mistral_i2c_board_info[] = { | 317 | static struct i2c_board_info __initdata mistral_i2c_board_info[] = { |
318 | { | 318 | { |
319 | /* NOTE: powered from LCD supply */ | 319 | /* NOTE: powered from LCD supply */ |
320 | I2C_BOARD_INFO("24c04", 0x50), | 320 | I2C_BOARD_INFO("24c04", 0x50), |
321 | .platform_data = &at24c04, | 321 | .properties = mistral_at24_properties, |
322 | }, | 322 | }, |
323 | /* TODO when driver support is ready: | 323 | /* TODO when driver support is ready: |
324 | * - optionally ov9640 camera sensor at 0x30 | 324 | * - optionally ov9640 camera sensor at 0x30 |
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index b79b1ca9aee9..6d44fe05a3fe 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/limits.h> | 23 | #include <linux/limits.h> |
24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/clk-provider.h> | 25 | #include <linux/clk-provider.h> |
26 | #include <linux/cpu_pm.h> | ||
26 | 27 | ||
27 | #include <linux/io.h> | 28 | #include <linux/io.h> |
28 | 29 | ||
@@ -31,6 +32,7 @@ | |||
31 | #include "soc.h" | 32 | #include "soc.h" |
32 | #include "clock.h" | 33 | #include "clock.h" |
33 | #include "clockdomain.h" | 34 | #include "clockdomain.h" |
35 | #include "pm.h" | ||
34 | 36 | ||
35 | /* clkdm_list contains all registered struct clockdomains */ | 37 | /* clkdm_list contains all registered struct clockdomains */ |
36 | static LIST_HEAD(clkdm_list); | 38 | static LIST_HEAD(clkdm_list); |
@@ -39,6 +41,8 @@ static LIST_HEAD(clkdm_list); | |||
39 | static struct clkdm_autodep *autodeps; | 41 | static struct clkdm_autodep *autodeps; |
40 | 42 | ||
41 | static struct clkdm_ops *arch_clkdm; | 43 | static struct clkdm_ops *arch_clkdm; |
44 | void clkdm_save_context(void); | ||
45 | void clkdm_restore_context(void); | ||
42 | 46 | ||
43 | /* Private functions */ | 47 | /* Private functions */ |
44 | 48 | ||
@@ -449,6 +453,22 @@ int clkdm_register_autodeps(struct clkdm_autodep *ia) | |||
449 | return 0; | 453 | return 0; |
450 | } | 454 | } |
451 | 455 | ||
456 | static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) | ||
457 | { | ||
458 | switch (cmd) { | ||
459 | case CPU_CLUSTER_PM_ENTER: | ||
460 | if (enable_off_mode) | ||
461 | clkdm_save_context(); | ||
462 | break; | ||
463 | case CPU_CLUSTER_PM_EXIT: | ||
464 | if (enable_off_mode) | ||
465 | clkdm_restore_context(); | ||
466 | break; | ||
467 | } | ||
468 | |||
469 | return NOTIFY_OK; | ||
470 | } | ||
471 | |||
452 | /** | 472 | /** |
453 | * clkdm_complete_init - set up the clockdomain layer | 473 | * clkdm_complete_init - set up the clockdomain layer |
454 | * | 474 | * |
@@ -460,6 +480,7 @@ int clkdm_register_autodeps(struct clkdm_autodep *ia) | |||
460 | int clkdm_complete_init(void) | 480 | int clkdm_complete_init(void) |
461 | { | 481 | { |
462 | struct clockdomain *clkdm; | 482 | struct clockdomain *clkdm; |
483 | static struct notifier_block nb; | ||
463 | 484 | ||
464 | if (list_empty(&clkdm_list)) | 485 | if (list_empty(&clkdm_list)) |
465 | return -EACCES; | 486 | return -EACCES; |
@@ -474,6 +495,12 @@ int clkdm_complete_init(void) | |||
474 | clkdm_clear_all_sleepdeps(clkdm); | 495 | clkdm_clear_all_sleepdeps(clkdm); |
475 | } | 496 | } |
476 | 497 | ||
498 | /* Only AM43XX can lose clkdm context during rtc-ddr suspend */ | ||
499 | if (soc_is_am43xx()) { | ||
500 | nb.notifier_call = cpu_notifier; | ||
501 | cpu_pm_register_notifier(&nb); | ||
502 | } | ||
503 | |||
477 | return 0; | 504 | return 0; |
478 | } | 505 | } |
479 | 506 | ||
@@ -1307,3 +1334,49 @@ int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) | |||
1307 | return 0; | 1334 | return 0; |
1308 | } | 1335 | } |
1309 | 1336 | ||
1337 | /** | ||
1338 | * _clkdm_save_context - save the context for the control of this clkdm | ||
1339 | * | ||
1340 | * Due to a suspend or hibernation operation, the state of the registers | ||
1341 | * controlling this clkdm will be lost, save their context. | ||
1342 | */ | ||
1343 | static int _clkdm_save_context(struct clockdomain *clkdm, void *ununsed) | ||
1344 | { | ||
1345 | if (!arch_clkdm || !arch_clkdm->clkdm_save_context) | ||
1346 | return -EINVAL; | ||
1347 | |||
1348 | return arch_clkdm->clkdm_save_context(clkdm); | ||
1349 | } | ||
1350 | |||
1351 | /** | ||
1352 | * _clkdm_restore_context - restore context for control of this clkdm | ||
1353 | * | ||
1354 | * Restore the register values for this clockdomain. | ||
1355 | */ | ||
1356 | static int _clkdm_restore_context(struct clockdomain *clkdm, void *ununsed) | ||
1357 | { | ||
1358 | if (!arch_clkdm || !arch_clkdm->clkdm_restore_context) | ||
1359 | return -EINVAL; | ||
1360 | |||
1361 | return arch_clkdm->clkdm_restore_context(clkdm); | ||
1362 | } | ||
1363 | |||
1364 | /** | ||
1365 | * clkdm_save_context - Saves the context for each registered clkdm | ||
1366 | * | ||
1367 | * Save the context for each registered clockdomain. | ||
1368 | */ | ||
1369 | void clkdm_save_context(void) | ||
1370 | { | ||
1371 | clkdm_for_each(_clkdm_save_context, NULL); | ||
1372 | } | ||
1373 | |||
1374 | /** | ||
1375 | * clkdm_restore_context - Restores the context for each registered clkdm | ||
1376 | * | ||
1377 | * Restore the context for each registered clockdomain. | ||
1378 | */ | ||
1379 | void clkdm_restore_context(void) | ||
1380 | { | ||
1381 | clkdm_for_each(_clkdm_restore_context, NULL); | ||
1382 | } | ||
diff --git a/arch/arm/mach-omap2/clockdomain.h b/arch/arm/mach-omap2/clockdomain.h index 24667a5a9dc0..c7d0953e4aa2 100644 --- a/arch/arm/mach-omap2/clockdomain.h +++ b/arch/arm/mach-omap2/clockdomain.h | |||
@@ -141,6 +141,7 @@ struct clockdomain { | |||
141 | int usecount; | 141 | int usecount; |
142 | int forcewake_count; | 142 | int forcewake_count; |
143 | struct list_head node; | 143 | struct list_head node; |
144 | u32 context; | ||
144 | }; | 145 | }; |
145 | 146 | ||
146 | /** | 147 | /** |
@@ -159,6 +160,8 @@ struct clockdomain { | |||
159 | * @clkdm_deny_idle: Disable hw supervised idle transitions for clock domain | 160 | * @clkdm_deny_idle: Disable hw supervised idle transitions for clock domain |
160 | * @clkdm_clk_enable: Put the clkdm in right state for a clock enable | 161 | * @clkdm_clk_enable: Put the clkdm in right state for a clock enable |
161 | * @clkdm_clk_disable: Put the clkdm in right state for a clock disable | 162 | * @clkdm_clk_disable: Put the clkdm in right state for a clock disable |
163 | * @clkdm_save_context: Save the current clkdm context | ||
164 | * @clkdm_restore_context: Restore the clkdm context | ||
162 | */ | 165 | */ |
163 | struct clkdm_ops { | 166 | struct clkdm_ops { |
164 | int (*clkdm_add_wkdep)(struct clockdomain *clkdm1, struct clockdomain *clkdm2); | 167 | int (*clkdm_add_wkdep)(struct clockdomain *clkdm1, struct clockdomain *clkdm2); |
@@ -175,6 +178,8 @@ struct clkdm_ops { | |||
175 | void (*clkdm_deny_idle)(struct clockdomain *clkdm); | 178 | void (*clkdm_deny_idle)(struct clockdomain *clkdm); |
176 | int (*clkdm_clk_enable)(struct clockdomain *clkdm); | 179 | int (*clkdm_clk_enable)(struct clockdomain *clkdm); |
177 | int (*clkdm_clk_disable)(struct clockdomain *clkdm); | 180 | int (*clkdm_clk_disable)(struct clockdomain *clkdm); |
181 | int (*clkdm_save_context)(struct clockdomain *clkdm); | ||
182 | int (*clkdm_restore_context)(struct clockdomain *clkdm); | ||
178 | }; | 183 | }; |
179 | 184 | ||
180 | int clkdm_register_platform_funcs(struct clkdm_ops *co); | 185 | int clkdm_register_platform_funcs(struct clkdm_ops *co); |
@@ -214,6 +219,9 @@ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk); | |||
214 | int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh); | 219 | int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh); |
215 | int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh); | 220 | int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh); |
216 | 221 | ||
222 | void clkdm_save_context(void); | ||
223 | void clkdm_restore_context(void); | ||
224 | |||
217 | extern void __init omap242x_clockdomains_init(void); | 225 | extern void __init omap242x_clockdomains_init(void); |
218 | extern void __init omap243x_clockdomains_init(void); | 226 | extern void __init omap243x_clockdomains_init(void); |
219 | extern void __init omap3xxx_clockdomains_init(void); | 227 | extern void __init omap3xxx_clockdomains_init(void); |
diff --git a/arch/arm/mach-omap2/cm33xx.c b/arch/arm/mach-omap2/cm33xx.c index 1cc0247a2cb5..084d454f6074 100644 --- a/arch/arm/mach-omap2/cm33xx.c +++ b/arch/arm/mach-omap2/cm33xx.c | |||
@@ -72,6 +72,17 @@ static inline u32 am33xx_cm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx) | |||
72 | return v; | 72 | return v; |
73 | } | 73 | } |
74 | 74 | ||
75 | static inline u32 am33xx_cm_read_reg_bits(u16 inst, s16 idx, u32 mask) | ||
76 | { | ||
77 | u32 v; | ||
78 | |||
79 | v = am33xx_cm_read_reg(inst, idx); | ||
80 | v &= mask; | ||
81 | v >>= __ffs(mask); | ||
82 | |||
83 | return v; | ||
84 | } | ||
85 | |||
75 | /** | 86 | /** |
76 | * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield | 87 | * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield |
77 | * @inst: CM instance register offset (*_INST macro) | 88 | * @inst: CM instance register offset (*_INST macro) |
@@ -338,6 +349,46 @@ static u32 am33xx_cm_xlate_clkctrl(u8 part, u16 inst, u16 offset) | |||
338 | return cm_base.pa + inst + offset; | 349 | return cm_base.pa + inst + offset; |
339 | } | 350 | } |
340 | 351 | ||
352 | /** | ||
353 | * am33xx_clkdm_save_context - Save the clockdomain transition context | ||
354 | * @clkdm: The clockdomain pointer whose context needs to be saved | ||
355 | * | ||
356 | * Save the clockdomain transition context. | ||
357 | */ | ||
358 | static int am33xx_clkdm_save_context(struct clockdomain *clkdm) | ||
359 | { | ||
360 | clkdm->context = am33xx_cm_read_reg_bits(clkdm->cm_inst, | ||
361 | clkdm->clkdm_offs, | ||
362 | AM33XX_CLKTRCTRL_MASK); | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | /** | ||
368 | * am33xx_restore_save_context - Restore the clockdomain transition context | ||
369 | * @clkdm: The clockdomain pointer whose context needs to be restored | ||
370 | * | ||
371 | * Restore the clockdomain transition context. | ||
372 | */ | ||
373 | static int am33xx_clkdm_restore_context(struct clockdomain *clkdm) | ||
374 | { | ||
375 | switch (clkdm->context) { | ||
376 | case OMAP34XX_CLKSTCTRL_DISABLE_AUTO: | ||
377 | am33xx_clkdm_deny_idle(clkdm); | ||
378 | break; | ||
379 | case OMAP34XX_CLKSTCTRL_FORCE_SLEEP: | ||
380 | am33xx_clkdm_sleep(clkdm); | ||
381 | break; | ||
382 | case OMAP34XX_CLKSTCTRL_FORCE_WAKEUP: | ||
383 | am33xx_clkdm_wakeup(clkdm); | ||
384 | break; | ||
385 | case OMAP34XX_CLKSTCTRL_ENABLE_AUTO: | ||
386 | am33xx_clkdm_allow_idle(clkdm); | ||
387 | break; | ||
388 | } | ||
389 | return 0; | ||
390 | } | ||
391 | |||
341 | struct clkdm_ops am33xx_clkdm_operations = { | 392 | struct clkdm_ops am33xx_clkdm_operations = { |
342 | .clkdm_sleep = am33xx_clkdm_sleep, | 393 | .clkdm_sleep = am33xx_clkdm_sleep, |
343 | .clkdm_wakeup = am33xx_clkdm_wakeup, | 394 | .clkdm_wakeup = am33xx_clkdm_wakeup, |
@@ -345,6 +396,8 @@ struct clkdm_ops am33xx_clkdm_operations = { | |||
345 | .clkdm_deny_idle = am33xx_clkdm_deny_idle, | 396 | .clkdm_deny_idle = am33xx_clkdm_deny_idle, |
346 | .clkdm_clk_enable = am33xx_clkdm_clk_enable, | 397 | .clkdm_clk_enable = am33xx_clkdm_clk_enable, |
347 | .clkdm_clk_disable = am33xx_clkdm_clk_disable, | 398 | .clkdm_clk_disable = am33xx_clkdm_clk_disable, |
399 | .clkdm_save_context = am33xx_clkdm_save_context, | ||
400 | .clkdm_restore_context = am33xx_clkdm_restore_context, | ||
348 | }; | 401 | }; |
349 | 402 | ||
350 | static const struct cm_ll_data am33xx_cm_ll_data = { | 403 | static const struct cm_ll_data am33xx_cm_ll_data = { |
diff --git a/arch/arm/mach-omap2/cminst44xx.c b/arch/arm/mach-omap2/cminst44xx.c index 7deefee49fc3..c11ac492b626 100644 --- a/arch/arm/mach-omap2/cminst44xx.c +++ b/arch/arm/mach-omap2/cminst44xx.c | |||
@@ -481,6 +481,47 @@ static u32 omap4_cminst_xlate_clkctrl(u8 part, u16 inst, u16 offset) | |||
481 | return _cm_bases[part].pa + inst + offset; | 481 | return _cm_bases[part].pa + inst + offset; |
482 | } | 482 | } |
483 | 483 | ||
484 | /** | ||
485 | * omap4_clkdm_save_context - Save the clockdomain modulemode context | ||
486 | * @clkdm: The clockdomain pointer whose context needs to be saved | ||
487 | * | ||
488 | * Save the clockdomain modulemode context. | ||
489 | */ | ||
490 | static int omap4_clkdm_save_context(struct clockdomain *clkdm) | ||
491 | { | ||
492 | clkdm->context = omap4_cminst_read_inst_reg(clkdm->prcm_partition, | ||
493 | clkdm->cm_inst, | ||
494 | clkdm->clkdm_offs + | ||
495 | OMAP4_CM_CLKSTCTRL); | ||
496 | clkdm->context &= OMAP4430_MODULEMODE_MASK; | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | /** | ||
501 | * omap4_clkdm_restore_context - Restore the clockdomain modulemode context | ||
502 | * @clkdm: The clockdomain pointer whose context needs to be restored | ||
503 | * | ||
504 | * Restore the clockdomain modulemode context. | ||
505 | */ | ||
506 | static int omap4_clkdm_restore_context(struct clockdomain *clkdm) | ||
507 | { | ||
508 | switch (clkdm->context) { | ||
509 | case OMAP34XX_CLKSTCTRL_DISABLE_AUTO: | ||
510 | omap4_clkdm_deny_idle(clkdm); | ||
511 | break; | ||
512 | case OMAP34XX_CLKSTCTRL_FORCE_SLEEP: | ||
513 | omap4_clkdm_sleep(clkdm); | ||
514 | break; | ||
515 | case OMAP34XX_CLKSTCTRL_FORCE_WAKEUP: | ||
516 | omap4_clkdm_wakeup(clkdm); | ||
517 | break; | ||
518 | case OMAP34XX_CLKSTCTRL_ENABLE_AUTO: | ||
519 | omap4_clkdm_allow_idle(clkdm); | ||
520 | break; | ||
521 | } | ||
522 | return 0; | ||
523 | } | ||
524 | |||
484 | struct clkdm_ops omap4_clkdm_operations = { | 525 | struct clkdm_ops omap4_clkdm_operations = { |
485 | .clkdm_add_wkdep = omap4_clkdm_add_wkup_sleep_dep, | 526 | .clkdm_add_wkdep = omap4_clkdm_add_wkup_sleep_dep, |
486 | .clkdm_del_wkdep = omap4_clkdm_del_wkup_sleep_dep, | 527 | .clkdm_del_wkdep = omap4_clkdm_del_wkup_sleep_dep, |
@@ -496,6 +537,8 @@ struct clkdm_ops omap4_clkdm_operations = { | |||
496 | .clkdm_deny_idle = omap4_clkdm_deny_idle, | 537 | .clkdm_deny_idle = omap4_clkdm_deny_idle, |
497 | .clkdm_clk_enable = omap4_clkdm_clk_enable, | 538 | .clkdm_clk_enable = omap4_clkdm_clk_enable, |
498 | .clkdm_clk_disable = omap4_clkdm_clk_disable, | 539 | .clkdm_clk_disable = omap4_clkdm_clk_disable, |
540 | .clkdm_save_context = omap4_clkdm_save_context, | ||
541 | .clkdm_restore_context = omap4_clkdm_restore_context, | ||
499 | }; | 542 | }; |
500 | 543 | ||
501 | struct clkdm_ops am43xx_clkdm_operations = { | 544 | struct clkdm_ops am43xx_clkdm_operations = { |
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c index b3870220612e..78e1ace7d17d 100644 --- a/arch/arm/mach-omap2/pm44xx.c +++ b/arch/arm/mach-omap2/pm44xx.c | |||
@@ -131,6 +131,19 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) | |||
131 | return 0; | 131 | return 0; |
132 | } | 132 | } |
133 | 133 | ||
134 | /* | ||
135 | * Bootloader or kexec boot may have LOGICRETSTATE cleared | ||
136 | * for some domains. This is the case when kexec booting from | ||
137 | * Android kernels that support off mode for example. | ||
138 | * Make sure it's set at least for core and per, otherwise | ||
139 | * we currently will see lost GPIO interrupts for wlcore and | ||
140 | * smsc911x at least if per hits retention during idle. | ||
141 | */ | ||
142 | if (!strncmp(pwrdm->name, "core", 4) || | ||
143 | !strncmp(pwrdm->name, "l4per", 5) || | ||
144 | !strncmp(pwrdm->name, "wkup", 4)) | ||
145 | pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET); | ||
146 | |||
134 | pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC); | 147 | pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC); |
135 | if (!pwrst) | 148 | if (!pwrst) |
136 | return -ENOMEM; | 149 | return -ENOMEM; |
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index 76eb6ec5f157..27fdef624e97 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c | |||
@@ -14,6 +14,7 @@ | |||
14 | */ | 14 | */ |
15 | #undef DEBUG | 15 | #undef DEBUG |
16 | 16 | ||
17 | #include <linux/cpu_pm.h> | ||
17 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
18 | #include <linux/types.h> | 19 | #include <linux/types.h> |
19 | #include <linux/list.h> | 20 | #include <linux/list.h> |
@@ -39,6 +40,9 @@ | |||
39 | 40 | ||
40 | #define PWRDM_TRACE_STATES_FLAG (1<<31) | 41 | #define PWRDM_TRACE_STATES_FLAG (1<<31) |
41 | 42 | ||
43 | void pwrdms_save_context(void); | ||
44 | void pwrdms_restore_context(void); | ||
45 | |||
42 | enum { | 46 | enum { |
43 | PWRDM_STATE_NOW = 0, | 47 | PWRDM_STATE_NOW = 0, |
44 | PWRDM_STATE_PREV, | 48 | PWRDM_STATE_PREV, |
@@ -333,6 +337,22 @@ int pwrdm_register_pwrdms(struct powerdomain **ps) | |||
333 | return 0; | 337 | return 0; |
334 | } | 338 | } |
335 | 339 | ||
340 | static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) | ||
341 | { | ||
342 | switch (cmd) { | ||
343 | case CPU_CLUSTER_PM_ENTER: | ||
344 | if (enable_off_mode) | ||
345 | pwrdms_save_context(); | ||
346 | break; | ||
347 | case CPU_CLUSTER_PM_EXIT: | ||
348 | if (enable_off_mode) | ||
349 | pwrdms_restore_context(); | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | return NOTIFY_OK; | ||
354 | } | ||
355 | |||
336 | /** | 356 | /** |
337 | * pwrdm_complete_init - set up the powerdomain layer | 357 | * pwrdm_complete_init - set up the powerdomain layer |
338 | * | 358 | * |
@@ -347,6 +367,7 @@ int pwrdm_register_pwrdms(struct powerdomain **ps) | |||
347 | int pwrdm_complete_init(void) | 367 | int pwrdm_complete_init(void) |
348 | { | 368 | { |
349 | struct powerdomain *temp_p; | 369 | struct powerdomain *temp_p; |
370 | static struct notifier_block nb; | ||
350 | 371 | ||
351 | if (list_empty(&pwrdm_list)) | 372 | if (list_empty(&pwrdm_list)) |
352 | return -EACCES; | 373 | return -EACCES; |
@@ -354,6 +375,12 @@ int pwrdm_complete_init(void) | |||
354 | list_for_each_entry(temp_p, &pwrdm_list, node) | 375 | list_for_each_entry(temp_p, &pwrdm_list, node) |
355 | pwrdm_set_next_pwrst(temp_p, PWRDM_POWER_ON); | 376 | pwrdm_set_next_pwrst(temp_p, PWRDM_POWER_ON); |
356 | 377 | ||
378 | /* Only AM43XX can lose pwrdm context during rtc-ddr suspend */ | ||
379 | if (soc_is_am43xx()) { | ||
380 | nb.notifier_call = cpu_notifier; | ||
381 | cpu_pm_register_notifier(&nb); | ||
382 | } | ||
383 | |||
357 | return 0; | 384 | return 0; |
358 | } | 385 | } |
359 | 386 | ||
@@ -1199,3 +1226,63 @@ bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm) | |||
1199 | 1226 | ||
1200 | return 0; | 1227 | return 0; |
1201 | } | 1228 | } |
1229 | |||
1230 | /** | ||
1231 | * pwrdm_save_context - save powerdomain registers | ||
1232 | * | ||
1233 | * Register state is going to be lost due to a suspend or hibernate | ||
1234 | * event. Save the powerdomain registers. | ||
1235 | */ | ||
1236 | static int pwrdm_save_context(struct powerdomain *pwrdm, void *unused) | ||
1237 | { | ||
1238 | if (arch_pwrdm && arch_pwrdm->pwrdm_save_context) | ||
1239 | arch_pwrdm->pwrdm_save_context(pwrdm); | ||
1240 | return 0; | ||
1241 | } | ||
1242 | |||
1243 | /** | ||
1244 | * pwrdm_save_context - restore powerdomain registers | ||
1245 | * | ||
1246 | * Restore powerdomain control registers after a suspend or resume | ||
1247 | * event. | ||
1248 | */ | ||
1249 | static int pwrdm_restore_context(struct powerdomain *pwrdm, void *unused) | ||
1250 | { | ||
1251 | if (arch_pwrdm && arch_pwrdm->pwrdm_restore_context) | ||
1252 | arch_pwrdm->pwrdm_restore_context(pwrdm); | ||
1253 | return 0; | ||
1254 | } | ||
1255 | |||
1256 | static int pwrdm_lost_power(struct powerdomain *pwrdm, void *unused) | ||
1257 | { | ||
1258 | int state; | ||
1259 | |||
1260 | /* | ||
1261 | * Power has been lost across all powerdomains, increment the | ||
1262 | * counter. | ||
1263 | */ | ||
1264 | |||
1265 | state = pwrdm_read_pwrst(pwrdm); | ||
1266 | if (state != PWRDM_POWER_OFF) { | ||
1267 | pwrdm->state_counter[state]++; | ||
1268 | pwrdm->state_counter[PWRDM_POWER_OFF]++; | ||
1269 | } | ||
1270 | pwrdm->state = state; | ||
1271 | |||
1272 | return 0; | ||
1273 | } | ||
1274 | |||
1275 | void pwrdms_save_context(void) | ||
1276 | { | ||
1277 | pwrdm_for_each(pwrdm_save_context, NULL); | ||
1278 | } | ||
1279 | |||
1280 | void pwrdms_restore_context(void) | ||
1281 | { | ||
1282 | pwrdm_for_each(pwrdm_restore_context, NULL); | ||
1283 | } | ||
1284 | |||
1285 | void pwrdms_lost_power(void) | ||
1286 | { | ||
1287 | pwrdm_for_each(pwrdm_lost_power, NULL); | ||
1288 | } | ||
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h index 28a796ce07d7..9a907fb14044 100644 --- a/arch/arm/mach-omap2/powerdomain.h +++ b/arch/arm/mach-omap2/powerdomain.h | |||
@@ -144,6 +144,7 @@ struct powerdomain { | |||
144 | s64 timer; | 144 | s64 timer; |
145 | s64 state_timer[PWRDM_MAX_PWRSTS]; | 145 | s64 state_timer[PWRDM_MAX_PWRSTS]; |
146 | #endif | 146 | #endif |
147 | u32 context; | ||
147 | }; | 148 | }; |
148 | 149 | ||
149 | /** | 150 | /** |
@@ -198,6 +199,8 @@ struct pwrdm_ops { | |||
198 | int (*pwrdm_set_lowpwrstchange)(struct powerdomain *pwrdm); | 199 | int (*pwrdm_set_lowpwrstchange)(struct powerdomain *pwrdm); |
199 | int (*pwrdm_wait_transition)(struct powerdomain *pwrdm); | 200 | int (*pwrdm_wait_transition)(struct powerdomain *pwrdm); |
200 | int (*pwrdm_has_voltdm)(void); | 201 | int (*pwrdm_has_voltdm)(void); |
202 | void (*pwrdm_save_context)(struct powerdomain *pwrdm); | ||
203 | void (*pwrdm_restore_context)(struct powerdomain *pwrdm); | ||
201 | }; | 204 | }; |
202 | 205 | ||
203 | int pwrdm_register_platform_funcs(struct pwrdm_ops *custom_funcs); | 206 | int pwrdm_register_platform_funcs(struct pwrdm_ops *custom_funcs); |
@@ -273,4 +276,8 @@ extern struct powerdomain gfx_omap2_pwrdm; | |||
273 | extern void pwrdm_lock(struct powerdomain *pwrdm); | 276 | extern void pwrdm_lock(struct powerdomain *pwrdm); |
274 | extern void pwrdm_unlock(struct powerdomain *pwrdm); | 277 | extern void pwrdm_unlock(struct powerdomain *pwrdm); |
275 | 278 | ||
279 | extern void pwrdms_save_context(void); | ||
280 | extern void pwrdms_restore_context(void); | ||
281 | |||
282 | extern void pwrdms_lost_power(void); | ||
276 | #endif | 283 | #endif |
diff --git a/arch/arm/mach-omap2/prm33xx.c b/arch/arm/mach-omap2/prm33xx.c index ebaf80d72a10..d5141669c28d 100644 --- a/arch/arm/mach-omap2/prm33xx.c +++ b/arch/arm/mach-omap2/prm33xx.c | |||
@@ -342,6 +342,35 @@ static void am33xx_prm_global_warm_sw_reset(void) | |||
342 | AM33XX_PRM_RSTCTRL_OFFSET); | 342 | AM33XX_PRM_RSTCTRL_OFFSET); |
343 | } | 343 | } |
344 | 344 | ||
345 | static void am33xx_pwrdm_save_context(struct powerdomain *pwrdm) | ||
346 | { | ||
347 | pwrdm->context = am33xx_prm_read_reg(pwrdm->prcm_offs, | ||
348 | pwrdm->pwrstctrl_offs); | ||
349 | /* | ||
350 | * Do not save LOWPOWERSTATECHANGE, writing a 1 indicates a request, | ||
351 | * reading back a 1 indicates a request in progress. | ||
352 | */ | ||
353 | pwrdm->context &= ~AM33XX_LOWPOWERSTATECHANGE_MASK; | ||
354 | } | ||
355 | |||
356 | static void am33xx_pwrdm_restore_context(struct powerdomain *pwrdm) | ||
357 | { | ||
358 | int st, ctrl; | ||
359 | |||
360 | st = am33xx_prm_read_reg(pwrdm->prcm_offs, | ||
361 | pwrdm->pwrstst_offs); | ||
362 | |||
363 | am33xx_prm_write_reg(pwrdm->context, pwrdm->prcm_offs, | ||
364 | pwrdm->pwrstctrl_offs); | ||
365 | |||
366 | /* Make sure we only wait for a transition if there is one */ | ||
367 | st &= OMAP_POWERSTATEST_MASK; | ||
368 | ctrl = OMAP_POWERSTATEST_MASK & pwrdm->context; | ||
369 | |||
370 | if (st != ctrl) | ||
371 | am33xx_pwrdm_wait_transition(pwrdm); | ||
372 | } | ||
373 | |||
345 | struct pwrdm_ops am33xx_pwrdm_operations = { | 374 | struct pwrdm_ops am33xx_pwrdm_operations = { |
346 | .pwrdm_set_next_pwrst = am33xx_pwrdm_set_next_pwrst, | 375 | .pwrdm_set_next_pwrst = am33xx_pwrdm_set_next_pwrst, |
347 | .pwrdm_read_next_pwrst = am33xx_pwrdm_read_next_pwrst, | 376 | .pwrdm_read_next_pwrst = am33xx_pwrdm_read_next_pwrst, |
@@ -357,6 +386,8 @@ struct pwrdm_ops am33xx_pwrdm_operations = { | |||
357 | .pwrdm_set_mem_retst = am33xx_pwrdm_set_mem_retst, | 386 | .pwrdm_set_mem_retst = am33xx_pwrdm_set_mem_retst, |
358 | .pwrdm_wait_transition = am33xx_pwrdm_wait_transition, | 387 | .pwrdm_wait_transition = am33xx_pwrdm_wait_transition, |
359 | .pwrdm_has_voltdm = am33xx_check_vcvp, | 388 | .pwrdm_has_voltdm = am33xx_check_vcvp, |
389 | .pwrdm_save_context = am33xx_pwrdm_save_context, | ||
390 | .pwrdm_restore_context = am33xx_pwrdm_restore_context, | ||
360 | }; | 391 | }; |
361 | 392 | ||
362 | static struct prm_ll_data am33xx_prm_ll_data = { | 393 | static struct prm_ll_data am33xx_prm_ll_data = { |
diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index acb95936dfe7..7b95729e8359 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c | |||
@@ -12,6 +12,7 @@ | |||
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/cpu_pm.h> | ||
15 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
16 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
17 | #include <linux/errno.h> | 18 | #include <linux/errno.h> |
@@ -30,6 +31,7 @@ | |||
30 | #include "prcm44xx.h" | 31 | #include "prcm44xx.h" |
31 | #include "prminst44xx.h" | 32 | #include "prminst44xx.h" |
32 | #include "powerdomain.h" | 33 | #include "powerdomain.h" |
34 | #include "pm.h" | ||
33 | 35 | ||
34 | /* Static data */ | 36 | /* Static data */ |
35 | 37 | ||
@@ -57,6 +59,13 @@ static struct omap_prcm_irq_setup omap4_prcm_irq_setup = { | |||
57 | .reconfigure_io_chain = &omap44xx_prm_reconfigure_io_chain, | 59 | .reconfigure_io_chain = &omap44xx_prm_reconfigure_io_chain, |
58 | }; | 60 | }; |
59 | 61 | ||
62 | struct omap_prm_irq_context { | ||
63 | unsigned long irq_enable; | ||
64 | unsigned long pm_ctrl; | ||
65 | }; | ||
66 | |||
67 | static struct omap_prm_irq_context omap_prm_context; | ||
68 | |||
60 | /* | 69 | /* |
61 | * omap44xx_prm_reset_src_map - map from bits in the PRM_RSTST | 70 | * omap44xx_prm_reset_src_map - map from bits in the PRM_RSTST |
62 | * hardware register (which are specific to OMAP44xx SoCs) to reset | 71 | * hardware register (which are specific to OMAP44xx SoCs) to reset |
@@ -667,6 +676,54 @@ static int omap4_check_vcvp(void) | |||
667 | return 0; | 676 | return 0; |
668 | } | 677 | } |
669 | 678 | ||
679 | /** | ||
680 | * omap4_pwrdm_save_context - Saves the powerdomain state | ||
681 | * @pwrdm: pointer to individual powerdomain | ||
682 | * | ||
683 | * The function saves the powerdomain state control information. | ||
684 | * This is needed in rtc+ddr modes where we lose powerdomain context. | ||
685 | */ | ||
686 | static void omap4_pwrdm_save_context(struct powerdomain *pwrdm) | ||
687 | { | ||
688 | pwrdm->context = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, | ||
689 | pwrdm->prcm_offs, | ||
690 | pwrdm->pwrstctrl_offs); | ||
691 | |||
692 | /* | ||
693 | * Do not save LOWPOWERSTATECHANGE, writing a 1 indicates a request, | ||
694 | * reading back a 1 indicates a request in progress. | ||
695 | */ | ||
696 | pwrdm->context &= ~OMAP4430_LOWPOWERSTATECHANGE_MASK; | ||
697 | } | ||
698 | |||
699 | /** | ||
700 | * omap4_pwrdm_restore_context - Restores the powerdomain state | ||
701 | * @pwrdm: pointer to individual powerdomain | ||
702 | * | ||
703 | * The function restores the powerdomain state control information. | ||
704 | * This is needed in rtc+ddr modes where we lose powerdomain context. | ||
705 | */ | ||
706 | static void omap4_pwrdm_restore_context(struct powerdomain *pwrdm) | ||
707 | { | ||
708 | int st, ctrl; | ||
709 | |||
710 | st = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, | ||
711 | pwrdm->prcm_offs, | ||
712 | pwrdm->pwrstctrl_offs); | ||
713 | |||
714 | omap4_prminst_write_inst_reg(pwrdm->context, | ||
715 | pwrdm->prcm_partition, | ||
716 | pwrdm->prcm_offs, | ||
717 | pwrdm->pwrstctrl_offs); | ||
718 | |||
719 | /* Make sure we only wait for a transition if there is one */ | ||
720 | st &= OMAP_POWERSTATEST_MASK; | ||
721 | ctrl = OMAP_POWERSTATEST_MASK & pwrdm->context; | ||
722 | |||
723 | if (st != ctrl) | ||
724 | omap4_pwrdm_wait_transition(pwrdm); | ||
725 | } | ||
726 | |||
670 | struct pwrdm_ops omap4_pwrdm_operations = { | 727 | struct pwrdm_ops omap4_pwrdm_operations = { |
671 | .pwrdm_set_next_pwrst = omap4_pwrdm_set_next_pwrst, | 728 | .pwrdm_set_next_pwrst = omap4_pwrdm_set_next_pwrst, |
672 | .pwrdm_read_next_pwrst = omap4_pwrdm_read_next_pwrst, | 729 | .pwrdm_read_next_pwrst = omap4_pwrdm_read_next_pwrst, |
@@ -685,10 +742,50 @@ struct pwrdm_ops omap4_pwrdm_operations = { | |||
685 | .pwrdm_set_mem_retst = omap4_pwrdm_set_mem_retst, | 742 | .pwrdm_set_mem_retst = omap4_pwrdm_set_mem_retst, |
686 | .pwrdm_wait_transition = omap4_pwrdm_wait_transition, | 743 | .pwrdm_wait_transition = omap4_pwrdm_wait_transition, |
687 | .pwrdm_has_voltdm = omap4_check_vcvp, | 744 | .pwrdm_has_voltdm = omap4_check_vcvp, |
745 | .pwrdm_save_context = omap4_pwrdm_save_context, | ||
746 | .pwrdm_restore_context = omap4_pwrdm_restore_context, | ||
688 | }; | 747 | }; |
689 | 748 | ||
690 | static int omap44xx_prm_late_init(void); | 749 | static int omap44xx_prm_late_init(void); |
691 | 750 | ||
751 | void prm_save_context(void) | ||
752 | { | ||
753 | omap_prm_context.irq_enable = | ||
754 | omap4_prm_read_inst_reg(AM43XX_PRM_OCP_SOCKET_INST, | ||
755 | omap4_prcm_irq_setup.mask); | ||
756 | |||
757 | omap_prm_context.pm_ctrl = | ||
758 | omap4_prm_read_inst_reg(AM43XX_PRM_DEVICE_INST, | ||
759 | omap4_prcm_irq_setup.pm_ctrl); | ||
760 | } | ||
761 | |||
762 | void prm_restore_context(void) | ||
763 | { | ||
764 | omap4_prm_write_inst_reg(omap_prm_context.irq_enable, | ||
765 | OMAP4430_PRM_OCP_SOCKET_INST, | ||
766 | omap4_prcm_irq_setup.mask); | ||
767 | |||
768 | omap4_prm_write_inst_reg(omap_prm_context.pm_ctrl, | ||
769 | AM43XX_PRM_DEVICE_INST, | ||
770 | omap4_prcm_irq_setup.pm_ctrl); | ||
771 | } | ||
772 | |||
773 | static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) | ||
774 | { | ||
775 | switch (cmd) { | ||
776 | case CPU_CLUSTER_PM_ENTER: | ||
777 | if (enable_off_mode) | ||
778 | prm_save_context(); | ||
779 | break; | ||
780 | case CPU_CLUSTER_PM_EXIT: | ||
781 | if (enable_off_mode) | ||
782 | prm_restore_context(); | ||
783 | break; | ||
784 | } | ||
785 | |||
786 | return NOTIFY_OK; | ||
787 | } | ||
788 | |||
692 | /* | 789 | /* |
693 | * XXX document | 790 | * XXX document |
694 | */ | 791 | */ |
@@ -709,6 +806,7 @@ static const struct omap_prcm_init_data *prm_init_data; | |||
709 | 806 | ||
710 | int __init omap44xx_prm_init(const struct omap_prcm_init_data *data) | 807 | int __init omap44xx_prm_init(const struct omap_prcm_init_data *data) |
711 | { | 808 | { |
809 | static struct notifier_block nb; | ||
712 | omap_prm_base_init(); | 810 | omap_prm_base_init(); |
713 | 811 | ||
714 | prm_init_data = data; | 812 | prm_init_data = data; |
@@ -730,6 +828,12 @@ int __init omap44xx_prm_init(const struct omap_prcm_init_data *data) | |||
730 | omap4_prcm_irq_setup.mask = AM43XX_PRM_IRQENABLE_MPU_OFFSET; | 828 | omap4_prcm_irq_setup.mask = AM43XX_PRM_IRQENABLE_MPU_OFFSET; |
731 | } | 829 | } |
732 | 830 | ||
831 | /* Only AM43XX can lose prm context during rtc-ddr suspend */ | ||
832 | if (soc_is_am43xx()) { | ||
833 | nb.notifier_call = cpu_notifier; | ||
834 | cpu_pm_register_notifier(&nb); | ||
835 | } | ||
836 | |||
733 | return prm_register(&omap44xx_prm_ll_data); | 837 | return prm_register(&omap44xx_prm_ll_data); |
734 | } | 838 | } |
735 | 839 | ||