ARM: imx: add cpuidle support for i.mx6sl
Add cpuidle support for i.MX6SL, currently only support two cpuidle levels(ARM wfi and WAIT mode), and add software workaround for WAIT mode errata as below: ERR005311 CCM: After exit from WAIT mode, unwanted interrupt(s) taken during WAIT mode entry process could cause cache memory corruption. Software workaround: To prevent this issue from occurring, software should ensure that the ARM to IPG clock ratio is less than 12:5 (that is < 2.4x), before entering WAIT mode. Signed-off-by: Anson Huang <b20788@freescale.com> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
This commit is contained in:
parent
848db4a0a1
commit
751f7e999a
|
@ -30,6 +30,7 @@ obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
|
|||
ifeq ($(CONFIG_CPU_IDLE),y)
|
||||
obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o
|
||||
obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o
|
||||
obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SND_IMX_SOC
|
||||
|
|
|
@ -66,6 +66,32 @@ static struct clk_div_table video_div_table[] = {
|
|||
static struct clk *clks[IMX6SL_CLK_END];
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
/*
|
||||
* ERR005311 CCM: After exit from WAIT mode, unwanted interrupt(s) taken
|
||||
* during WAIT mode entry process could cause cache memory
|
||||
* corruption.
|
||||
*
|
||||
* Software workaround:
|
||||
* To prevent this issue from occurring, software should ensure that the
|
||||
* ARM to IPG clock ratio is less than 12:5 (that is < 2.4x), before
|
||||
* entering WAIT mode.
|
||||
*
|
||||
* This function will set the ARM clk to max value within the 12:5 limit.
|
||||
*/
|
||||
void imx6sl_set_wait_clk(bool enter)
|
||||
{
|
||||
static unsigned long saved_arm_rate;
|
||||
|
||||
if (enter) {
|
||||
unsigned long ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]);
|
||||
unsigned long max_arm_wait_rate = (12 * ipg_rate) / 5;
|
||||
saved_arm_rate = clk_get_rate(clks[IMX6SL_CLK_ARM]);
|
||||
clk_set_rate(clks[IMX6SL_CLK_ARM], max_arm_wait_rate);
|
||||
} else {
|
||||
clk_set_rate(clks[IMX6SL_CLK_ARM], saved_arm_rate);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init imx6sl_clocks_init(struct device_node *ccm_node)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
|
|
@ -140,6 +140,7 @@ void imx_anatop_pre_suspend(void);
|
|||
void imx_anatop_post_resume(void);
|
||||
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode);
|
||||
void imx6q_set_int_mem_clk_lpm(void);
|
||||
void imx6sl_set_wait_clk(bool enter);
|
||||
|
||||
void imx_cpu_die(unsigned int cpu);
|
||||
int imx_cpu_kill(unsigned int cpu);
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/proc-fns.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
|
||||
static int imx6sl_enter_wait(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
imx6q_set_lpm(WAIT_UNCLOCKED);
|
||||
/*
|
||||
* Software workaround for ERR005311, see function
|
||||
* description for details.
|
||||
*/
|
||||
imx6sl_set_wait_clk(true);
|
||||
cpu_do_idle();
|
||||
imx6sl_set_wait_clk(false);
|
||||
imx6q_set_lpm(WAIT_CLOCKED);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static struct cpuidle_driver imx6sl_cpuidle_driver = {
|
||||
.name = "imx6sl_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.states = {
|
||||
/* WFI */
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
/* WAIT */
|
||||
{
|
||||
.exit_latency = 50,
|
||||
.target_residency = 75,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_TIMER_STOP,
|
||||
.enter = imx6sl_enter_wait,
|
||||
.name = "WAIT",
|
||||
.desc = "Clock off",
|
||||
},
|
||||
},
|
||||
.state_count = 2,
|
||||
.safe_state_index = 0,
|
||||
};
|
||||
|
||||
int __init imx6sl_cpuidle_init(void)
|
||||
{
|
||||
return cpuidle_register(&imx6sl_cpuidle_driver, NULL);
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
#ifdef CONFIG_CPU_IDLE
|
||||
extern int imx5_cpuidle_init(void);
|
||||
extern int imx6q_cpuidle_init(void);
|
||||
extern int imx6sl_cpuidle_init(void);
|
||||
#else
|
||||
static inline int imx5_cpuidle_init(void)
|
||||
{
|
||||
|
@ -22,4 +23,8 @@ static inline int imx6q_cpuidle_init(void)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx6sl_cpuidle_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <asm/mach/map.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
|
||||
static void __init imx6sl_fec_init(void)
|
||||
{
|
||||
|
@ -39,6 +40,8 @@ static void __init imx6sl_init_late(void)
|
|||
/* imx6sl reuses imx6q cpufreq driver */
|
||||
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
|
||||
platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
|
||||
|
||||
imx6sl_cpuidle_init();
|
||||
}
|
||||
|
||||
static void __init imx6sl_init_machine(void)
|
||||
|
|
Loading…
Reference in New Issue