blob: 4ba3f95ca9577434de544d504792c6246884eb3d [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
33 {0, 1, "C1", "STANDALONE_POWER_COLLAPSE",
34 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
35
36 {0, 2, "C2", "POWER_COLLAPSE",
37 MSM_PM_SLEEP_MODE_POWER_COLLAPSE},
38
39 {1, 0, "C0", "WFI",
40 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
41
42 {1, 1, "C1", "STANDALONE_POWER_COLLAPSE",
43 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
44
45 {2, 0, "C0", "WFI",
46 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
47
48 {2, 1, "C1", "STANDALONE_POWER_COLLAPSE",
49 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
50
51 {3, 0, "C0", "WFI",
52 MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
53
54 {3, 1, "C1", "STANDALONE_POWER_COLLAPSE",
55 MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
56};
57
58
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059#ifdef CONFIG_MSM_SLEEP_STATS
60static DEFINE_PER_CPU(struct atomic_notifier_head, msm_cpuidle_notifiers);
61
62int msm_cpuidle_register_notifier(unsigned int cpu, struct notifier_block *nb)
63{
64 struct atomic_notifier_head *head =
65 &per_cpu(msm_cpuidle_notifiers, cpu);
66
67 return atomic_notifier_chain_register(head, nb);
68}
69EXPORT_SYMBOL(msm_cpuidle_register_notifier);
70
71int msm_cpuidle_unregister_notifier(unsigned int cpu, struct notifier_block *nb)
72{
73 struct atomic_notifier_head *head =
74 &per_cpu(msm_cpuidle_notifiers, cpu);
75
76 return atomic_notifier_chain_unregister(head, nb);
77}
78EXPORT_SYMBOL(msm_cpuidle_unregister_notifier);
79#endif
80
81static int msm_cpuidle_enter(
82 struct cpuidle_device *dev, struct cpuidle_state *state)
83{
84 int ret;
85#ifdef CONFIG_MSM_SLEEP_STATS
86 struct atomic_notifier_head *head =
87 &__get_cpu_var(msm_cpuidle_notifiers);
88#endif
89
90 local_irq_disable();
91
92#ifdef CONFIG_MSM_SLEEP_STATS
93 atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_ENTER, NULL);
94#endif
95
Ashwin Chaugule464983a2011-11-21 14:51:51 -050096#ifdef CONFIG_CPU_PM
97 cpu_pm_enter();
98#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099 ret = msm_pm_idle_enter((enum msm_pm_sleep_mode) (state->driver_data));
100
Ashwin Chaugule464983a2011-11-21 14:51:51 -0500101#ifdef CONFIG_CPU_PM
102 cpu_pm_exit();
103#endif
104
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105#ifdef CONFIG_MSM_SLEEP_STATS
106 atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_EXIT, NULL);
107#endif
108
109 local_irq_enable();
110
111 return ret;
112}
113
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600114static void __init msm_cpuidle_set_states(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115{
116 unsigned int cpu;
117
118 for_each_possible_cpu(cpu) {
119 struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
120 int i;
121
122 dev->cpu = cpu;
123 dev->prepare = msm_pm_idle_prepare;
124
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600125 for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) {
126 struct msm_cpuidle_state *cstate = &msm_cstates[i];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 struct cpuidle_state *state;
128 struct msm_pm_platform_data *pm_mode;
129
130 if (cstate->cpu != cpu)
131 continue;
132
133 state = &dev->states[cstate->state_nr];
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600134 pm_mode = &msm_pm_sleep_modes[
135 MSM_PM_MODE(cpu, cstate->mode_nr)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136
137 snprintf(state->name, CPUIDLE_NAME_LEN, cstate->name);
138 snprintf(state->desc, CPUIDLE_DESC_LEN, cstate->desc);
139 state->driver_data = (void *) cstate->mode_nr;
140 state->flags = CPUIDLE_FLAG_TIME_VALID;
141 state->exit_latency = pm_mode->latency;
142 state->power_usage = 0;
143 state->target_residency = pm_mode->residency;
144 state->enter = msm_cpuidle_enter;
145 }
146
147 for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
148 if (dev->states[i].enter == NULL)
149 break;
150 dev->state_count = i + 1;
151 }
152 }
153}
154
155int __init msm_cpuidle_init(void)
156{
157 unsigned int cpu;
158 int ret;
159
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600160 msm_cpuidle_set_states();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 ret = cpuidle_register_driver(&msm_cpuidle_driver);
162 if (ret)
163 pr_err("%s: failed to register cpuidle driver: %d\n",
164 __func__, ret);
165
166 for_each_possible_cpu(cpu) {
167 struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
168
169 ret = cpuidle_register_device(dev);
170 if (ret) {
171 pr_err("%s: failed to register cpuidle device for "
172 "cpu %u: %d\n", __func__, cpu, ret);
173 return ret;
174 }
175 }
176
177 return 0;
178}
179
180static int __init msm_cpuidle_early_init(void)
181{
182#ifdef CONFIG_MSM_SLEEP_STATS
183 unsigned int cpu;
184
185 for_each_possible_cpu(cpu)
186 ATOMIC_INIT_NOTIFIER_HEAD(&per_cpu(msm_cpuidle_notifiers, cpu));
187#endif
188 return 0;
189}
190
191early_initcall(msm_cpuidle_early_init);