blob: 8f086aaa933421cc16b37cbe27eab8041edaf0e7 [file] [log] [blame]
Abhijeet Dharmapurikar44451662012-08-23 18:58:44 -07001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -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
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/mutex.h>
18#include <linux/kobject.h>
19#include <linux/cpufreq.h>
20#include <linux/platform_device.h>
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -070021#include <linux/cpu_pm.h>
22#include <linux/pm_qos.h>
23#include <linux/hrtimer.h>
24#include <linux/tick.h>
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -070025#include <mach/msm_dcvs.h>
26
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -070027struct cpu_idle_info {
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -070028 int enabled;
29 int dcvs_core_id;
30 struct pm_qos_request pm_qos_req;
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -070031};
32
33static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
34static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -070035static uint32_t latency;
36
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -070037static int msm_dcvs_idle_notifier(int core_num,
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -070038 enum msm_core_control_event event)
39{
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -070040 struct cpu_idle_info *info = &per_cpu(cpu_idle_info, core_num);
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -070041
42 switch (event) {
43 case MSM_DCVS_ENABLE_IDLE_PULSE:
44 info->enabled = true;
45 break;
46
47 case MSM_DCVS_DISABLE_IDLE_PULSE:
48 info->enabled = false;
49 break;
50
51 case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
52 pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE);
53 break;
54
55 case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
56 pm_qos_update_request(&info->pm_qos_req, latency);
57 break;
58 }
59
60 return 0;
61}
62
63static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
64 void *v)
65{
66 struct cpu_idle_info *info =
67 &per_cpu(cpu_idle_info, smp_processor_id());
68 u64 io_wait_us = 0;
69 u64 prev_io_wait_us = 0;
70 u64 last_update_time = 0;
71 u64 val = 0;
72 uint32_t iowaited = 0;
73
74 if (!info->enabled)
75 return NOTIFY_OK;
76
77 switch (cmd) {
78 case CPU_PM_ENTER:
79 val = get_cpu_iowait_time_us(smp_processor_id(),
80 &last_update_time);
81 /* val could be -1 when NOHZ is not enabled */
82 if (val == (u64)-1)
83 val = 0;
84 per_cpu(iowait_on_cpu, smp_processor_id()) = val;
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -070085 msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_ENTER, 0);
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -070086 break;
87
88 case CPU_PM_EXIT:
89 prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
90 val = get_cpu_iowait_time_us(smp_processor_id(),
91 &last_update_time);
92 if (val == (u64)-1)
93 val = 0;
94 io_wait_us = val;
95 iowaited = (io_wait_us - prev_io_wait_us);
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -070096 msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_EXIT, iowaited);
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -070097 break;
98 }
99
100 return NOTIFY_OK;
101}
102
103static struct notifier_block idle_nb = {
104 .notifier_call = msm_cpuidle_notifier,
105};
106
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700107static void msm_gov_idle_source_init(int cpu, int dcvs_core_id)
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700108{
109 struct cpu_idle_info *info = NULL;
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700110
111 info = &per_cpu(cpu_idle_info, cpu);
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700112 info->dcvs_core_id = dcvs_core_id;
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700113
114 pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
115 PM_QOS_DEFAULT_VALUE);
116}
117
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700118struct msm_gov {
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700119 int cpu;
120 unsigned int cur_freq;
121 unsigned int min_freq;
122 unsigned int max_freq;
123 struct cpufreq_policy *policy;
124 int dcvs_core_id;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700125};
126
127static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex);
128static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700129
130static void msm_gov_check_limits(struct cpufreq_policy *policy)
131{
132 struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
133
134 if (policy->max < gov->cur_freq)
135 __cpufreq_driver_target(policy, policy->max,
136 CPUFREQ_RELATION_H);
Abhijeet Dharmapurikar68c970e2012-08-31 20:42:53 -0700137 else if (policy->min > gov->cur_freq)
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700138 __cpufreq_driver_target(policy, policy->min,
139 CPUFREQ_RELATION_L);
140 else
141 __cpufreq_driver_target(policy, gov->cur_freq,
142 CPUFREQ_RELATION_L);
143
144 gov->cur_freq = policy->cur;
145 gov->min_freq = policy->min;
146 gov->max_freq = policy->max;
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700147 msm_dcvs_update_limits(gov->dcvs_core_id);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700148}
149
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700150static int msm_dcvs_freq_set(int core_num,
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700151 unsigned int freq)
152{
153 int ret = -EINVAL;
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700154 struct msm_gov *gov = &per_cpu(msm_gov_info, core_num);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700155
156 mutex_lock(&per_cpu(gov_mutex, gov->cpu));
157
158 if (freq < gov->min_freq)
159 freq = gov->min_freq;
160 if (freq > gov->max_freq)
161 freq = gov->max_freq;
162
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700163 mutex_unlock(&per_cpu(gov_mutex, gov->cpu));
164
Abhijeet Dharmapurikar54fc08e2012-08-26 20:33:43 -0700165 ret = cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
166
167 if (!ret) {
168 gov->cur_freq = cpufreq_quick_get(gov->cpu);
169 if (freq != gov->cur_freq)
170 pr_err("cpu %d freq %u gov->cur_freq %u didn't match",
171 gov->cpu, freq, gov->cur_freq);
172 }
173 ret = gov->cur_freq;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700174
175 return ret;
176}
177
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700178static unsigned int msm_dcvs_freq_get(int core_num)
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700179{
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700180 struct msm_gov *gov = &per_cpu(msm_gov_info, core_num);
Abhijeet Dharmapurikar54fc08e2012-08-26 20:33:43 -0700181 /*
182 * the rw_sem in cpufreq is always held when this is called.
183 * The policy->cur won't be updated in this case - so it is safe to
184 * access policy->cur
185 */
Abhijeet Dharmapurikar68c970e2012-08-31 20:42:53 -0700186 return gov->policy->cur;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700187}
188
189static int cpufreq_governor_msm(struct cpufreq_policy *policy,
190 unsigned int event)
191{
192 unsigned int cpu = policy->cpu;
193 int ret = 0;
194 int handle = 0;
195 struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700196
197 switch (event) {
198 case CPUFREQ_GOV_START:
199 if (!cpu_online(cpu))
200 return -EINVAL;
201 BUG_ON(!policy->cur);
202 mutex_lock(&per_cpu(gov_mutex, cpu));
203 per_cpu(msm_gov_info, cpu).cpu = cpu;
204 gov->policy = policy;
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700205 handle = msm_dcvs_freq_sink_start(gov->dcvs_core_id);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700206 BUG_ON(handle < 0);
207 msm_gov_check_limits(policy);
208 mutex_unlock(&per_cpu(gov_mutex, cpu));
209 break;
210
211 case CPUFREQ_GOV_STOP:
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700212 msm_dcvs_freq_sink_stop(gov->dcvs_core_id);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700213 break;
214
215 case CPUFREQ_GOV_LIMITS:
216 mutex_lock(&per_cpu(gov_mutex, cpu));
217 msm_gov_check_limits(policy);
218 mutex_unlock(&per_cpu(gov_mutex, cpu));
219 break;
220 };
221
222 return ret;
223}
224
225struct cpufreq_governor cpufreq_gov_msm = {
226 .name = "msm-dcvs",
227 .governor = cpufreq_governor_msm,
228 .owner = THIS_MODULE,
229};
230
231static int __devinit msm_gov_probe(struct platform_device *pdev)
232{
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700233 int cpu;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700234 struct msm_dcvs_core_info *core = NULL;
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700235 struct msm_dcvs_core_info *core_info = NULL;
236 struct msm_gov_platform_data *pdata = pdev->dev.platform_data;
Abhijeet Dharmapurikarfc7dca42012-08-26 18:27:53 -0700237 int sensor = 0;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700238
239 core = pdev->dev.platform_data;
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700240 core_info = pdata->info;
241 latency = pdata->latency;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700242
243 for_each_possible_cpu(cpu) {
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700244 struct msm_gov *gov = &per_cpu(msm_gov_info, cpu);
245
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700246 mutex_init(&per_cpu(gov_mutex, cpu));
Abhijeet Dharmapurikarfc7dca42012-08-26 18:27:53 -0700247 if (cpu < core->num_cores)
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700248 sensor = core_info->sensors[cpu];
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700249 gov->dcvs_core_id = msm_dcvs_register_core(
250 MSM_DCVS_CORE_TYPE_CPU,
251 cpu,
252 core_info,
Abhijeet Dharmapurikar69134112012-08-31 22:10:41 -0700253 msm_dcvs_freq_set,
254 msm_dcvs_freq_get,
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700255 msm_dcvs_idle_notifier,
Steve Muckle682c7a02012-11-12 14:20:39 -0800256 NULL,
Abhijeet Dharmapurikar69134112012-08-31 22:10:41 -0700257 sensor);
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700258 if (gov->dcvs_core_id < 0) {
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700259 pr_err("Unable to register core for %d\n", cpu);
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700260 return -EINVAL;
261 }
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700262
Abhijeet Dharmapurikar1bbc0322012-09-12 16:40:20 -0700263 msm_gov_idle_source_init(cpu, gov->dcvs_core_id);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700264 }
265
Abhijeet Dharmapurikarc1ed66c2012-09-10 16:03:39 -0700266 cpu_pm_register_notifier(&idle_nb);
267
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700268 return cpufreq_register_governor(&cpufreq_gov_msm);
269}
270
271static int __devexit msm_gov_remove(struct platform_device *pdev)
272{
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700273 platform_set_drvdata(pdev, NULL);
274 return 0;
275}
276
277static struct platform_driver msm_gov_driver = {
278 .probe = msm_gov_probe,
279 .remove = __devexit_p(msm_gov_remove),
280 .driver = {
281 .name = "msm_dcvs_gov",
282 .owner = THIS_MODULE,
283 },
284};
285
286static int __init cpufreq_gov_msm_init(void)
287{
288 return platform_driver_register(&msm_gov_driver);
289}
290late_initcall(cpufreq_gov_msm_init);