diff options
Diffstat (limited to 'arch/arm/mach-imx/pm-imx5.c')
| -rw-r--r-- | arch/arm/mach-imx/pm-imx5.c | 111 | 
1 files changed, 85 insertions, 26 deletions
diff --git a/arch/arm/mach-imx/pm-imx5.c b/arch/arm/mach-imx/pm-imx5.c index e26a9cb05ed..19621ed1ffa 100644 --- a/arch/arm/mach-imx/pm-imx5.c +++ b/arch/arm/mach-imx/pm-imx5.c @@ -12,19 +12,30 @@  #include <linux/clk.h>  #include <linux/io.h>  #include <linux/err.h> +#include <linux/export.h>  #include <asm/cacheflush.h> +#include <asm/system_misc.h>  #include <asm/tlbflush.h>  #include <mach/common.h> +#include <mach/cpuidle.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 +98,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 +105,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 +120,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 +133,80 @@ 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 inline int imx5_cpu_do_idle(void) +{ +	int ret = tzic_enable_wake(); + +	if (likely(!ret)) +		cpu_do_idle(); + +	return ret; +} + +static void imx5_pm_idle(void) +{ +	imx5_cpu_do_idle(); +} + +static int imx5_cpuidle_enter(struct cpuidle_device *dev, +				struct cpuidle_driver *drv, int idx) +{ +	int ret; + +	ret = imx5_cpu_do_idle(); +	if (ret < 0) +		return ret; + +	return idx; +} + +static struct cpuidle_driver imx5_cpuidle_driver = { +	.name			= "imx5_cpuidle", +	.owner			= THIS_MODULE, +	.en_core_tk_irqen	= 1, +	.states[0]	= { +		.enter			= imx5_cpuidle_enter, +		.exit_latency		= 2, +		.target_residency	= 1, +		.flags			= CPUIDLE_FLAG_TIME_VALID, +		.name			= "IMX5 SRPG", +		.desc			= "CPU state retained,powered off", +	}, +	.state_count		= 1, +}; + +static int __init imx5_pm_common_init(void)  { -	if (!cpu_is_mx51() && !cpu_is_mx53()) -		return 0; +	int ret; +	struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); -	if (gpc_dvfs_clk == NULL) -		gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); +	if (IS_ERR(gpc_dvfs_clk)) +		return PTR_ERR(gpc_dvfs_clk); -	if (!IS_ERR(gpc_dvfs_clk)) { -		if (cpu_is_mx51()) -			suspend_set_ops(&mx5_suspend_ops); -	} else -		return -EPERM; +	ret = clk_prepare_enable(gpc_dvfs_clk); +	if (ret) +		return ret; +	arm_pm_idle = imx5_pm_idle; + +	/* Set the registers to the default cpu idle state. */ +	mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); + +	imx_cpuidle_init(&imx5_cpuidle_driver);  	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); +} + +void __init imx53_pm_init(void) +{ +	imx5_pm_common_init(); +}  |