diff options
-rw-r--r-- | arch/arm/mach-imx/clk-imx6q.c | 12 | ||||
-rw-r--r-- | arch/arm/mach-imx/common.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-imx/cpuidle-imx6q.c | 73 | ||||
-rw-r--r-- | arch/arm/mach-imx/mach-imx6q.c | 7 | ||||
-rw-r--r-- | arch/arm/mach-imx/platsmp.c | 10 |
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 | |||
57 | static void __iomem *ccm_base; | 60 | static void __iomem *ccm_base; |
58 | 61 | ||
62 | void 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 | |||
59 | int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) | 70 | int 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); | |||
117 | extern void v7_secondary_startup(void); | 117 | extern void v7_secondary_startup(void); |
118 | extern void imx_scu_map_io(void); | 118 | extern void imx_scu_map_io(void); |
119 | extern void imx_smp_prepare(void); | 119 | extern void imx_smp_prepare(void); |
120 | extern void imx_scu_standby_enable(void); | ||
120 | #else | 121 | #else |
121 | static inline void imx_scu_map_io(void) {} | 122 | static inline void imx_scu_map_io(void) {} |
122 | static inline void imx_smp_prepare(void) {} | 123 | static inline void imx_smp_prepare(void) {} |
124 | static inline void imx_scu_standby_enable(void) {} | ||
123 | #endif | 125 | #endif |
124 | extern void imx_enable_cpu(int cpu, bool enable); | 126 | extern void imx_enable_cpu(int cpu, bool enable); |
125 | extern void imx_set_cpu_jump(int cpu, void *jump_addr); | 127 | extern void imx_set_cpu_jump(int cpu, void *jump_addr); |
@@ -129,6 +131,7 @@ extern void imx_gpc_init(void); | |||
129 | extern void imx_gpc_pre_suspend(void); | 131 | extern void imx_gpc_pre_suspend(void); |
130 | extern void imx_gpc_post_resume(void); | 132 | extern void imx_gpc_post_resume(void); |
131 | extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); | 133 | extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); |
134 | extern void imx6q_set_chicken_bit(void); | ||
132 | 135 | ||
133 | extern void imx_cpu_die(unsigned int cpu); | 136 | extern 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 | ||
18 | static atomic_t master = ATOMIC_INIT(0); | ||
19 | static DEFINE_SPINLOCK(master_lock); | ||
20 | |||
21 | static 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 | |||
42 | idle: | ||
43 | cpu_do_idle(); | ||
44 | done: | ||
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 | */ | ||
55 | static 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 | |||
15 | static struct cpuidle_driver imx6q_cpuidle_driver = { | 62 | static 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 | ||
23 | int __init imx6q_cpuidle_init(void) | 83 | int __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 | ||
203 | static void __init imx6q_init_late(void) | 203 | static 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 | ||
208 | static void __init imx6q_map_io(void) | 213 | static 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 | |||
23 | static void __iomem *scu_base; | 25 | static void __iomem *scu_base; |
24 | 26 | ||
25 | static struct map_desc scu_io_desc __initdata = { | 27 | static 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 | ||
47 | void 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 | |||
45 | static void __cpuinit imx_secondary_init(unsigned int cpu) | 55 | static void __cpuinit imx_secondary_init(unsigned int cpu) |
46 | { | 56 | { |
47 | /* | 57 | /* |