Commit 751f7e99 authored by Anson Huang's avatar Anson Huang Committed by Shawn Guo

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: default avatarAnson Huang <b20788@freescale.com>
Signed-off-by: default avatarShawn Guo <shawn.guo@linaro.org>
parent 848db4a0
...@@ -30,6 +30,7 @@ obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o ...@@ -30,6 +30,7 @@ obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
ifeq ($(CONFIG_CPU_IDLE),y) ifeq ($(CONFIG_CPU_IDLE),y)
obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o
obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o
obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o
endif endif
ifdef CONFIG_SND_IMX_SOC ifdef CONFIG_SND_IMX_SOC
......
...@@ -66,6 +66,32 @@ static struct clk_div_table video_div_table[] = { ...@@ -66,6 +66,32 @@ static struct clk_div_table video_div_table[] = {
static struct clk *clks[IMX6SL_CLK_END]; static struct clk *clks[IMX6SL_CLK_END];
static struct clk_onecell_data clk_data; 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) static void __init imx6sl_clocks_init(struct device_node *ccm_node)
{ {
struct device_node *np; struct device_node *np;
......
...@@ -140,6 +140,7 @@ void imx_anatop_pre_suspend(void); ...@@ -140,6 +140,7 @@ void imx_anatop_pre_suspend(void);
void imx_anatop_post_resume(void); void imx_anatop_post_resume(void);
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode);
void imx6q_set_int_mem_clk_lpm(void); void imx6q_set_int_mem_clk_lpm(void);
void imx6sl_set_wait_clk(bool enter);
void imx_cpu_die(unsigned int cpu); void imx_cpu_die(unsigned int cpu);
int imx_cpu_kill(unsigned int cpu); int imx_cpu_kill(unsigned int cpu);
......
/*
* 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 @@ ...@@ -13,6 +13,7 @@
#ifdef CONFIG_CPU_IDLE #ifdef CONFIG_CPU_IDLE
extern int imx5_cpuidle_init(void); extern int imx5_cpuidle_init(void);
extern int imx6q_cpuidle_init(void); extern int imx6q_cpuidle_init(void);
extern int imx6sl_cpuidle_init(void);
#else #else
static inline int imx5_cpuidle_init(void) static inline int imx5_cpuidle_init(void)
{ {
...@@ -22,4 +23,8 @@ static inline int imx6q_cpuidle_init(void) ...@@ -22,4 +23,8 @@ static inline int imx6q_cpuidle_init(void)
{ {
return 0; return 0;
} }
static inline int imx6sl_cpuidle_init(void)
{
return 0;
}
#endif #endif
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include "common.h" #include "common.h"
#include "cpuidle.h"
static void __init imx6sl_fec_init(void) static void __init imx6sl_fec_init(void)
{ {
...@@ -39,6 +40,8 @@ static void __init imx6sl_init_late(void) ...@@ -39,6 +40,8 @@ static void __init imx6sl_init_late(void)
/* imx6sl reuses imx6q cpufreq driver */ /* imx6sl reuses imx6q cpufreq driver */
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0); platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
imx6sl_cpuidle_init();
} }
static void __init imx6sl_init_machine(void) static void __init imx6sl_init_machine(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment