aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2018-05-25 17:13:22 -0400
committerOlof Johansson <olof@lixom.net>2018-05-25 17:13:22 -0400
commitd900f5ce12f96c608153d7cc954631c8c03a0d27 (patch)
tree04fa09859af607248ead90cfa277acd89ee7be7f
parent280b0471fd2c6823e486919bef01e20c7c503297 (diff)
parent3bb3799cd4233b7c24622ae8c41455fb27a55c0f (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.c10
-rw-r--r--arch/arm/mach-omap2/clockdomain.c73
-rw-r--r--arch/arm/mach-omap2/clockdomain.h8
-rw-r--r--arch/arm/mach-omap2/cm33xx.c53
-rw-r--r--arch/arm/mach-omap2/cminst44xx.c43
-rw-r--r--arch/arm/mach-omap2/pm44xx.c13
-rw-r--r--arch/arm/mach-omap2/powerdomain.c87
-rw-r--r--arch/arm/mach-omap2/powerdomain.h7
-rw-r--r--arch/arm/mach-omap2/prm33xx.c31
-rw-r--r--arch/arm/mach-omap2/prm44xx.c104
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
312static struct at24_platform_data at24c04 = { 312static 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
317static struct i2c_board_info __initdata mistral_i2c_board_info[] = { 317static 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 */
36static LIST_HEAD(clkdm_list); 38static LIST_HEAD(clkdm_list);
@@ -39,6 +41,8 @@ static LIST_HEAD(clkdm_list);
39static struct clkdm_autodep *autodeps; 41static struct clkdm_autodep *autodeps;
40 42
41static struct clkdm_ops *arch_clkdm; 43static struct clkdm_ops *arch_clkdm;
44void clkdm_save_context(void);
45void 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
456static 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)
460int clkdm_complete_init(void) 480int 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 */
1343static 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 */
1356static 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 */
1369void 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 */
1379void 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 */
163struct clkdm_ops { 166struct 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
180int clkdm_register_platform_funcs(struct clkdm_ops *co); 185int clkdm_register_platform_funcs(struct clkdm_ops *co);
@@ -214,6 +219,9 @@ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk);
214int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh); 219int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh);
215int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh); 220int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh);
216 221
222void clkdm_save_context(void);
223void clkdm_restore_context(void);
224
217extern void __init omap242x_clockdomains_init(void); 225extern void __init omap242x_clockdomains_init(void);
218extern void __init omap243x_clockdomains_init(void); 226extern void __init omap243x_clockdomains_init(void);
219extern void __init omap3xxx_clockdomains_init(void); 227extern 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
75static 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 */
358static 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 */
373static 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
341struct clkdm_ops am33xx_clkdm_operations = { 392struct 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
350static const struct cm_ll_data am33xx_cm_ll_data = { 403static 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 */
490static 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 */
506static 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
484struct clkdm_ops omap4_clkdm_operations = { 525struct 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
501struct clkdm_ops am43xx_clkdm_operations = { 544struct 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
43void pwrdms_save_context(void);
44void pwrdms_restore_context(void);
45
42enum { 46enum {
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
340static 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)
347int pwrdm_complete_init(void) 367int 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 */
1236static 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 */
1249static 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
1256static 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
1275void pwrdms_save_context(void)
1276{
1277 pwrdm_for_each(pwrdm_save_context, NULL);
1278}
1279
1280void pwrdms_restore_context(void)
1281{
1282 pwrdm_for_each(pwrdm_restore_context, NULL);
1283}
1284
1285void 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
203int pwrdm_register_platform_funcs(struct pwrdm_ops *custom_funcs); 206int pwrdm_register_platform_funcs(struct pwrdm_ops *custom_funcs);
@@ -273,4 +276,8 @@ extern struct powerdomain gfx_omap2_pwrdm;
273extern void pwrdm_lock(struct powerdomain *pwrdm); 276extern void pwrdm_lock(struct powerdomain *pwrdm);
274extern void pwrdm_unlock(struct powerdomain *pwrdm); 277extern void pwrdm_unlock(struct powerdomain *pwrdm);
275 278
279extern void pwrdms_save_context(void);
280extern void pwrdms_restore_context(void);
281
282extern 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
345static 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
356static 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
345struct pwrdm_ops am33xx_pwrdm_operations = { 374struct 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
362static struct prm_ll_data am33xx_prm_ll_data = { 393static 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
62struct omap_prm_irq_context {
63 unsigned long irq_enable;
64 unsigned long pm_ctrl;
65};
66
67static 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 */
686static 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 */
706static 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
670struct pwrdm_ops omap4_pwrdm_operations = { 727struct 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
690static int omap44xx_prm_late_init(void); 749static int omap44xx_prm_late_init(void);
691 750
751void 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
762void 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
773static 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
710int __init omap44xx_prm_init(const struct omap_prcm_init_data *data) 807int __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