blob: cccba2dd43901d526ab82183f9743501138776ce [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
29#ifdef CONFIG_MSM_SLEEP_STATS
30static DEFINE_PER_CPU(struct atomic_notifier_head, msm_cpuidle_notifiers);
31
32int msm_cpuidle_register_notifier(unsigned int cpu, struct notifier_block *nb)
33{
34 struct atomic_notifier_head *head =
35 &per_cpu(msm_cpuidle_notifiers, cpu);
36
37 return atomic_notifier_chain_register(head, nb);
38}
39EXPORT_SYMBOL(msm_cpuidle_register_notifier);
40
41int msm_cpuidle_unregister_notifier(unsigned int cpu, struct notifier_block *nb)
42{
43 struct atomic_notifier_head *head =
44 &per_cpu(msm_cpuidle_notifiers, cpu);
45
46 return atomic_notifier_chain_unregister(head, nb);
47}
48EXPORT_SYMBOL(msm_cpuidle_unregister_notifier);
49#endif
50
51static int msm_cpuidle_enter(
52 struct cpuidle_device *dev, struct cpuidle_state *state)
53{
54 int ret;
55#ifdef CONFIG_MSM_SLEEP_STATS
56 struct atomic_notifier_head *head =
57 &__get_cpu_var(msm_cpuidle_notifiers);
58#endif
59
60 local_irq_disable();
61
62#ifdef CONFIG_MSM_SLEEP_STATS
63 atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_ENTER, NULL);
64#endif
65
Ashwin Chaugule464983a2011-11-21 14:51:51 -050066#ifdef CONFIG_CPU_PM
67 cpu_pm_enter();
68#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069 ret = msm_pm_idle_enter((enum msm_pm_sleep_mode) (state->driver_data));
70
Ashwin Chaugule464983a2011-11-21 14:51:51 -050071#ifdef CONFIG_CPU_PM
72 cpu_pm_exit();
73#endif
74
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075#ifdef CONFIG_MSM_SLEEP_STATS
76 atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_EXIT, NULL);
77#endif
78
79 local_irq_enable();
80
81 return ret;
82}
83
84void __init msm_cpuidle_set_states(struct msm_cpuidle_state *states,
85 int nr_states, struct msm_pm_platform_data *pm_data)
86{
87 unsigned int cpu;
88
89 for_each_possible_cpu(cpu) {
90 struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
91 int i;
92
93 dev->cpu = cpu;
94 dev->prepare = msm_pm_idle_prepare;
95
96 for (i = 0; i < nr_states; i++) {
97 struct msm_cpuidle_state *cstate = &states[i];
98 struct cpuidle_state *state;
99 struct msm_pm_platform_data *pm_mode;
100
101 if (cstate->cpu != cpu)
102 continue;
103
104 state = &dev->states[cstate->state_nr];
105 pm_mode = &pm_data[MSM_PM_MODE(cpu, cstate->mode_nr)];
106
107 snprintf(state->name, CPUIDLE_NAME_LEN, cstate->name);
108 snprintf(state->desc, CPUIDLE_DESC_LEN, cstate->desc);
109 state->driver_data = (void *) cstate->mode_nr;
110 state->flags = CPUIDLE_FLAG_TIME_VALID;
111 state->exit_latency = pm_mode->latency;
112 state->power_usage = 0;
113 state->target_residency = pm_mode->residency;
114 state->enter = msm_cpuidle_enter;
115 }
116
117 for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
118 if (dev->states[i].enter == NULL)
119 break;
120 dev->state_count = i + 1;
121 }
122 }
123}
124
125int __init msm_cpuidle_init(void)
126{
127 unsigned int cpu;
128 int ret;
129
130 ret = cpuidle_register_driver(&msm_cpuidle_driver);
131 if (ret)
132 pr_err("%s: failed to register cpuidle driver: %d\n",
133 __func__, ret);
134
135 for_each_possible_cpu(cpu) {
136 struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
137
138 ret = cpuidle_register_device(dev);
139 if (ret) {
140 pr_err("%s: failed to register cpuidle device for "
141 "cpu %u: %d\n", __func__, cpu, ret);
142 return ret;
143 }
144 }
145
146 return 0;
147}
148
149static int __init msm_cpuidle_early_init(void)
150{
151#ifdef CONFIG_MSM_SLEEP_STATS
152 unsigned int cpu;
153
154 for_each_possible_cpu(cpu)
155 ATOMIC_INIT_NOTIFIER_HEAD(&per_cpu(msm_cpuidle_notifiers, cpu));
156#endif
157 return 0;
158}
159
160early_initcall(msm_cpuidle_early_init);