]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - arch/arm/mach-exynos/cpuidle.c
ARM: EXYNOS: Move scu_enable in the cpu_pm notifier
[karo-tx-linux.git] / arch / arm / mach-exynos / cpuidle.c
1 /* linux/arch/arm/mach-exynos/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/suspend.h>
23 #include <asm/unified.h>
24 #include <asm/cpuidle.h>
25
26 #include <plat/cpu.h>
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 /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
44 static void exynos_set_wakeupmask(void)
45 {
46         __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK);
47 }
48
49 static int idle_finisher(unsigned long flags)
50 {
51         exynos_set_wakeupmask();
52
53         __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR);
54         __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG);
55
56         /* Set value of power down register for aftr mode */
57         exynos_sys_powerdown_conf(SYS_AFTR);
58
59         cpu_do_idle();
60
61         return 1;
62 }
63
64 static int exynos_enter_core0_aftr(struct cpuidle_device *dev,
65                                 struct cpuidle_driver *drv,
66                                 int index)
67 {
68         unsigned long tmp;
69
70         /* Setting Central Sequence Register for power down mode */
71         tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
72         tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
73         __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
74
75         cpu_pm_enter();
76         cpu_suspend(0, idle_finisher);
77         cpu_pm_exit();
78
79         /*
80          * If PMU failed while entering sleep mode, WFI will be
81          * ignored by PMU and then exiting cpu_do_idle().
82          * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
83          * in this situation.
84          */
85         tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
86         if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
87                 tmp |= S5P_CENTRAL_LOWPWR_CFG;
88                 __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
89                 /* Clear wakeup state register */
90                 __raw_writel(0x0, S5P_WAKEUP_STAT);
91         }
92
93         return index;
94 }
95
96 static int exynos_enter_lowpower(struct cpuidle_device *dev,
97                                 struct cpuidle_driver *drv,
98                                 int index)
99 {
100         int new_index = index;
101
102         /* AFTR can only be entered when cores other than CPU0 are offline */
103         if (num_online_cpus() > 1 || dev->cpu != 0)
104                 new_index = drv->safe_state_index;
105
106         if (new_index == 0)
107                 return arm_cpuidle_simple_enter(dev, drv, new_index);
108         else
109                 return exynos_enter_core0_aftr(dev, drv, new_index);
110 }
111
112 static struct cpuidle_driver exynos_idle_driver = {
113         .name                   = "exynos_idle",
114         .owner                  = THIS_MODULE,
115         .states = {
116                 [0] = ARM_CPUIDLE_WFI_STATE,
117                 [1] = {
118                         .enter                  = exynos_enter_lowpower,
119                         .exit_latency           = 300,
120                         .target_residency       = 100000,
121                         .flags                  = CPUIDLE_FLAG_TIME_VALID,
122                         .name                   = "C1",
123                         .desc                   = "ARM power down",
124                 },
125         },
126         .state_count = 2,
127         .safe_state_index = 0,
128 };
129
130 static int exynos_cpuidle_probe(struct platform_device *pdev)
131 {
132         int ret;
133
134         if (soc_is_exynos5440())
135                 exynos_idle_driver.state_count = 1;
136
137         ret = cpuidle_register(&exynos_idle_driver, NULL);
138         if (ret) {
139                 dev_err(&pdev->dev, "failed to register cpuidle driver\n");
140                 return ret;
141         }
142
143         return 0;
144 }
145
146 static struct platform_driver exynos_cpuidle_driver = {
147         .probe  = exynos_cpuidle_probe,
148         .driver = {
149                 .name = "exynos_cpuidle",
150                 .owner = THIS_MODULE,
151         },
152 };
153
154 module_platform_driver(exynos_cpuidle_driver);