Message ID | 1389240402-23353-1-git-send-email-b20788@freescale.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Jan 09, 2014 at 12:06:42PM +0800, Anson Huang wrote: > 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> The patch looks fine to me. But I failed to apply it to my tree. You need to base it on my for-next branch below. git://git.linaro.org/people/shawnguo/linux-2.6.git for-next Shawn > --- > Changes since V1: > 1. Add Errata description for software workaround of WAIT mode; > 2. Improve variable type from u32 to unsigned long. > > arch/arm/mach-imx/Makefile | 1 + > arch/arm/mach-imx/clk-imx6sl.c | 26 ++++++++++++++++ > arch/arm/mach-imx/common.h | 1 + > arch/arm/mach-imx/cpuidle-imx6sl.c | 57 ++++++++++++++++++++++++++++++++++++ > arch/arm/mach-imx/cpuidle.h | 5 ++++ > arch/arm/mach-imx/mach-imx6sl.c | 7 +++++ > 6 files changed, 97 insertions(+) > create mode 100644 arch/arm/mach-imx/cpuidle-imx6sl.c > > diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile > index d8205fb..1fcd273 100644 > --- a/arch/arm/mach-imx/Makefile > +++ b/arch/arm/mach-imx/Makefile > @@ -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 > diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c > index e6900e9..0e3e182 100644 > --- a/arch/arm/mach-imx/clk-imx6sl.c > +++ b/arch/arm/mach-imx/clk-imx6sl.c > @@ -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; > diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h > index 2b0151f..524c4cb 100644 > --- a/arch/arm/mach-imx/common.h > +++ b/arch/arm/mach-imx/common.h > @@ -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_chicken_bit(void); > +void imx6sl_set_wait_clk(bool enter); > > void imx_cpu_die(unsigned int cpu); > int imx_cpu_kill(unsigned int cpu); > diff --git a/arch/arm/mach-imx/cpuidle-imx6sl.c b/arch/arm/mach-imx/cpuidle-imx6sl.c > new file mode 100644 > index 0000000..d4b6b81 > --- /dev/null > +++ b/arch/arm/mach-imx/cpuidle-imx6sl.c > @@ -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); > +} > diff --git a/arch/arm/mach-imx/cpuidle.h b/arch/arm/mach-imx/cpuidle.h > index 786f98e..24e3367 100644 > --- a/arch/arm/mach-imx/cpuidle.h > +++ b/arch/arm/mach-imx/cpuidle.h > @@ -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 > diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c > index 7fdd4e8..1cb6f6e 100644 > --- a/arch/arm/mach-imx/mach-imx6sl.c > +++ b/arch/arm/mach-imx/mach-imx6sl.c > @@ -17,6 +17,7 @@ > #include <asm/mach/map.h> > > #include "common.h" > +#include "cpuidle.h" > > static void __init imx6sl_fec_init(void) > { > @@ -52,6 +53,11 @@ static void __init imx6sl_init_machine(void) > imx6q_pm_init(); > } > > +static void __init imx6sl_init_late(void) > +{ > + imx6sl_cpuidle_init(); > +} > + > static void __init imx6sl_map_io(void) > { > debug_ll_io_init(); > @@ -78,4 +84,5 @@ DT_MACHINE_START(IMX6SL, "Freescale i.MX6 SoloLite (Device Tree)") > .init_machine = imx6sl_init_machine, > .dt_compat = imx6sl_dt_compat, > .restart = mxc_restart, > + .init_late = imx6sl_init_late, > MACHINE_END > -- > 1.7.9.5 > >
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index d8205fb..1fcd273 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -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 diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c index e6900e9..0e3e182 100644 --- a/arch/arm/mach-imx/clk-imx6sl.c +++ b/arch/arm/mach-imx/clk-imx6sl.c @@ -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; diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 2b0151f..524c4cb 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -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_chicken_bit(void); +void imx6sl_set_wait_clk(bool enter); void imx_cpu_die(unsigned int cpu); int imx_cpu_kill(unsigned int cpu); diff --git a/arch/arm/mach-imx/cpuidle-imx6sl.c b/arch/arm/mach-imx/cpuidle-imx6sl.c new file mode 100644 index 0000000..d4b6b81 --- /dev/null +++ b/arch/arm/mach-imx/cpuidle-imx6sl.c @@ -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); +} diff --git a/arch/arm/mach-imx/cpuidle.h b/arch/arm/mach-imx/cpuidle.h index 786f98e..24e3367 100644 --- a/arch/arm/mach-imx/cpuidle.h +++ b/arch/arm/mach-imx/cpuidle.h @@ -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 diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c index 7fdd4e8..1cb6f6e 100644 --- a/arch/arm/mach-imx/mach-imx6sl.c +++ b/arch/arm/mach-imx/mach-imx6sl.c @@ -17,6 +17,7 @@ #include <asm/mach/map.h> #include "common.h" +#include "cpuidle.h" static void __init imx6sl_fec_init(void) { @@ -52,6 +53,11 @@ static void __init imx6sl_init_machine(void) imx6q_pm_init(); } +static void __init imx6sl_init_late(void) +{ + imx6sl_cpuidle_init(); +} + static void __init imx6sl_map_io(void) { debug_ll_io_init(); @@ -78,4 +84,5 @@ DT_MACHINE_START(IMX6SL, "Freescale i.MX6 SoloLite (Device Tree)") .init_machine = imx6sl_init_machine, .dt_compat = imx6sl_dt_compat, .restart = mxc_restart, + .init_late = imx6sl_init_late, MACHINE_END
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> --- Changes since V1: 1. Add Errata description for software workaround of WAIT mode; 2. Improve variable type from u32 to unsigned long. arch/arm/mach-imx/Makefile | 1 + arch/arm/mach-imx/clk-imx6sl.c | 26 ++++++++++++++++ arch/arm/mach-imx/common.h | 1 + arch/arm/mach-imx/cpuidle-imx6sl.c | 57 ++++++++++++++++++++++++++++++++++++ arch/arm/mach-imx/cpuidle.h | 5 ++++ arch/arm/mach-imx/mach-imx6sl.c | 7 +++++ 6 files changed, 97 insertions(+) create mode 100644 arch/arm/mach-imx/cpuidle-imx6sl.c