diff options
author | Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> | 2015-01-24 00:05:50 -0500 |
---|---|---|
committer | Kukjin Kim <kgene@kernel.org> | 2015-01-29 18:39:15 -0500 |
commit | 712eddf70225ab5ae65e946e22d2dfe6b93e8dd1 (patch) | |
tree | 62477a0a4b364cd2e87786481ee6dfe03e3fd569 /arch/arm/mach-exynos/pm.c | |
parent | 865e8b76a04d018f23d809ebf735c52105e3adb2 (diff) |
cpuidle: exynos: add coupled cpuidle support for exynos4210
The following patch adds coupled cpuidle support for Exynos4210 to
an existing cpuidle-exynos driver. As a result it enables AFTR mode
to be used by default on Exynos4210 without the need to hot unplug
CPU1 first.
The patch is heavily based on earlier cpuidle-exynos4210 driver from
Daniel Lezcano:
http://www.spinics.net/lists/linux-samsung-soc/msg28134.html
Changes from Daniel's code include:
- porting code to current kernels
- fixing it to work on my setup (by using S5P_INFORM register
instead of S5P_VA_SYSRAM one on Revison 1.1 and retrying poking
CPU1 out of the BOOT ROM if necessary)
- fixing rare lockup caused by waiting for CPU1 to get stuck in
the BOOT ROM (CPU hotplug code in arch/arm/mach-exynos/platsmp.c
doesn't require this and works fine)
- moving Exynos specific code to arch/arm/mach-exynos/pm.c
- using cpu_boot_reg_base() helper instead of BOOT_VECTOR macro
- using exynos_cpu_*() helpers instead of accessing registers
directly
- using arch_send_wakeup_ipi_mask() instead of dsb_sev()
(this matches CPU hotplug code in arch/arm/mach-exynos/platsmp.c)
- integrating separate exynos4210-cpuidle driver into existing
exynos-cpuidle one
Cc: Colin Cross <ccross@google.com>
Cc: Kukjin Kim <kgene.kim@samsung.com>
Cc: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: Tomasz Figa <tomasz.figa@gmail.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Kukjin Kim <kgene@kernel.org>
Diffstat (limited to 'arch/arm/mach-exynos/pm.c')
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 159eb4c9779e..1f9cbd434c60 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c | |||
@@ -179,3 +179,125 @@ void exynos_enter_aftr(void) | |||
179 | 179 | ||
180 | cpu_pm_exit(); | 180 | cpu_pm_exit(); |
181 | } | 181 | } |
182 | |||
183 | static atomic_t cpu1_wakeup = ATOMIC_INIT(0); | ||
184 | |||
185 | static int exynos_cpu0_enter_aftr(void) | ||
186 | { | ||
187 | int ret = -1; | ||
188 | |||
189 | /* | ||
190 | * If the other cpu is powered on, we have to power it off, because | ||
191 | * the AFTR state won't work otherwise | ||
192 | */ | ||
193 | if (cpu_online(1)) { | ||
194 | /* | ||
195 | * We reach a sync point with the coupled idle state, we know | ||
196 | * the other cpu will power down itself or will abort the | ||
197 | * sequence, let's wait for one of these to happen | ||
198 | */ | ||
199 | while (exynos_cpu_power_state(1)) { | ||
200 | /* | ||
201 | * The other cpu may skip idle and boot back | ||
202 | * up again | ||
203 | */ | ||
204 | if (atomic_read(&cpu1_wakeup)) | ||
205 | goto abort; | ||
206 | |||
207 | /* | ||
208 | * The other cpu may bounce through idle and | ||
209 | * boot back up again, getting stuck in the | ||
210 | * boot rom code | ||
211 | */ | ||
212 | if (__raw_readl(cpu_boot_reg_base()) == 0) | ||
213 | goto abort; | ||
214 | |||
215 | cpu_relax(); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | exynos_enter_aftr(); | ||
220 | ret = 0; | ||
221 | |||
222 | abort: | ||
223 | if (cpu_online(1)) { | ||
224 | /* | ||
225 | * Set the boot vector to something non-zero | ||
226 | */ | ||
227 | __raw_writel(virt_to_phys(exynos_cpu_resume), | ||
228 | cpu_boot_reg_base()); | ||
229 | dsb(); | ||
230 | |||
231 | /* | ||
232 | * Turn on cpu1 and wait for it to be on | ||
233 | */ | ||
234 | exynos_cpu_power_up(1); | ||
235 | while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN) | ||
236 | cpu_relax(); | ||
237 | |||
238 | while (!atomic_read(&cpu1_wakeup)) { | ||
239 | /* | ||
240 | * Poke cpu1 out of the boot rom | ||
241 | */ | ||
242 | __raw_writel(virt_to_phys(exynos_cpu_resume), | ||
243 | cpu_boot_reg_base()); | ||
244 | |||
245 | arch_send_wakeup_ipi_mask(cpumask_of(1)); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | return ret; | ||
250 | } | ||
251 | |||
252 | static int exynos_wfi_finisher(unsigned long flags) | ||
253 | { | ||
254 | cpu_do_idle(); | ||
255 | |||
256 | return -1; | ||
257 | } | ||
258 | |||
259 | static int exynos_cpu1_powerdown(void) | ||
260 | { | ||
261 | int ret = -1; | ||
262 | |||
263 | /* | ||
264 | * Idle sequence for cpu1 | ||
265 | */ | ||
266 | if (cpu_pm_enter()) | ||
267 | goto cpu1_aborted; | ||
268 | |||
269 | /* | ||
270 | * Turn off cpu 1 | ||
271 | */ | ||
272 | exynos_cpu_power_down(1); | ||
273 | |||
274 | ret = cpu_suspend(0, exynos_wfi_finisher); | ||
275 | |||
276 | cpu_pm_exit(); | ||
277 | |||
278 | cpu1_aborted: | ||
279 | dsb(); | ||
280 | /* | ||
281 | * Notify cpu 0 that cpu 1 is awake | ||
282 | */ | ||
283 | atomic_set(&cpu1_wakeup, 1); | ||
284 | |||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | static void exynos_pre_enter_aftr(void) | ||
289 | { | ||
290 | __raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base()); | ||
291 | } | ||
292 | |||
293 | static void exynos_post_enter_aftr(void) | ||
294 | { | ||
295 | atomic_set(&cpu1_wakeup, 0); | ||
296 | } | ||
297 | |||
298 | struct cpuidle_exynos_data cpuidle_coupled_exynos_data = { | ||
299 | .cpu0_enter_aftr = exynos_cpu0_enter_aftr, | ||
300 | .cpu1_powerdown = exynos_cpu1_powerdown, | ||
301 | .pre_enter_aftr = exynos_pre_enter_aftr, | ||
302 | .post_enter_aftr = exynos_post_enter_aftr, | ||
303 | }; | ||