aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/cpuidle-imx6q.c
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 /arch/arm/mach-imx/cpuidle-imx6q.c
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>
Diffstat (limited to 'arch/arm/mach-imx/cpuidle-imx6q.c')
-rw-r--r--arch/arm/mach-imx/cpuidle-imx6q.c73
1 files changed, 71 insertions, 2 deletions
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}