diff options
author | Robert Lee <rob.lee@linaro.org> | 2012-05-21 18:50:26 -0400 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2012-06-05 02:49:10 -0400 |
commit | 565fa91f236524b6ba4872903dc9cc9c874493e6 (patch) | |
tree | bf0872ac1d86158da8209341b4d1eef0755f9150 /arch/arm/mach-imx/pm-imx5.c | |
parent | eee4f4003824b0887251a3ba4493217371816a57 (diff) |
ARM: imx: clean and consolidate imx5 suspend and idle code
The imx5 idle code that existed in mm-imx5.c is moved to pm-imx5.c.
The imx5_pm_init call is now exported and called during the
MACHINE_START late_init in supported imx5 platforms.
Remove various enabling/disabling of the gpc_dvfs clock and
enable it once during initialization. This is a very low
power clock that must be enabled during low power operations.
There are only two "suspend_state_t" imx5 low power modes ever
used. STOP_POWER_OFF for suspend to mem and
WAIT_UNCLOCKED_POWER_OFF for idle and suspend to standby. The
latter mode only requires 500 nanoseconds of extra hardware
exit time beyond a basic WFI operation (WAIT_CLOCKED mode) so
no other idle mode is necessary. Given this information, it
is more efficient to keep the registers in the often used
WAIT_UNCLOCKED_POWER_OFF state and only to and from the
STOP_POWER_OFF register state as needed when suspend to
mem is required.
Signed-off-by: Robert Lee <rob.lee@linaro.org>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/arm/mach-imx/pm-imx5.c')
-rw-r--r-- | arch/arm/mach-imx/pm-imx5.c | 67 |
1 files changed, 41 insertions, 26 deletions
diff --git a/arch/arm/mach-imx/pm-imx5.c b/arch/arm/mach-imx/pm-imx5.c index e26a9cb05ed8..baf93214f899 100644 --- a/arch/arm/mach-imx/pm-imx5.c +++ b/arch/arm/mach-imx/pm-imx5.c | |||
@@ -13,18 +13,27 @@ | |||
13 | #include <linux/io.h> | 13 | #include <linux/io.h> |
14 | #include <linux/err.h> | 14 | #include <linux/err.h> |
15 | #include <asm/cacheflush.h> | 15 | #include <asm/cacheflush.h> |
16 | #include <asm/system_misc.h> | ||
16 | #include <asm/tlbflush.h> | 17 | #include <asm/tlbflush.h> |
17 | #include <mach/common.h> | 18 | #include <mach/common.h> |
18 | #include <mach/hardware.h> | 19 | #include <mach/hardware.h> |
19 | #include "crm-regs-imx5.h" | 20 | #include "crm-regs-imx5.h" |
20 | 21 | ||
21 | static struct clk *gpc_dvfs_clk; | 22 | /* |
23 | * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit. | ||
24 | * This is also the lowest power state possible without affecting | ||
25 | * non-cpu parts of the system. For these reasons, imx5 should default | ||
26 | * to always using this state for cpu idling. The PM_SUSPEND_STANDBY also | ||
27 | * uses this state and needs to take no action when registers remain confgiured | ||
28 | * for this state. | ||
29 | */ | ||
30 | #define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF | ||
22 | 31 | ||
23 | /* | 32 | /* |
24 | * set cpu low power mode before WFI instruction. This function is called | 33 | * set cpu low power mode before WFI instruction. This function is called |
25 | * mx5 because it can be used for mx50, mx51, and mx53. | 34 | * mx5 because it can be used for mx50, mx51, and mx53. |
26 | */ | 35 | */ |
27 | void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) | 36 | static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) |
28 | { | 37 | { |
29 | u32 plat_lpc, arm_srpgcr, ccm_clpcr; | 38 | u32 plat_lpc, arm_srpgcr, ccm_clpcr; |
30 | u32 empgc0, empgc1; | 39 | u32 empgc0, empgc1; |
@@ -87,11 +96,6 @@ void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) | |||
87 | } | 96 | } |
88 | } | 97 | } |
89 | 98 | ||
90 | static int mx5_suspend_prepare(void) | ||
91 | { | ||
92 | return clk_prepare_enable(gpc_dvfs_clk); | ||
93 | } | ||
94 | |||
95 | static int mx5_suspend_enter(suspend_state_t state) | 99 | static int mx5_suspend_enter(suspend_state_t state) |
96 | { | 100 | { |
97 | switch (state) { | 101 | switch (state) { |
@@ -99,7 +103,7 @@ static int mx5_suspend_enter(suspend_state_t state) | |||
99 | mx5_cpu_lp_set(STOP_POWER_OFF); | 103 | mx5_cpu_lp_set(STOP_POWER_OFF); |
100 | break; | 104 | break; |
101 | case PM_SUSPEND_STANDBY: | 105 | case PM_SUSPEND_STANDBY: |
102 | mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); | 106 | /* DEFAULT_IDLE_STATE already configured */ |
103 | break; | 107 | break; |
104 | default: | 108 | default: |
105 | return -EINVAL; | 109 | return -EINVAL; |
@@ -114,12 +118,10 @@ static int mx5_suspend_enter(suspend_state_t state) | |||
114 | __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR); | 118 | __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR); |
115 | } | 119 | } |
116 | cpu_do_idle(); | 120 | cpu_do_idle(); |
117 | return 0; | ||
118 | } | ||
119 | 121 | ||
120 | static void mx5_suspend_finish(void) | 122 | /* return registers to default idle state */ |
121 | { | 123 | mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); |
122 | clk_disable_unprepare(gpc_dvfs_clk); | 124 | return 0; |
123 | } | 125 | } |
124 | 126 | ||
125 | static int mx5_pm_valid(suspend_state_t state) | 127 | static int mx5_pm_valid(suspend_state_t state) |
@@ -129,25 +131,38 @@ static int mx5_pm_valid(suspend_state_t state) | |||
129 | 131 | ||
130 | static const struct platform_suspend_ops mx5_suspend_ops = { | 132 | static const struct platform_suspend_ops mx5_suspend_ops = { |
131 | .valid = mx5_pm_valid, | 133 | .valid = mx5_pm_valid, |
132 | .prepare = mx5_suspend_prepare, | ||
133 | .enter = mx5_suspend_enter, | 134 | .enter = mx5_suspend_enter, |
134 | .finish = mx5_suspend_finish, | ||
135 | }; | 135 | }; |
136 | 136 | ||
137 | static int __init mx5_pm_init(void) | 137 | static void imx5_pm_idle(void) |
138 | { | 138 | { |
139 | if (!cpu_is_mx51() && !cpu_is_mx53()) | 139 | if (likely(!tzic_enable_wake())) |
140 | return 0; | 140 | cpu_do_idle(); |
141 | } | ||
142 | |||
143 | static int __init imx5_pm_common_init(void) | ||
144 | { | ||
145 | int ret; | ||
146 | struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); | ||
147 | |||
148 | if (IS_ERR(gpc_dvfs_clk)) | ||
149 | return PTR_ERR(gpc_dvfs_clk); | ||
141 | 150 | ||
142 | if (gpc_dvfs_clk == NULL) | 151 | ret = clk_prepare_enable(gpc_dvfs_clk); |
143 | gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); | 152 | if (ret) |
153 | return ret; | ||
144 | 154 | ||
145 | if (!IS_ERR(gpc_dvfs_clk)) { | 155 | arm_pm_idle = imx5_pm_idle; |
146 | if (cpu_is_mx51()) | 156 | |
147 | suspend_set_ops(&mx5_suspend_ops); | 157 | /* Set the registers to the default cpu idle state. */ |
148 | } else | 158 | mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); |
149 | return -EPERM; | ||
150 | 159 | ||
151 | return 0; | 160 | return 0; |
152 | } | 161 | } |
153 | device_initcall(mx5_pm_init); | 162 | |
163 | void __init imx51_pm_init(void) | ||
164 | { | ||
165 | int ret = imx5_pm_common_init(); | ||
166 | if (!ret) | ||
167 | suspend_set_ops(&mx5_suspend_ops); | ||
168 | } | ||