aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Guo <shawn.guo@linaro.org>2012-12-04 09:55:15 -0500
committerShawn Guo <shawn.guo@linaro.org>2013-01-30 08:09:31 -0500
commite5f9dec8ff5ff3f6254412abed1f68d758f6616b (patch)
tree05f293a11ec605b1380b2c7bfb0c3895abeddb08
parent12bb344074cfc50482121c76571e3cda07c4bc09 (diff)
ARM: imx6q: support WAIT mode using cpuidle
Add WAIT mode (ARM core clock gating) support to imx6q cpuidle driver. As WAIT mode is broken on imx6q TO 1.0 and 1.1, it only enables the support for revision 1.2 with chicken bit set. Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
-rw-r--r--arch/arm/mach-imx/clk-imx6q.c12
-rw-r--r--arch/arm/mach-imx/common.h3
-rw-r--r--arch/arm/mach-imx/cpuidle-imx6q.c73
-rw-r--r--arch/arm/mach-imx/mach-imx6q.c7
-rw-r--r--arch/arm/mach-imx/platsmp.c10
5 files changed, 102 insertions, 3 deletions
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index c7e429bd32d0..b30b65a8cce5 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -54,8 +54,19 @@
54#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26) 54#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
55#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27) 55#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
56 56
57#define CGPR 0x64
58#define BM_CGPR_CHICKEN_BIT (0x1 << 17)
59
57static void __iomem *ccm_base; 60static void __iomem *ccm_base;
58 61
62void imx6q_set_chicken_bit(void)
63{
64 u32 val = readl_relaxed(ccm_base + CGPR);
65
66 val |= BM_CGPR_CHICKEN_BIT;
67 writel_relaxed(val, ccm_base + CGPR);
68}
69
59int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) 70int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
60{ 71{
61 u32 val = readl_relaxed(ccm_base + CLPCR); 72 u32 val = readl_relaxed(ccm_base + CLPCR);
@@ -66,6 +77,7 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
66 break; 77 break;
67 case WAIT_UNCLOCKED: 78 case WAIT_UNCLOCKED:
68 val |= 0x1 << BP_CLPCR_LPM; 79 val |= 0x1 << BP_CLPCR_LPM;
80 val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
69 break; 81 break;
70 case STOP_POWER_ON: 82 case STOP_POWER_ON:
71 val |= 0x2 << BP_CLPCR_LPM; 83 val |= 0x2 << BP_CLPCR_LPM;
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 972c9f8cc97a..c04ec845e3a3 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -117,9 +117,11 @@ extern u32 *pl310_get_save_ptr(void);
117extern void v7_secondary_startup(void); 117extern void v7_secondary_startup(void);
118extern void imx_scu_map_io(void); 118extern void imx_scu_map_io(void);
119extern void imx_smp_prepare(void); 119extern void imx_smp_prepare(void);
120extern void imx_scu_standby_enable(void);
120#else 121#else
121static inline void imx_scu_map_io(void) {} 122static inline void imx_scu_map_io(void) {}
122static inline void imx_smp_prepare(void) {} 123static inline void imx_smp_prepare(void) {}
124static inline void imx_scu_standby_enable(void) {}
123#endif 125#endif
124extern void imx_enable_cpu(int cpu, bool enable); 126extern void imx_enable_cpu(int cpu, bool enable);
125extern void imx_set_cpu_jump(int cpu, void *jump_addr); 127extern void imx_set_cpu_jump(int cpu, void *jump_addr);
@@ -129,6 +131,7 @@ extern void imx_gpc_init(void);
129extern void imx_gpc_pre_suspend(void); 131extern void imx_gpc_pre_suspend(void);
130extern void imx_gpc_post_resume(void); 132extern void imx_gpc_post_resume(void);
131extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); 133extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode);
134extern void imx6q_set_chicken_bit(void);
132 135
133extern void imx_cpu_die(unsigned int cpu); 136extern void imx_cpu_die(unsigned int cpu);
134 137
diff --git a/arch/arm/mach-imx/cpuidle-imx6q.c b/arch/arm/mach-imx/cpuidle-imx6q.c
index 83facc97b5da..d533e2695f0e 100644
--- a/arch/arm/mach-imx/cpuidle-imx6q.c
+++ b/arch/arm/mach-imx/cpuidle-imx6q.c
@@ -6,21 +6,90 @@
6 * published by the Free Software Foundation. 6 * published by the Free Software Foundation.
7 */ 7 */
8 8
9#include <linux/clockchips.h>
9#include <linux/cpuidle.h> 10#include <linux/cpuidle.h>
10#include <linux/module.h> 11#include <linux/module.h>
11#include <asm/cpuidle.h> 12#include <asm/cpuidle.h>
13#include <asm/proc-fns.h>
12 14
15#include "common.h"
13#include "cpuidle.h" 16#include "cpuidle.h"
14 17
18static atomic_t master = ATOMIC_INIT(0);
19static DEFINE_SPINLOCK(master_lock);
20
21static int imx6q_enter_wait(struct cpuidle_device *dev,
22 struct cpuidle_driver *drv, int index)
23{
24 int cpu = dev->cpu;
25
26 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
27
28 if (atomic_inc_return(&master) == num_online_cpus()) {
29 /*
30 * With this lock, we prevent other cpu to exit and enter
31 * this function again and become the master.
32 */
33 if (!spin_trylock(&master_lock))
34 goto idle;
35 imx6q_set_lpm(WAIT_UNCLOCKED);
36 cpu_do_idle();
37 imx6q_set_lpm(WAIT_CLOCKED);
38 spin_unlock(&master_lock);
39 goto done;
40 }
41
42idle:
43 cpu_do_idle();
44done:
45 atomic_dec(&master);
46 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
47
48 return index;
49}
50
51/*
52 * For each cpu, setup the broadcast timer because local timer
53 * stops for the states other than WFI.
54 */
55static void imx6q_setup_broadcast_timer(void *arg)
56{
57 int cpu = smp_processor_id();
58
59 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
60}
61
15static struct cpuidle_driver imx6q_cpuidle_driver = { 62static struct cpuidle_driver imx6q_cpuidle_driver = {
16 .name = "imx6q_cpuidle", 63 .name = "imx6q_cpuidle",
17 .owner = THIS_MODULE, 64 .owner = THIS_MODULE,
18 .en_core_tk_irqen = 1, 65 .en_core_tk_irqen = 1,
19 .states[0] = ARM_CPUIDLE_WFI_STATE, 66 .states = {
20 .state_count = 1, 67 /* WFI */
68 ARM_CPUIDLE_WFI_STATE,
69 /* WAIT */
70 {
71 .exit_latency = 50,
72 .target_residency = 75,
73 .flags = CPUIDLE_FLAG_TIME_VALID,
74 .enter = imx6q_enter_wait,
75 .name = "WAIT",
76 .desc = "Clock off",
77 },
78 },
79 .state_count = 2,
80 .safe_state_index = 0,
21}; 81};
22 82
23int __init imx6q_cpuidle_init(void) 83int __init imx6q_cpuidle_init(void)
24{ 84{
85 /* Need to enable SCU standby for entering WAIT modes */
86 imx_scu_standby_enable();
87
88 /* Set chicken bit to get a reliable WAIT mode support */
89 imx6q_set_chicken_bit();
90
91 /* Configure the broadcast timer on each cpu */
92 on_each_cpu(imx6q_setup_broadcast_timer, NULL, 1);
93
25 return imx_cpuidle_init(&imx6q_cpuidle_driver); 94 return imx_cpuidle_init(&imx6q_cpuidle_driver);
26} 95}
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 27726de3537e..77a3b4bfff20 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -202,7 +202,12 @@ static void __init imx6q_init_machine(void)
202 202
203static void __init imx6q_init_late(void) 203static void __init imx6q_init_late(void)
204{ 204{
205 imx6q_cpuidle_init(); 205 /*
206 * WAIT mode is broken on TO 1.0 and 1.1, so there is no point
207 * to run cpuidle on them.
208 */
209 if (imx6q_revision() > IMX_CHIP_REVISION_1_1)
210 imx6q_cpuidle_init();
206} 211}
207 212
208static void __init imx6q_map_io(void) 213static void __init imx6q_map_io(void)
diff --git a/arch/arm/mach-imx/platsmp.c b/arch/arm/mach-imx/platsmp.c
index 3777b805b76b..a70b54877191 100644
--- a/arch/arm/mach-imx/platsmp.c
+++ b/arch/arm/mach-imx/platsmp.c
@@ -20,6 +20,8 @@
20#include "common.h" 20#include "common.h"
21#include "hardware.h" 21#include "hardware.h"
22 22
23#define SCU_STANDBY_ENABLE (1 << 5)
24
23static void __iomem *scu_base; 25static void __iomem *scu_base;
24 26
25static struct map_desc scu_io_desc __initdata = { 27static struct map_desc scu_io_desc __initdata = {
@@ -42,6 +44,14 @@ void __init imx_scu_map_io(void)
42 scu_base = IMX_IO_ADDRESS(base); 44 scu_base = IMX_IO_ADDRESS(base);
43} 45}
44 46
47void imx_scu_standby_enable(void)
48{
49 u32 val = readl_relaxed(scu_base);
50
51 val |= SCU_STANDBY_ENABLE;
52 writel_relaxed(val, scu_base);
53}
54
45static void __cpuinit imx_secondary_init(unsigned int cpu) 55static void __cpuinit imx_secondary_init(unsigned int cpu)
46{ 56{
47 /* 57 /*