blob: b68fdc1debcedae3ca55c11719342a2220daf18a [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);
24static struct cpuidle_driver msm_cpuidle_driver = {
25 .name = "msm_idle",
26 .owner = THIS_MODULE,
27};
28
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060029static struct msm_cpuidle_state msm_cstates[] = {
30 {0, 0, "C0", "WFI",
31 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
32
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060033 {0, 1, "C1", "RETENTION",
34 MSM_PM_SLEEP_MODE_RETENTION},
35
36 {0, 2, "C2", "STANDALONE_POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060037 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
38
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060039 {0, 3, "C3", "POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060040 MSM_PM_SLEEP_MODE_POWER_COLLAPSE},
41
42 {1, 0, "C0", "WFI",
43 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
44
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060045 {1, 1, "C1", "RETENTION",
46 MSM_PM_SLEEP_MODE_RETENTION},
47
48 {1, 2, "C2", "STANDALONE_POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060049 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
50
51 {2, 0, "C0", "WFI",
52 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
53
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060054 {2, 1, "C1", "RETENTION",
55 MSM_PM_SLEEP_MODE_RETENTION},
56
57 {2, 2, "C2", "STANDALONE_POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060058 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
59
60 {3, 0, "C0", "WFI",
61 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
62
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -060063 {3, 1, "C1", "RETENTION",
64 MSM_PM_SLEEP_MODE_RETENTION},
65
66 {3, 2, "C2", "STANDALONE_POWER_COLLAPSE",
Praveen Chidambaram42da9d22012-03-30 12:16:34 -060067 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
68};
69
70
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071#ifdef CONFIG_MSM_SLEEP_STATS
72static DEFINE_PER_CPU(struct atomic_notifier_head, msm_cpuidle_notifiers);
73
74int msm_cpuidle_register_notifier(unsigned int cpu, struct notifier_block *nb)
75{
76 struct atomic_notifier_head *head =
77 &per_cpu(msm_cpuidle_notifiers, cpu);
78
79 return atomic_notifier_chain_register(head, nb);
80}
81EXPORT_SYMBOL(msm_cpuidle_register_notifier);
82
83int msm_cpuidle_unregister_notifier(unsigned int cpu, struct notifier_block *nb)
84{
85 struct atomic_notifier_head *head =
86 &per_cpu(msm_cpuidle_notifiers, cpu);
87
88 return atomic_notifier_chain_unregister(head, nb);
89}
90EXPORT_SYMBOL(msm_cpuidle_unregister_notifier);
91#endif
92
93static int msm_cpuidle_enter(
94 struct cpuidle_device *dev, struct cpuidle_state *state)
95{
96 int ret;
97#ifdef CONFIG_MSM_SLEEP_STATS
98 struct atomic_notifier_head *head =
99 &__get_cpu_var(msm_cpuidle_notifiers);
100#endif
101
102 local_irq_disable();
103
104#ifdef CONFIG_MSM_SLEEP_STATS
105 atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_ENTER, NULL);
106#endif
107
Ashwin Chaugule464983a2011-11-21 14:51:51 -0500108#ifdef CONFIG_CPU_PM
109 cpu_pm_enter();
110#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 ret = msm_pm_idle_enter((enum msm_pm_sleep_mode) (state->driver_data));
112
Ashwin Chaugule464983a2011-11-21 14:51:51 -0500113#ifdef CONFIG_CPU_PM
114 cpu_pm_exit();
115#endif
116
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117#ifdef CONFIG_MSM_SLEEP_STATS
118 atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_EXIT, NULL);
119#endif
120
121 local_irq_enable();
122
123 return ret;
124}
125
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600126static void __init msm_cpuidle_set_states(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127{
128 unsigned int cpu;
129
130 for_each_possible_cpu(cpu) {
131 struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
132 int i;
133
134 dev->cpu = cpu;
135 dev->prepare = msm_pm_idle_prepare;
136
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600137 for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) {
138 struct msm_cpuidle_state *cstate = &msm_cstates[i];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139 struct cpuidle_state *state;
140 struct msm_pm_platform_data *pm_mode;
141
142 if (cstate->cpu != cpu)
143 continue;
144
145 state = &dev->states[cstate->state_nr];
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600146 pm_mode = &msm_pm_sleep_modes[
147 MSM_PM_MODE(cpu, cstate->mode_nr)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148
149 snprintf(state->name, CPUIDLE_NAME_LEN, cstate->name);
150 snprintf(state->desc, CPUIDLE_DESC_LEN, cstate->desc);
151 state->driver_data = (void *) cstate->mode_nr;
152 state->flags = CPUIDLE_FLAG_TIME_VALID;
153 state->exit_latency = pm_mode->latency;
154 state->power_usage = 0;
155 state->target_residency = pm_mode->residency;
156 state->enter = msm_cpuidle_enter;
157 }
158
159 for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
160 if (dev->states[i].enter == NULL)
161 break;
162 dev->state_count = i + 1;
163 }
164 }
165}
166
167int __init msm_cpuidle_init(void)
168{
169 unsigned int cpu;
170 int ret;
171
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600172 msm_cpuidle_set_states();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173 ret = cpuidle_register_driver(&msm_cpuidle_driver);
174 if (ret)
175 pr_err("%s: failed to register cpuidle driver: %d\n",
176 __func__, ret);
177
178 for_each_possible_cpu(cpu) {
179 struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
180
181 ret = cpuidle_register_device(dev);
182 if (ret) {
183 pr_err("%s: failed to register cpuidle device for "
184 "cpu %u: %d\n", __func__, cpu, ret);
185 return ret;
186 }
187 }
188
189 return 0;
190}
191
192static int __init msm_cpuidle_early_init(void)
193{
194#ifdef CONFIG_MSM_SLEEP_STATS
195 unsigned int cpu;
196
197 for_each_possible_cpu(cpu)
198 ATOMIC_INIT_NOTIFIER_HEAD(&per_cpu(msm_cpuidle_notifiers, cpu));
199#endif
200 return 0;
201}
202
203early_initcall(msm_cpuidle_early_init);