aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/pm-imx5.c
diff options
context:
space:
mode:
authorRobert Lee <rob.lee@linaro.org>2012-05-21 18:50:26 -0400
committerSascha Hauer <s.hauer@pengutronix.de>2012-06-05 02:49:10 -0400
commit565fa91f236524b6ba4872903dc9cc9c874493e6 (patch)
treebf0872ac1d86158da8209341b4d1eef0755f9150 /arch/arm/mach-imx/pm-imx5.c
parenteee4f4003824b0887251a3ba4493217371816a57 (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.c67
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
21static 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 */
27void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) 36static 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
90static int mx5_suspend_prepare(void)
91{
92 return clk_prepare_enable(gpc_dvfs_clk);
93}
94
95static int mx5_suspend_enter(suspend_state_t state) 99static 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
120static 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
125static int mx5_pm_valid(suspend_state_t state) 127static int mx5_pm_valid(suspend_state_t state)
@@ -129,25 +131,38 @@ static int mx5_pm_valid(suspend_state_t state)
129 131
130static const struct platform_suspend_ops mx5_suspend_ops = { 132static 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
137static int __init mx5_pm_init(void) 137static 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
143static 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}
153device_initcall(mx5_pm_init); 162
163void __init imx51_pm_init(void)
164{
165 int ret = imx5_pm_common_init();
166 if (!ret)
167 suspend_set_ops(&mx5_suspend_ops);
168}