diff options
Diffstat (limited to 'arch/arm/mach-exynos/cpuidle.c')
-rw-r--r-- | arch/arm/mach-exynos/cpuidle.c | 255 |
1 files changed, 0 insertions, 255 deletions
diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c deleted file mode 100644 index 3dd385ebf195..000000000000 --- a/arch/arm/mach-exynos/cpuidle.c +++ /dev/null | |||
@@ -1,255 +0,0 @@ | |||
1 | /* linux/arch/arm/mach-exynos4/cpuidle.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/cpuidle.h> | ||
14 | #include <linux/cpu_pm.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/export.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/time.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | #include <asm/proc-fns.h> | ||
22 | #include <asm/smp_scu.h> | ||
23 | #include <asm/suspend.h> | ||
24 | #include <asm/unified.h> | ||
25 | #include <asm/cpuidle.h> | ||
26 | |||
27 | #include <plat/pm.h> | ||
28 | |||
29 | #include <mach/map.h> | ||
30 | |||
31 | #include "common.h" | ||
32 | #include "regs-pmu.h" | ||
33 | |||
34 | #define REG_DIRECTGO_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \ | ||
35 | S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ | ||
36 | (S5P_VA_SYSRAM + 0x24) : S5P_INFORM0)) | ||
37 | #define REG_DIRECTGO_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ | ||
38 | S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ | ||
39 | (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1)) | ||
40 | |||
41 | #define S5P_CHECK_AFTR 0xFCBA0D10 | ||
42 | |||
43 | #define EXYNOS5_PWR_CTRL1 (S5P_VA_CMU + 0x01020) | ||
44 | #define EXYNOS5_PWR_CTRL2 (S5P_VA_CMU + 0x01024) | ||
45 | |||
46 | #define PWR_CTRL1_CORE2_DOWN_RATIO (7 << 28) | ||
47 | #define PWR_CTRL1_CORE1_DOWN_RATIO (7 << 16) | ||
48 | #define PWR_CTRL1_DIV2_DOWN_EN (1 << 9) | ||
49 | #define PWR_CTRL1_DIV1_DOWN_EN (1 << 8) | ||
50 | #define PWR_CTRL1_USE_CORE1_WFE (1 << 5) | ||
51 | #define PWR_CTRL1_USE_CORE0_WFE (1 << 4) | ||
52 | #define PWR_CTRL1_USE_CORE1_WFI (1 << 1) | ||
53 | #define PWR_CTRL1_USE_CORE0_WFI (1 << 0) | ||
54 | |||
55 | #define PWR_CTRL2_DIV2_UP_EN (1 << 25) | ||
56 | #define PWR_CTRL2_DIV1_UP_EN (1 << 24) | ||
57 | #define PWR_CTRL2_DUR_STANDBY2_VAL (1 << 16) | ||
58 | #define PWR_CTRL2_DUR_STANDBY1_VAL (1 << 8) | ||
59 | #define PWR_CTRL2_CORE2_UP_RATIO (1 << 4) | ||
60 | #define PWR_CTRL2_CORE1_UP_RATIO (1 << 0) | ||
61 | |||
62 | static int exynos4_enter_lowpower(struct cpuidle_device *dev, | ||
63 | struct cpuidle_driver *drv, | ||
64 | int index); | ||
65 | |||
66 | static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device); | ||
67 | |||
68 | static struct cpuidle_driver exynos4_idle_driver = { | ||
69 | .name = "exynos4_idle", | ||
70 | .owner = THIS_MODULE, | ||
71 | .states = { | ||
72 | [0] = ARM_CPUIDLE_WFI_STATE, | ||
73 | [1] = { | ||
74 | .enter = exynos4_enter_lowpower, | ||
75 | .exit_latency = 300, | ||
76 | .target_residency = 100000, | ||
77 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
78 | .name = "C1", | ||
79 | .desc = "ARM power down", | ||
80 | }, | ||
81 | }, | ||
82 | .state_count = 2, | ||
83 | .safe_state_index = 0, | ||
84 | }; | ||
85 | |||
86 | /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ | ||
87 | static void exynos4_set_wakeupmask(void) | ||
88 | { | ||
89 | __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); | ||
90 | } | ||
91 | |||
92 | static unsigned int g_pwr_ctrl, g_diag_reg; | ||
93 | |||
94 | static void save_cpu_arch_register(void) | ||
95 | { | ||
96 | /*read power control register*/ | ||
97 | asm("mrc p15, 0, %0, c15, c0, 0" : "=r"(g_pwr_ctrl) : : "cc"); | ||
98 | /*read diagnostic register*/ | ||
99 | asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | static void restore_cpu_arch_register(void) | ||
104 | { | ||
105 | /*write power control register*/ | ||
106 | asm("mcr p15, 0, %0, c15, c0, 0" : : "r"(g_pwr_ctrl) : "cc"); | ||
107 | /*write diagnostic register*/ | ||
108 | asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | static int idle_finisher(unsigned long flags) | ||
113 | { | ||
114 | cpu_do_idle(); | ||
115 | return 1; | ||
116 | } | ||
117 | |||
118 | static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, | ||
119 | struct cpuidle_driver *drv, | ||
120 | int index) | ||
121 | { | ||
122 | unsigned long tmp; | ||
123 | |||
124 | exynos4_set_wakeupmask(); | ||
125 | |||
126 | /* Set value of power down register for aftr mode */ | ||
127 | exynos_sys_powerdown_conf(SYS_AFTR); | ||
128 | |||
129 | __raw_writel(virt_to_phys(exynos_cpu_resume), REG_DIRECTGO_ADDR); | ||
130 | __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG); | ||
131 | |||
132 | save_cpu_arch_register(); | ||
133 | |||
134 | /* Setting Central Sequence Register for power down mode */ | ||
135 | tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
136 | tmp &= ~S5P_CENTRAL_LOWPWR_CFG; | ||
137 | __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
138 | |||
139 | cpu_pm_enter(); | ||
140 | cpu_suspend(0, idle_finisher); | ||
141 | |||
142 | #ifdef CONFIG_SMP | ||
143 | if (!soc_is_exynos5250()) | ||
144 | scu_enable(S5P_VA_SCU); | ||
145 | #endif | ||
146 | cpu_pm_exit(); | ||
147 | |||
148 | restore_cpu_arch_register(); | ||
149 | |||
150 | /* | ||
151 | * If PMU failed while entering sleep mode, WFI will be | ||
152 | * ignored by PMU and then exiting cpu_do_idle(). | ||
153 | * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically | ||
154 | * in this situation. | ||
155 | */ | ||
156 | tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
157 | if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { | ||
158 | tmp |= S5P_CENTRAL_LOWPWR_CFG; | ||
159 | __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
160 | } | ||
161 | |||
162 | /* Clear wakeup state register */ | ||
163 | __raw_writel(0x0, S5P_WAKEUP_STAT); | ||
164 | |||
165 | return index; | ||
166 | } | ||
167 | |||
168 | static int exynos4_enter_lowpower(struct cpuidle_device *dev, | ||
169 | struct cpuidle_driver *drv, | ||
170 | int index) | ||
171 | { | ||
172 | int new_index = index; | ||
173 | |||
174 | /* AFTR can only be entered when cores other than CPU0 are offline */ | ||
175 | if (num_online_cpus() > 1 || dev->cpu != 0) | ||
176 | new_index = drv->safe_state_index; | ||
177 | |||
178 | if (new_index == 0) | ||
179 | return arm_cpuidle_simple_enter(dev, drv, new_index); | ||
180 | else | ||
181 | return exynos4_enter_core0_aftr(dev, drv, new_index); | ||
182 | } | ||
183 | |||
184 | static void __init exynos5_core_down_clk(void) | ||
185 | { | ||
186 | unsigned int tmp; | ||
187 | |||
188 | /* | ||
189 | * Enable arm clock down (in idle) and set arm divider | ||
190 | * ratios in WFI/WFE state. | ||
191 | */ | ||
192 | tmp = PWR_CTRL1_CORE2_DOWN_RATIO | \ | ||
193 | PWR_CTRL1_CORE1_DOWN_RATIO | \ | ||
194 | PWR_CTRL1_DIV2_DOWN_EN | \ | ||
195 | PWR_CTRL1_DIV1_DOWN_EN | \ | ||
196 | PWR_CTRL1_USE_CORE1_WFE | \ | ||
197 | PWR_CTRL1_USE_CORE0_WFE | \ | ||
198 | PWR_CTRL1_USE_CORE1_WFI | \ | ||
199 | PWR_CTRL1_USE_CORE0_WFI; | ||
200 | __raw_writel(tmp, EXYNOS5_PWR_CTRL1); | ||
201 | |||
202 | /* | ||
203 | * Enable arm clock up (on exiting idle). Set arm divider | ||
204 | * ratios when not in idle along with the standby duration | ||
205 | * ratios. | ||
206 | */ | ||
207 | tmp = PWR_CTRL2_DIV2_UP_EN | \ | ||
208 | PWR_CTRL2_DIV1_UP_EN | \ | ||
209 | PWR_CTRL2_DUR_STANDBY2_VAL | \ | ||
210 | PWR_CTRL2_DUR_STANDBY1_VAL | \ | ||
211 | PWR_CTRL2_CORE2_UP_RATIO | \ | ||
212 | PWR_CTRL2_CORE1_UP_RATIO; | ||
213 | __raw_writel(tmp, EXYNOS5_PWR_CTRL2); | ||
214 | } | ||
215 | |||
216 | static int exynos_cpuidle_probe(struct platform_device *pdev) | ||
217 | { | ||
218 | int cpu_id, ret; | ||
219 | struct cpuidle_device *device; | ||
220 | |||
221 | if (soc_is_exynos5250()) | ||
222 | exynos5_core_down_clk(); | ||
223 | |||
224 | if (soc_is_exynos5440()) | ||
225 | exynos4_idle_driver.state_count = 1; | ||
226 | |||
227 | ret = cpuidle_register_driver(&exynos4_idle_driver); | ||
228 | if (ret) { | ||
229 | dev_err(&pdev->dev, "failed to register cpuidle driver\n"); | ||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | for_each_online_cpu(cpu_id) { | ||
234 | device = &per_cpu(exynos4_cpuidle_device, cpu_id); | ||
235 | device->cpu = cpu_id; | ||
236 | |||
237 | ret = cpuidle_register_device(device); | ||
238 | if (ret) { | ||
239 | dev_err(&pdev->dev, "failed to register cpuidle device\n"); | ||
240 | return ret; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static struct platform_driver exynos_cpuidle_driver = { | ||
248 | .probe = exynos_cpuidle_probe, | ||
249 | .driver = { | ||
250 | .name = "exynos_cpuidle", | ||
251 | .owner = THIS_MODULE, | ||
252 | }, | ||
253 | }; | ||
254 | |||
255 | module_platform_driver(exynos_cpuidle_driver); | ||