aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 /*