blob: c480e56cbb4e3b9298140281ab7516122b5a477a [file] [log] [blame]
Praveen Chidambaramf27a5152013-02-01 11:44:53 -07001/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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>
17
Abhijeet Dharmapurikarefaca4f2011-12-27 16:24:07 -080018#include <mach/cpuidle.h>
Matt Wagantall7cca4642012-02-01 16:43:24 -080019
20#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021
22static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpuidle_device, msm_cpuidle_devs);
Steve Mucklef132c6c2012-06-06 18:30:57 -070023
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024static 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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070static int msm_cpuidle_enter(
Steve Mucklef132c6c2012-06-06 18:30:57 -070071 struct cpuidle_device *dev, struct cpuidle_driver *drv, int index)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072{
Steve Mucklef132c6c2012-06-06 18:30:57 -070073 int ret = 0;
Priyanka Mathur92fe5752013-01-17 10:58:04 -080074 int i;
Steve Mucklef132c6c2012-06-06 18:30:57 -070075 enum msm_pm_sleep_mode pm_mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076
Priyanka Mathur92fe5752013-01-17 10:58:04 -080077 pm_mode = msm_pm_idle_enter(dev, drv, index);
78
Steve Mucklef132c6c2012-06-06 18:30:57 -070079 for (i = 0; i < dev->state_count; i++) {
Priyanka Mathur92fe5752013-01-17 10:58:04 -080080 struct cpuidle_state_usage *st_usage = &dev->states_usage[i];
81 enum msm_pm_sleep_mode last_mode =
82 (enum msm_pm_sleep_mode)cpuidle_get_statedata(st_usage);
83
84 if (last_mode == pm_mode) {
Steve Mucklef132c6c2012-06-06 18:30:57 -070085 ret = i;
86 break;
87 }
88 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090 local_irq_enable();
91
92 return ret;
93}
94
Praveen Chidambaramf27a5152013-02-01 11:44:53 -070095static void __devinit msm_cpuidle_set_states(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096{
Steve Mucklef132c6c2012-06-06 18:30:57 -070097 int i = 0;
98 int state_count = 0;
99 struct msm_cpuidle_state *cstate = NULL;
100 struct cpuidle_state *state = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101
Steve Mucklef132c6c2012-06-06 18:30:57 -0700102 for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) {
103 cstate = &msm_cstates[i];
104 /* We have an asymmetric CPU C-State in MSMs.
105 * The primary CPU can do PC while all secondary cpus
106 * can only do standalone PC as part of their idle LPM.
107 * However, the secondary cpus can do PC when hotplugged
108 * We do not care about the hotplug here.
109 * Register the C-States available for Core0.
110 */
111 if (cstate->cpu)
112 continue;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
Steve Mucklef132c6c2012-06-06 18:30:57 -0700114 state = &msm_cpuidle_driver.states[state_count];
Murali Nalajalab0460b72013-10-24 07:54:18 +0530115 snprintf(state->name, CPUIDLE_NAME_LEN, "%s", cstate->name);
116 snprintf(state->desc, CPUIDLE_DESC_LEN, "%s", cstate->desc);
Steve Mucklef132c6c2012-06-06 18:30:57 -0700117 state->flags = 0;
118 state->exit_latency = 0;
119 state->power_usage = 0;
120 state->target_residency = 0;
121 state->enter = msm_cpuidle_enter;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122
Steve Mucklef132c6c2012-06-06 18:30:57 -0700123 state_count++;
124 BUG_ON(state_count >= CPUIDLE_STATE_MAX);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 }
Steve Mucklef132c6c2012-06-06 18:30:57 -0700126 msm_cpuidle_driver.state_count = state_count;
127 msm_cpuidle_driver.safe_state_index = 0;
128}
129
130static void __init msm_cpuidle_set_cpu_statedata(struct cpuidle_device *dev)
131{
132 int i = 0;
133 int state_count = 0;
134 struct cpuidle_state_usage *st_usage = NULL;
135 struct msm_cpuidle_state *cstate = NULL;
136
137 for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) {
138 cstate = &msm_cstates[i];
139 if (cstate->cpu != dev->cpu)
140 continue;
141
142 st_usage = &dev->states_usage[state_count];
143 cpuidle_set_statedata(st_usage, (void *)cstate->mode_nr);
144 state_count++;
145 BUG_ON(state_count > msm_cpuidle_driver.state_count);
146 }
147
148 dev->state_count = state_count; /* Per cpu state count */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149}
150
Praveen Chidambaramf27a5152013-02-01 11:44:53 -0700151int __devinit msm_cpuidle_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152{
Steve Mucklef132c6c2012-06-06 18:30:57 -0700153 unsigned int cpu = 0;
154 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600156 msm_cpuidle_set_states();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 ret = cpuidle_register_driver(&msm_cpuidle_driver);
158 if (ret)
159 pr_err("%s: failed to register cpuidle driver: %d\n",
160 __func__, ret);
161
162 for_each_possible_cpu(cpu) {
163 struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
164
Steve Mucklef132c6c2012-06-06 18:30:57 -0700165 dev->cpu = cpu;
166 msm_cpuidle_set_cpu_statedata(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 ret = cpuidle_register_device(dev);
168 if (ret) {
169 pr_err("%s: failed to register cpuidle device for "
170 "cpu %u: %d\n", __func__, cpu, ret);
171 return ret;
172 }
173 }
174
175 return 0;
176}