diff options
author | Robert Lee <rob.lee@linaro.org> | 2012-05-21 17:50:26 -0500 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2012-06-05 08:49:10 +0200 |
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 e26a9cb05ed..baf93214f89 100644 --- a/arch/arm/mach-imx/pm-imx5.c +++ b/arch/arm/mach-imx/pm-imx5.c @@ -13,18 +13,27 @@ #include <linux/io.h> #include <linux/err.h> #include <asm/cacheflush.h> +#include <asm/system_misc.h> #include <asm/tlbflush.h> #include <mach/common.h> #include <mach/hardware.h> #include "crm-regs-imx5.h" -static struct clk *gpc_dvfs_clk; +/* + * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit. + * This is also the lowest power state possible without affecting + * non-cpu parts of the system. For these reasons, imx5 should default + * to always using this state for cpu idling. The PM_SUSPEND_STANDBY also + * uses this state and needs to take no action when registers remain confgiured + * for this state. + */ +#define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF /* * set cpu low power mode before WFI instruction. This function is called * mx5 because it can be used for mx50, mx51, and mx53. */ -void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) +static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) { u32 plat_lpc, arm_srpgcr, ccm_clpcr; u32 empgc0, empgc1; @@ -87,11 +96,6 @@ void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) } } -static int mx5_suspend_prepare(void) -{ - return clk_prepare_enable(gpc_dvfs_clk); -} - static int mx5_suspend_enter(suspend_state_t state) { switch (state) { @@ -99,7 +103,7 @@ static int mx5_suspend_enter(suspend_state_t state) mx5_cpu_lp_set(STOP_POWER_OFF); break; case PM_SUSPEND_STANDBY: - mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); + /* DEFAULT_IDLE_STATE already configured */ break; default: return -EINVAL; @@ -114,12 +118,10 @@ static int mx5_suspend_enter(suspend_state_t state) __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR); } cpu_do_idle(); - return 0; -} -static void mx5_suspend_finish(void) -{ - clk_disable_unprepare(gpc_dvfs_clk); + /* return registers to default idle state */ + mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); + return 0; } static int mx5_pm_valid(suspend_state_t state) @@ -129,25 +131,38 @@ static int mx5_pm_valid(suspend_state_t state) static const struct platform_suspend_ops mx5_suspend_ops = { .valid = mx5_pm_valid, - .prepare = mx5_suspend_prepare, .enter = mx5_suspend_enter, - .finish = mx5_suspend_finish, }; -static int __init mx5_pm_init(void) +static void imx5_pm_idle(void) { - if (!cpu_is_mx51() && !cpu_is_mx53()) - return 0; + if (likely(!tzic_enable_wake())) + cpu_do_idle(); +} + +static int __init imx5_pm_common_init(void) +{ + int ret; + struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); + + if (IS_ERR(gpc_dvfs_clk)) + return PTR_ERR(gpc_dvfs_clk); - if (gpc_dvfs_clk == NULL) - gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); + ret = clk_prepare_enable(gpc_dvfs_clk); + if (ret) + return ret; - if (!IS_ERR(gpc_dvfs_clk)) { - if (cpu_is_mx51()) - suspend_set_ops(&mx5_suspend_ops); - } else - return -EPERM; + arm_pm_idle = imx5_pm_idle; + + /* Set the registers to the default cpu idle state. */ + mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); return 0; } -device_initcall(mx5_pm_init); + +void __init imx51_pm_init(void) +{ + int ret = imx5_pm_common_init(); + if (!ret) + suspend_set_ops(&mx5_suspend_ops); +} |