diff options
-rw-r--r-- | arch/arm/mach-imx/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-imx6sl.c | 26 | ||||
-rw-r--r-- | arch/arm/mach-imx/common.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/cpuidle-imx6sl.c | 57 | ||||
-rw-r--r-- | arch/arm/mach-imx/cpuidle.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-imx/mach-imx6sl.c | 3 |
6 files changed, 93 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index ec419649320f..21114a9a2ae7 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 | |||
30 | ifeq ($(CONFIG_CPU_IDLE),y) | 30 | ifeq ($(CONFIG_CPU_IDLE),y) |
31 | obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o | 31 | obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o |
32 | obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o | 32 | obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o |
33 | obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o | ||
33 | endif | 34 | endif |
34 | 35 | ||
35 | ifdef CONFIG_SND_IMX_SOC | 36 | ifdef CONFIG_SND_IMX_SOC |
diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c index c5f17c2d4951..6f21a1333fe4 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[] = { | |||
66 | static struct clk *clks[IMX6SL_CLK_END]; | 66 | static struct clk *clks[IMX6SL_CLK_END]; |
67 | static struct clk_onecell_data clk_data; | 67 | static struct clk_onecell_data clk_data; |
68 | 68 | ||
69 | /* | ||
70 | * ERR005311 CCM: After exit from WAIT mode, unwanted interrupt(s) taken | ||
71 | * during WAIT mode entry process could cause cache memory | ||
72 | * corruption. | ||
73 | * | ||
74 | * Software workaround: | ||
75 | * To prevent this issue from occurring, software should ensure that the | ||
76 | * ARM to IPG clock ratio is less than 12:5 (that is < 2.4x), before | ||
77 | * entering WAIT mode. | ||
78 | * | ||
79 | * This function will set the ARM clk to max value within the 12:5 limit. | ||
80 | */ | ||
81 | void imx6sl_set_wait_clk(bool enter) | ||
82 | { | ||
83 | static unsigned long saved_arm_rate; | ||
84 | |||
85 | if (enter) { | ||
86 | unsigned long ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]); | ||
87 | unsigned long max_arm_wait_rate = (12 * ipg_rate) / 5; | ||
88 | saved_arm_rate = clk_get_rate(clks[IMX6SL_CLK_ARM]); | ||
89 | clk_set_rate(clks[IMX6SL_CLK_ARM], max_arm_wait_rate); | ||
90 | } else { | ||
91 | clk_set_rate(clks[IMX6SL_CLK_ARM], saved_arm_rate); | ||
92 | } | ||
93 | } | ||
94 | |||
69 | static void __init imx6sl_clocks_init(struct device_node *ccm_node) | 95 | static void __init imx6sl_clocks_init(struct device_node *ccm_node) |
70 | { | 96 | { |
71 | struct device_node *np; | 97 | struct device_node *np; |
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index cdbddfa2a42f..b909b689619b 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); | |||
140 | void imx_anatop_post_resume(void); | 140 | void imx_anatop_post_resume(void); |
141 | int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); | 141 | int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); |
142 | void imx6q_set_int_mem_clk_lpm(void); | 142 | void imx6q_set_int_mem_clk_lpm(void); |
143 | void imx6sl_set_wait_clk(bool enter); | ||
143 | 144 | ||
144 | void imx_cpu_die(unsigned int cpu); | 145 | void imx_cpu_die(unsigned int cpu); |
145 | int imx_cpu_kill(unsigned int cpu); | 146 | 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 000000000000..d4b6b8171fa9 --- /dev/null +++ b/arch/arm/mach-imx/cpuidle-imx6sl.c | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Freescale Semiconductor, Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/cpuidle.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <asm/cpuidle.h> | ||
12 | #include <asm/proc-fns.h> | ||
13 | |||
14 | #include "common.h" | ||
15 | #include "cpuidle.h" | ||
16 | |||
17 | static int imx6sl_enter_wait(struct cpuidle_device *dev, | ||
18 | struct cpuidle_driver *drv, int index) | ||
19 | { | ||
20 | imx6q_set_lpm(WAIT_UNCLOCKED); | ||
21 | /* | ||
22 | * Software workaround for ERR005311, see function | ||
23 | * description for details. | ||
24 | */ | ||
25 | imx6sl_set_wait_clk(true); | ||
26 | cpu_do_idle(); | ||
27 | imx6sl_set_wait_clk(false); | ||
28 | imx6q_set_lpm(WAIT_CLOCKED); | ||
29 | |||
30 | return index; | ||
31 | } | ||
32 | |||
33 | static struct cpuidle_driver imx6sl_cpuidle_driver = { | ||
34 | .name = "imx6sl_cpuidle", | ||
35 | .owner = THIS_MODULE, | ||
36 | .states = { | ||
37 | /* WFI */ | ||
38 | ARM_CPUIDLE_WFI_STATE, | ||
39 | /* WAIT */ | ||
40 | { | ||
41 | .exit_latency = 50, | ||
42 | .target_residency = 75, | ||
43 | .flags = CPUIDLE_FLAG_TIME_VALID | | ||
44 | CPUIDLE_FLAG_TIMER_STOP, | ||
45 | .enter = imx6sl_enter_wait, | ||
46 | .name = "WAIT", | ||
47 | .desc = "Clock off", | ||
48 | }, | ||
49 | }, | ||
50 | .state_count = 2, | ||
51 | .safe_state_index = 0, | ||
52 | }; | ||
53 | |||
54 | int __init imx6sl_cpuidle_init(void) | ||
55 | { | ||
56 | return cpuidle_register(&imx6sl_cpuidle_driver, NULL); | ||
57 | } | ||
diff --git a/arch/arm/mach-imx/cpuidle.h b/arch/arm/mach-imx/cpuidle.h index 786f98ecc145..24e33670417c 100644 --- a/arch/arm/mach-imx/cpuidle.h +++ b/arch/arm/mach-imx/cpuidle.h | |||
@@ -13,6 +13,7 @@ | |||
13 | #ifdef CONFIG_CPU_IDLE | 13 | #ifdef CONFIG_CPU_IDLE |
14 | extern int imx5_cpuidle_init(void); | 14 | extern int imx5_cpuidle_init(void); |
15 | extern int imx6q_cpuidle_init(void); | 15 | extern int imx6q_cpuidle_init(void); |
16 | extern int imx6sl_cpuidle_init(void); | ||
16 | #else | 17 | #else |
17 | static inline int imx5_cpuidle_init(void) | 18 | static inline int imx5_cpuidle_init(void) |
18 | { | 19 | { |
@@ -22,4 +23,8 @@ static inline int imx6q_cpuidle_init(void) | |||
22 | { | 23 | { |
23 | return 0; | 24 | return 0; |
24 | } | 25 | } |
26 | static inline int imx6sl_cpuidle_init(void) | ||
27 | { | ||
28 | return 0; | ||
29 | } | ||
25 | #endif | 30 | #endif |
diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c index 0f4fd4c0ab8e..a26fdb2b1a6f 100644 --- a/arch/arm/mach-imx/mach-imx6sl.c +++ b/arch/arm/mach-imx/mach-imx6sl.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <asm/mach/map.h> | 17 | #include <asm/mach/map.h> |
18 | 18 | ||
19 | #include "common.h" | 19 | #include "common.h" |
20 | #include "cpuidle.h" | ||
20 | 21 | ||
21 | static void __init imx6sl_fec_init(void) | 22 | static void __init imx6sl_fec_init(void) |
22 | { | 23 | { |
@@ -39,6 +40,8 @@ static void __init imx6sl_init_late(void) | |||
39 | /* imx6sl reuses imx6q cpufreq driver */ | 40 | /* imx6sl reuses imx6q cpufreq driver */ |
40 | if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) | 41 | if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) |
41 | platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0); | 42 | platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0); |
43 | |||
44 | imx6sl_cpuidle_init(); | ||
42 | } | 45 | } |
43 | 46 | ||
44 | static void __init imx6sl_init_machine(void) | 47 | static void __init imx6sl_init_machine(void) |