aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2014-12-16 23:24:12 -0500
committerShawn Guo <shawn.guo@linaro.org>2015-01-05 08:34:29 -0500
commit05136f0897b526b9cd090c93b95bbd1b67c18cc5 (patch)
treea550264eb74fa756155662b4fdd67ed7d147618f
parentdf096fde0889a7a624fcc9616ff5ebd7446d131e (diff)
ARM: imx: support arm power off in cpuidle for i.mx6sx
This patch introduces an independent cpuidle driver for i.MX6SX, and supports arm power off in idle, totally 3 levels of cpuidle are supported as below: 1. ARM WFI; 2. SOC in WAIT mode; 3. SOC in WAIT mode + ARM power off. ARM power off can save at least 5mW power. This patch also replaces imx6q_enable_rbc with imx6_enable_rbc. Signed-off-by: Anson Huang <b20788@freescale.com> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
-rw-r--r--arch/arm/mach-imx/Makefile3
-rw-r--r--arch/arm/mach-imx/common.h4
-rw-r--r--arch/arm/mach-imx/cpuidle-imx6sx.c107
-rw-r--r--arch/arm/mach-imx/cpuidle.h5
-rw-r--r--arch/arm/mach-imx/gpc.c25
-rw-r--r--arch/arm/mach-imx/mach-imx6sx.c2
-rw-r--r--arch/arm/mach-imx/pm-imx6.c6
7 files changed, 144 insertions, 8 deletions
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index f5ac685a29fc..8d1b10180908 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -32,8 +32,7 @@ ifeq ($(CONFIG_CPU_IDLE),y)
32obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o 32obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o
33obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o 33obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o
34obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o 34obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o
35# i.MX6SX reuses i.MX6Q cpuidle driver 35obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6sx.o
36obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6q.o
37endif 36endif
38 37
39ifdef CONFIG_SND_IMX_SOC 38ifdef CONFIG_SND_IMX_SOC
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index cfcdb623d78f..1028b6c505c4 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -70,6 +70,10 @@ void imx_set_soc_revision(unsigned int rev);
70unsigned int imx_get_soc_revision(void); 70unsigned int imx_get_soc_revision(void);
71void imx_init_revision_from_anatop(void); 71void imx_init_revision_from_anatop(void);
72struct device *imx_soc_device_init(void); 72struct device *imx_soc_device_init(void);
73void imx6_enable_rbc(bool enable);
74void imx_gpc_set_arm_power_in_lpm(bool power_off);
75void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw);
76void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw);
73 77
74enum mxc_cpu_pwr_mode { 78enum mxc_cpu_pwr_mode {
75 WAIT_CLOCKED, /* wfi only */ 79 WAIT_CLOCKED, /* wfi only */
diff --git a/arch/arm/mach-imx/cpuidle-imx6sx.c b/arch/arm/mach-imx/cpuidle-imx6sx.c
new file mode 100644
index 000000000000..d8a9f219e028
--- /dev/null
+++ b/arch/arm/mach-imx/cpuidle-imx6sx.c
@@ -0,0 +1,107 @@
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/cpu_pm.h>
11#include <linux/module.h>
12#include <asm/cpuidle.h>
13#include <asm/proc-fns.h>
14#include <asm/suspend.h>
15
16#include "common.h"
17#include "cpuidle.h"
18
19static int imx6sx_idle_finish(unsigned long val)
20{
21 cpu_do_idle();
22
23 return 0;
24}
25
26static int imx6sx_enter_wait(struct cpuidle_device *dev,
27 struct cpuidle_driver *drv, int index)
28{
29 imx6q_set_lpm(WAIT_UNCLOCKED);
30
31 switch (index) {
32 case 1:
33 cpu_do_idle();
34 break;
35 case 2:
36 imx6_enable_rbc(true);
37 imx_gpc_set_arm_power_in_lpm(true);
38 imx_set_cpu_jump(0, v7_cpu_resume);
39 /* Need to notify there is a cpu pm operation. */
40 cpu_pm_enter();
41 cpu_cluster_pm_enter();
42
43 cpu_suspend(0, imx6sx_idle_finish);
44
45 cpu_cluster_pm_exit();
46 cpu_pm_exit();
47 imx_gpc_set_arm_power_in_lpm(false);
48 imx6_enable_rbc(false);
49 break;
50 default:
51 break;
52 }
53
54 imx6q_set_lpm(WAIT_CLOCKED);
55
56 return index;
57}
58
59static struct cpuidle_driver imx6sx_cpuidle_driver = {
60 .name = "imx6sx_cpuidle",
61 .owner = THIS_MODULE,
62 .states = {
63 /* WFI */
64 ARM_CPUIDLE_WFI_STATE,
65 /* WAIT */
66 {
67 .exit_latency = 50,
68 .target_residency = 75,
69 .flags = CPUIDLE_FLAG_TIME_VALID |
70 CPUIDLE_FLAG_TIMER_STOP,
71 .enter = imx6sx_enter_wait,
72 .name = "WAIT",
73 .desc = "Clock off",
74 },
75 /* WAIT + ARM power off */
76 {
77 /*
78 * ARM gating 31us * 5 + RBC clear 65us
79 * and some margin for SW execution, here set it
80 * to 300us.
81 */
82 .exit_latency = 300,
83 .target_residency = 500,
84 .flags = CPUIDLE_FLAG_TIME_VALID,
85 .enter = imx6sx_enter_wait,
86 .name = "LOW-POWER-IDLE",
87 .desc = "ARM power off",
88 },
89 },
90 .state_count = 3,
91 .safe_state_index = 0,
92};
93
94int __init imx6sx_cpuidle_init(void)
95{
96 imx6_enable_rbc(false);
97 /*
98 * set ARM power up/down timing to the fastest,
99 * sw2iso and sw can be set to one 32K cycle = 31us
100 * except for power up sw2iso which need to be
101 * larger than LDO ramp up time.
102 */
103 imx_gpc_set_arm_power_up_timing(2, 1);
104 imx_gpc_set_arm_power_down_timing(1, 1);
105
106 return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
107}
diff --git a/arch/arm/mach-imx/cpuidle.h b/arch/arm/mach-imx/cpuidle.h
index 24e33670417c..f9140128ba05 100644
--- a/arch/arm/mach-imx/cpuidle.h
+++ b/arch/arm/mach-imx/cpuidle.h
@@ -14,6 +14,7 @@
14extern int imx5_cpuidle_init(void); 14extern int imx5_cpuidle_init(void);
15extern int imx6q_cpuidle_init(void); 15extern int imx6q_cpuidle_init(void);
16extern int imx6sl_cpuidle_init(void); 16extern int imx6sl_cpuidle_init(void);
17extern int imx6sx_cpuidle_init(void);
17#else 18#else
18static inline int imx5_cpuidle_init(void) 19static inline int imx5_cpuidle_init(void)
19{ 20{
@@ -27,4 +28,8 @@ static inline int imx6sl_cpuidle_init(void)
27{ 28{
28 return 0; 29 return 0;
29} 30}
31static inline int imx6sx_cpuidle_init(void)
32{
33 return 0;
34}
30#endif 35#endif
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 5f3602ec74fa..745caa18ab2c 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -20,6 +20,10 @@
20 20
21#define GPC_IMR1 0x008 21#define GPC_IMR1 0x008
22#define GPC_PGC_CPU_PDN 0x2a0 22#define GPC_PGC_CPU_PDN 0x2a0
23#define GPC_PGC_CPU_PUPSCR 0x2a4
24#define GPC_PGC_CPU_PDNSCR 0x2a8
25#define GPC_PGC_SW2ISO_SHIFT 0x8
26#define GPC_PGC_SW_SHIFT 0x0
23 27
24#define IMR_NUM 4 28#define IMR_NUM 4
25 29
@@ -27,6 +31,23 @@ static void __iomem *gpc_base;
27static u32 gpc_wake_irqs[IMR_NUM]; 31static u32 gpc_wake_irqs[IMR_NUM];
28static u32 gpc_saved_imrs[IMR_NUM]; 32static u32 gpc_saved_imrs[IMR_NUM];
29 33
34void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw)
35{
36 writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) |
37 (sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PUPSCR);
38}
39
40void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw)
41{
42 writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) |
43 (sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PDNSCR);
44}
45
46void imx_gpc_set_arm_power_in_lpm(bool power_off)
47{
48 writel_relaxed(power_off, gpc_base + GPC_PGC_CPU_PDN);
49}
50
30void imx_gpc_pre_suspend(bool arm_power_off) 51void imx_gpc_pre_suspend(bool arm_power_off)
31{ 52{
32 void __iomem *reg_imr1 = gpc_base + GPC_IMR1; 53 void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
@@ -34,7 +55,7 @@ void imx_gpc_pre_suspend(bool arm_power_off)
34 55
35 /* Tell GPC to power off ARM core when suspend */ 56 /* Tell GPC to power off ARM core when suspend */
36 if (arm_power_off) 57 if (arm_power_off)
37 writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_PDN); 58 imx_gpc_set_arm_power_in_lpm(arm_power_off);
38 59
39 for (i = 0; i < IMR_NUM; i++) { 60 for (i = 0; i < IMR_NUM; i++) {
40 gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); 61 gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4);
@@ -48,7 +69,7 @@ void imx_gpc_post_resume(void)
48 int i; 69 int i;
49 70
50 /* Keep ARM core powered on for other low-power modes */ 71 /* Keep ARM core powered on for other low-power modes */
51 writel_relaxed(0x0, gpc_base + GPC_PGC_CPU_PDN); 72 imx_gpc_set_arm_power_in_lpm(false);
52 73
53 for (i = 0; i < IMR_NUM; i++) 74 for (i = 0; i < IMR_NUM; i++)
54 writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); 75 writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
diff --git a/arch/arm/mach-imx/mach-imx6sx.c b/arch/arm/mach-imx/mach-imx6sx.c
index 7a96c6577234..66988eb6a3a4 100644
--- a/arch/arm/mach-imx/mach-imx6sx.c
+++ b/arch/arm/mach-imx/mach-imx6sx.c
@@ -90,7 +90,7 @@ static void __init imx6sx_init_irq(void)
90 90
91static void __init imx6sx_init_late(void) 91static void __init imx6sx_init_late(void)
92{ 92{
93 imx6q_cpuidle_init(); 93 imx6sx_cpuidle_init();
94 94
95 if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) 95 if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
96 platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0); 96 platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
index 661ffcf1031e..46fd695203c7 100644
--- a/arch/arm/mach-imx/pm-imx6.c
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -205,7 +205,7 @@ void imx6q_set_int_mem_clk_lpm(bool enable)
205 writel_relaxed(val, ccm_base + CGPR); 205 writel_relaxed(val, ccm_base + CGPR);
206} 206}
207 207
208static void imx6q_enable_rbc(bool enable) 208void imx6_enable_rbc(bool enable)
209{ 209{
210 u32 val; 210 u32 val;
211 211
@@ -359,7 +359,7 @@ static int imx6q_pm_enter(suspend_state_t state)
359 * RBC setting, so we do NOT need to do that here. 359 * RBC setting, so we do NOT need to do that here.
360 */ 360 */
361 if (!imx6_suspend_in_ocram_fn) 361 if (!imx6_suspend_in_ocram_fn)
362 imx6q_enable_rbc(true); 362 imx6_enable_rbc(true);
363 imx_gpc_pre_suspend(true); 363 imx_gpc_pre_suspend(true);
364 imx_anatop_pre_suspend(); 364 imx_anatop_pre_suspend();
365 /* Zzz ... */ 365 /* Zzz ... */
@@ -368,7 +368,7 @@ static int imx6q_pm_enter(suspend_state_t state)
368 imx_smp_prepare(); 368 imx_smp_prepare();
369 imx_anatop_post_resume(); 369 imx_anatop_post_resume();
370 imx_gpc_post_resume(); 370 imx_gpc_post_resume();
371 imx6q_enable_rbc(false); 371 imx6_enable_rbc(false);
372 imx6q_enable_wb(false); 372 imx6q_enable_wb(false);
373 imx6q_set_int_mem_clk_lpm(true); 373 imx6q_set_int_mem_clk_lpm(true);
374 imx6q_set_lpm(WAIT_CLOCKED); 374 imx6q_set_lpm(WAIT_CLOCKED);