blob: e4ec4d48c2b31470176d4106e75aed69ec4ff6d3 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/cpuidle.h>
Ashwin Chaugule464983a2011-11-21 14:51:51 -050017#include <linux/cpu_pm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018
Abhijeet Dharmapurikarefaca4f2011-12-27 16:24:07 -080019#include <mach/cpuidle.h>
Matt Wagantall7cca4642012-02-01 16:43:24 -080020
21#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022
23static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpuidle_device, msm_cpuidle_devs);
Steve Mucklef132c6c2012-06-06 18:30:57 -070024
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025static struct cpuidle_driver msm_cpuidle_driver = {
26 .name = "msm_idle",
27 .owner = THIS_MODULE,
28};
29
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060030static struct msm_cpuidle_state msm_cstates[] = {
31 {0, 0, "C0", "WFI",
32 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
33
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060034 {0, 1, "C1", "RETENTION",
35 MSM_PM_SLEEP_MODE_RETENTION},
36
37 {0, 2, "C2", "STANDALONE_POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060038 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
39
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060040 {0, 3, "C3", "POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060041 MSM_PM_SLEEP_MODE_POWER_COLLAPSE},
42
43 {1, 0, "C0", "WFI",
44 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
45
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060046 {1, 1, "C1", "RETENTION",
47 MSM_PM_SLEEP_MODE_RETENTION},
48
49 {1, 2, "C2", "STANDALONE_POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060050 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
51
52 {2, 0, "C0", "WFI",
53 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
54
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060055 {2, 1, "C1", "RETENTION",
56 MSM_PM_SLEEP_MODE_RETENTION},
57
58 {2, 2, "C2", "STANDALONE_POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060059 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
60
61 {3, 0, "C0", "WFI",
62 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
63
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060064 {3, 1, "C1", "RETENTION",
65 MSM_PM_SLEEP_MODE_RETENTION},
66
67 {3, 2, "C2", "STANDALONE_POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060068 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
69};
70
71
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072#ifdef CONFIG_MSM_SLEEP_STATS
73static DEFINE_PER_CPU(struct atomic_notifier_head, msm_cpuidle_notifiers);
74
75int msm_cpuidle_register_notifier(unsigned int cpu, struct notifier_block *nb)
76{
77 struct atomic_notifier_head *head =
78 &per_cpu(msm_cpuidle_notifiers, cpu);
79
80 return atomic_notifier_chain_register(head, nb);
81}
82EXPORT_SYMBOL(msm_cpuidle_register_notifier);
83
84int msm_cpuidle_unregister_notifier(unsigned int cpu, struct notifier_block *nb)
85{
86 struct atomic_notifier_head *head =
87 &per_cpu(msm_cpuidle_notifiers, cpu);
88
89 return atomic_notifier_chain_unregister(head, nb);
90}
91EXPORT_SYMBOL(msm_cpuidle_unregister_notifier);
92#endif
93
94static int msm_cpuidle_enter(
Steve Mucklef132c6c2012-06-06 18:30:57 -070095 struct cpuidle_device *dev, struct cpuidle_driver *drv, int index)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096{
Steve Mucklef132c6c2012-06-06 18:30:57 -070097 int ret = 0;
98 int i = 0;
99 enum msm_pm_sleep_mode pm_mode;
100 struct cpuidle_state_usage *st_usage = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101#ifdef CONFIG_MSM_SLEEP_STATS
102 struct atomic_notifier_head *head =
103 &__get_cpu_var(msm_cpuidle_notifiers);
104#endif
105
106 local_irq_disable();
107
108#ifdef CONFIG_MSM_SLEEP_STATS
109 atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_ENTER, NULL);
110#endif
111
Ashwin Chaugule464983a2011-11-21 14:51:51 -0500112#ifdef CONFIG_CPU_PM
113 cpu_pm_enter();
114#endif
Steve Mucklef132c6c2012-06-06 18:30:57 -0700115
116 pm_mode = msm_pm_idle_prepare(dev, drv, index);
117 msm_pm_idle_enter(pm_mode);
118 for (i = 0; i < dev->state_count; i++) {
119 st_usage = &dev->states_usage[i];
120 if ((enum msm_pm_sleep_mode) cpuidle_get_statedata(st_usage)
121 == pm_mode) {
122 ret = i;
123 break;
124 }
125 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126
Ashwin Chaugule464983a2011-11-21 14:51:51 -0500127#ifdef CONFIG_CPU_PM
128 cpu_pm_exit();
129#endif
130
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131#ifdef CONFIG_MSM_SLEEP_STATS
132 atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_EXIT, NULL);
133#endif
134
135 local_irq_enable();
136
137 return ret;
138}
139
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600140static void __init msm_cpuidle_set_states(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141{
Steve Mucklef132c6c2012-06-06 18:30:57 -0700142 int i = 0;
143 int state_count = 0;
144 struct msm_cpuidle_state *cstate = NULL;
145 struct cpuidle_state *state = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146
Steve Mucklef132c6c2012-06-06 18:30:57 -0700147 for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) {
148 cstate = &msm_cstates[i];
149 /* We have an asymmetric CPU C-State in MSMs.
150 * The primary CPU can do PC while all secondary cpus
151 * can only do standalone PC as part of their idle LPM.
152 * However, the secondary cpus can do PC when hotplugged
153 * We do not care about the hotplug here.
154 * Register the C-States available for Core0.
155 */
156 if (cstate->cpu)
157 continue;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158
Steve Mucklef132c6c2012-06-06 18:30:57 -0700159 state = &msm_cpuidle_driver.states[state_count];
160 snprintf(state->name, CPUIDLE_NAME_LEN, cstate->name);
161 snprintf(state->desc, CPUIDLE_DESC_LEN, cstate->desc);
162 state->flags = 0;
163 state->exit_latency = 0;
164 state->power_usage = 0;
165 state->target_residency = 0;
166 state->enter = msm_cpuidle_enter;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167
Steve Mucklef132c6c2012-06-06 18:30:57 -0700168 state_count++;
169 BUG_ON(state_count >= CPUIDLE_STATE_MAX);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170 }
Steve Mucklef132c6c2012-06-06 18:30:57 -0700171 msm_cpuidle_driver.state_count = state_count;
172 msm_cpuidle_driver.safe_state_index = 0;
173}
174
175static void __init msm_cpuidle_set_cpu_statedata(struct cpuidle_device *dev)
176{
177 int i = 0;
178 int state_count = 0;
179 struct cpuidle_state_usage *st_usage = NULL;
180 struct msm_cpuidle_state *cstate = NULL;
181
182 for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) {
183 cstate = &msm_cstates[i];
184 if (cstate->cpu != dev->cpu)
185 continue;
186
187 st_usage = &dev->states_usage[state_count];
188 cpuidle_set_statedata(st_usage, (void *)cstate->mode_nr);
189 state_count++;
190 BUG_ON(state_count > msm_cpuidle_driver.state_count);
191 }
192
193 dev->state_count = state_count; /* Per cpu state count */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194}
195
196int __init msm_cpuidle_init(void)
197{
Steve Mucklef132c6c2012-06-06 18:30:57 -0700198 unsigned int cpu = 0;
199 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600201 msm_cpuidle_set_states();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 ret = cpuidle_register_driver(&msm_cpuidle_driver);
203 if (ret)
204 pr_err("%s: failed to register cpuidle driver: %d\n",
205 __func__, ret);
206
207 for_each_possible_cpu(cpu) {
208 struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
209
Steve Mucklef132c6c2012-06-06 18:30:57 -0700210 dev->cpu = cpu;
211 msm_cpuidle_set_cpu_statedata(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212 ret = cpuidle_register_device(dev);
213 if (ret) {
214 pr_err("%s: failed to register cpuidle device for "
215 "cpu %u: %d\n", __func__, cpu, ret);
216 return ret;
217 }
218 }
219
220 return 0;
221}
222
223static int __init msm_cpuidle_early_init(void)
224{
225#ifdef CONFIG_MSM_SLEEP_STATS
226 unsigned int cpu;
227
228 for_each_possible_cpu(cpu)
229 ATOMIC_INIT_NOTIFIER_HEAD(&per_cpu(msm_cpuidle_notifiers, cpu));
230#endif
231 return 0;
232}
233
234early_initcall(msm_cpuidle_early_init);