diff options
-rw-r--r-- | arch/arm/mach-imx/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-imx/common.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-imx/cpuidle-imx6sx.c | 107 | ||||
-rw-r--r-- | arch/arm/mach-imx/cpuidle.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-imx/gpc.c | 25 | ||||
-rw-r--r-- | arch/arm/mach-imx/mach-imx6sx.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/pm-imx6.c | 6 |
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) | |||
32 | obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o | 32 | obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o |
33 | obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o | 33 | obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o |
34 | obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o | 34 | obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o |
35 | # i.MX6SX reuses i.MX6Q cpuidle driver | 35 | obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6sx.o |
36 | obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6q.o | ||
37 | endif | 36 | endif |
38 | 37 | ||
39 | ifdef CONFIG_SND_IMX_SOC | 38 | ifdef 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); | |||
70 | unsigned int imx_get_soc_revision(void); | 70 | unsigned int imx_get_soc_revision(void); |
71 | void imx_init_revision_from_anatop(void); | 71 | void imx_init_revision_from_anatop(void); |
72 | struct device *imx_soc_device_init(void); | 72 | struct device *imx_soc_device_init(void); |
73 | void imx6_enable_rbc(bool enable); | ||
74 | void imx_gpc_set_arm_power_in_lpm(bool power_off); | ||
75 | void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw); | ||
76 | void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw); | ||
73 | 77 | ||
74 | enum mxc_cpu_pwr_mode { | 78 | enum 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 | |||
19 | static int imx6sx_idle_finish(unsigned long val) | ||
20 | { | ||
21 | cpu_do_idle(); | ||
22 | |||
23 | return 0; | ||
24 | } | ||
25 | |||
26 | static 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 | |||
59 | static 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 | |||
94 | int __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 @@ | |||
14 | extern int imx5_cpuidle_init(void); | 14 | extern int imx5_cpuidle_init(void); |
15 | extern int imx6q_cpuidle_init(void); | 15 | extern int imx6q_cpuidle_init(void); |
16 | extern int imx6sl_cpuidle_init(void); | 16 | extern int imx6sl_cpuidle_init(void); |
17 | extern int imx6sx_cpuidle_init(void); | ||
17 | #else | 18 | #else |
18 | static inline int imx5_cpuidle_init(void) | 19 | static 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 | } |
31 | static 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; | |||
27 | static u32 gpc_wake_irqs[IMR_NUM]; | 31 | static u32 gpc_wake_irqs[IMR_NUM]; |
28 | static u32 gpc_saved_imrs[IMR_NUM]; | 32 | static u32 gpc_saved_imrs[IMR_NUM]; |
29 | 33 | ||
34 | void 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 | |||
40 | void 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 | |||
46 | void imx_gpc_set_arm_power_in_lpm(bool power_off) | ||
47 | { | ||
48 | writel_relaxed(power_off, gpc_base + GPC_PGC_CPU_PDN); | ||
49 | } | ||
50 | |||
30 | void imx_gpc_pre_suspend(bool arm_power_off) | 51 | void 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 | ||
91 | static void __init imx6sx_init_late(void) | 91 | static 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 | ||
208 | static void imx6q_enable_rbc(bool enable) | 208 | void 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); |