blob: 2b68d2a5b4463d5307d2b6488314235704664c6a [file] [log] [blame]
Abhijeet Dharmapurikar7e37e6e2012-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 Dharmapurikar6e9b34f2012-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 Dharmapurikar6e9b34f2012-09-10 16:03:39 -070027struct cpu_idle_info {
28 int cpu;
29 int enabled;
30 int handle;
31 struct msm_dcvs_idle dcvs_notifier;
32 struct pm_qos_request pm_qos_req;
33};
34
35static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
36static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
37static char core_name[NR_CPUS][10];
38static uint32_t latency;
39
40static int msm_dcvs_idle_notifier(struct msm_dcvs_idle *self,
41 enum msm_core_control_event event)
42{
43 struct cpu_idle_info *info = container_of(self,
44 struct cpu_idle_info, dcvs_notifier);
45
46 switch (event) {
47 case MSM_DCVS_ENABLE_IDLE_PULSE:
48 info->enabled = true;
49 break;
50
51 case MSM_DCVS_DISABLE_IDLE_PULSE:
52 info->enabled = false;
53 break;
54
55 case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
56 pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE);
57 break;
58
59 case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
60 pm_qos_update_request(&info->pm_qos_req, latency);
61 break;
62 }
63
64 return 0;
65}
66
67static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
68 void *v)
69{
70 struct cpu_idle_info *info =
71 &per_cpu(cpu_idle_info, smp_processor_id());
72 u64 io_wait_us = 0;
73 u64 prev_io_wait_us = 0;
74 u64 last_update_time = 0;
75 u64 val = 0;
76 uint32_t iowaited = 0;
77
78 if (!info->enabled)
79 return NOTIFY_OK;
80
81 switch (cmd) {
82 case CPU_PM_ENTER:
83 val = get_cpu_iowait_time_us(smp_processor_id(),
84 &last_update_time);
85 /* val could be -1 when NOHZ is not enabled */
86 if (val == (u64)-1)
87 val = 0;
88 per_cpu(iowait_on_cpu, smp_processor_id()) = val;
89 msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_ENTER, 0);
90 break;
91
92 case CPU_PM_EXIT:
93 prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
94 val = get_cpu_iowait_time_us(smp_processor_id(),
95 &last_update_time);
96 if (val == (u64)-1)
97 val = 0;
98 io_wait_us = val;
99 iowaited = (io_wait_us - prev_io_wait_us);
100 msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_EXIT, iowaited);
101 break;
102 }
103
104 return NOTIFY_OK;
105}
106
107static struct notifier_block idle_nb = {
108 .notifier_call = msm_cpuidle_notifier,
109};
110
111static void msm_gov_idle_source_init(int cpu)
112{
113 struct cpu_idle_info *info = NULL;
114 struct msm_dcvs_idle *inotify = NULL;
115
116 info = &per_cpu(cpu_idle_info, cpu);
117 info->cpu = cpu;
118 inotify = &info->dcvs_notifier;
119 snprintf(core_name[cpu], 10, "cpu%d", cpu);
120 inotify->core_name = core_name[cpu];
121 info->handle = msm_dcvs_idle_source_register(inotify);
122 BUG_ON(info->handle < 0);
123
124 pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
125 PM_QOS_DEFAULT_VALUE);
126}
127
128static int msm_gov_idle_source_uninit(int cpu)
129{
130 struct cpu_idle_info *info = NULL;
131 struct msm_dcvs_idle *inotify = NULL;
132
133 info = &per_cpu(cpu_idle_info, cpu);
134 info->cpu = cpu;
135 inotify = &info->dcvs_notifier;
136 return msm_dcvs_idle_source_unregister(inotify);
137}
138
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700139struct msm_gov {
140 int cpu;
141 unsigned int cur_freq;
142 unsigned int min_freq;
143 unsigned int max_freq;
144 struct msm_dcvs_freq gov_notifier;
145 struct cpufreq_policy *policy;
146};
147
148static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex);
149static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info);
150static char core_name[NR_CPUS][10];
151
152static void msm_gov_check_limits(struct cpufreq_policy *policy)
153{
154 struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700155 struct msm_dcvs_freq *dcvs_notifier =
156 &(per_cpu(msm_gov_info, policy->cpu).gov_notifier);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700157
158 if (policy->max < gov->cur_freq)
159 __cpufreq_driver_target(policy, policy->max,
160 CPUFREQ_RELATION_H);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700161 else if (policy->min > gov->cur_freq)
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700162 __cpufreq_driver_target(policy, policy->min,
163 CPUFREQ_RELATION_L);
164 else
165 __cpufreq_driver_target(policy, gov->cur_freq,
166 CPUFREQ_RELATION_L);
167
168 gov->cur_freq = policy->cur;
169 gov->min_freq = policy->min;
170 gov->max_freq = policy->max;
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700171 msm_dcvs_update_limits(dcvs_notifier);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700172}
173
174static int msm_dcvs_freq_set(struct msm_dcvs_freq *self,
175 unsigned int freq)
176{
177 int ret = -EINVAL;
178 struct msm_gov *gov =
179 container_of(self, struct msm_gov, gov_notifier);
180
181 mutex_lock(&per_cpu(gov_mutex, gov->cpu));
182
183 if (freq < gov->min_freq)
184 freq = gov->min_freq;
185 if (freq > gov->max_freq)
186 freq = gov->max_freq;
187
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700188 mutex_unlock(&per_cpu(gov_mutex, gov->cpu));
189
Abhijeet Dharmapurikar83347e02012-08-26 20:33:43 -0700190 ret = cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
191
192 if (!ret) {
193 gov->cur_freq = cpufreq_quick_get(gov->cpu);
194 if (freq != gov->cur_freq)
195 pr_err("cpu %d freq %u gov->cur_freq %u didn't match",
196 gov->cpu, freq, gov->cur_freq);
197 }
198 ret = gov->cur_freq;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700199
200 return ret;
201}
202
203static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self)
204{
205 struct msm_gov *gov =
206 container_of(self, struct msm_gov, gov_notifier);
207
Abhijeet Dharmapurikar83347e02012-08-26 20:33:43 -0700208 /*
209 * the rw_sem in cpufreq is always held when this is called.
210 * The policy->cur won't be updated in this case - so it is safe to
211 * access policy->cur
212 */
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700213 return gov->policy->cur;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700214}
215
216static int cpufreq_governor_msm(struct cpufreq_policy *policy,
217 unsigned int event)
218{
219 unsigned int cpu = policy->cpu;
220 int ret = 0;
221 int handle = 0;
222 struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
223 struct msm_dcvs_freq *dcvs_notifier =
224 &(per_cpu(msm_gov_info, cpu).gov_notifier);
225
226 switch (event) {
227 case CPUFREQ_GOV_START:
228 if (!cpu_online(cpu))
229 return -EINVAL;
230 BUG_ON(!policy->cur);
231 mutex_lock(&per_cpu(gov_mutex, cpu));
232 per_cpu(msm_gov_info, cpu).cpu = cpu;
233 gov->policy = policy;
234 dcvs_notifier->core_name = core_name[cpu];
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700235 handle = msm_dcvs_freq_sink_start(dcvs_notifier);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700236 BUG_ON(handle < 0);
237 msm_gov_check_limits(policy);
238 mutex_unlock(&per_cpu(gov_mutex, cpu));
239 break;
240
241 case CPUFREQ_GOV_STOP:
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700242 msm_dcvs_freq_sink_stop(dcvs_notifier);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700243 break;
244
245 case CPUFREQ_GOV_LIMITS:
246 mutex_lock(&per_cpu(gov_mutex, cpu));
247 msm_gov_check_limits(policy);
248 mutex_unlock(&per_cpu(gov_mutex, cpu));
249 break;
250 };
251
252 return ret;
253}
254
255struct cpufreq_governor cpufreq_gov_msm = {
256 .name = "msm-dcvs",
257 .governor = cpufreq_governor_msm,
258 .owner = THIS_MODULE,
259};
260
261static int __devinit msm_gov_probe(struct platform_device *pdev)
262{
263 int ret = 0;
264 int cpu;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700265 struct msm_dcvs_core_info *core = NULL;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700266 struct msm_dcvs_core_info *core_info = NULL;
267 struct msm_gov_platform_data *pdata = pdev->dev.platform_data;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700268 int sensor = 0;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700269
270 core = pdev->dev.platform_data;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700271 core_info = pdata->info;
272 latency = pdata->latency;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700273
274 for_each_possible_cpu(cpu) {
275 mutex_init(&per_cpu(gov_mutex, cpu));
276 snprintf(core_name[cpu], 10, "cpu%d", cpu);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700277 if (cpu < core->num_cores)
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700278 sensor = core_info->sensors[cpu];
279 ret = msm_dcvs_register_core(core_name[cpu], core_info,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700280 msm_dcvs_freq_set,
281 msm_dcvs_freq_get,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700282 msm_dcvs_idle_notifier,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700283 sensor);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700284 if (ret)
285 pr_err("Unable to register core for %d\n", cpu);
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700286
287 msm_gov_idle_source_init(cpu);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700288 }
289
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700290 cpu_pm_register_notifier(&idle_nb);
291
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700292 return cpufreq_register_governor(&cpufreq_gov_msm);
293}
294
295static int __devexit msm_gov_remove(struct platform_device *pdev)
296{
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700297 int cpu;
298
299 for_each_possible_cpu(cpu) {
300 msm_gov_idle_source_uninit(cpu);
301 }
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700302 platform_set_drvdata(pdev, NULL);
303 return 0;
304}
305
306static struct platform_driver msm_gov_driver = {
307 .probe = msm_gov_probe,
308 .remove = __devexit_p(msm_gov_remove),
309 .driver = {
310 .name = "msm_dcvs_gov",
311 .owner = THIS_MODULE,
312 },
313};
314
315static int __init cpufreq_gov_msm_init(void)
316{
317 return platform_driver_register(&msm_gov_driver);
318}
319late_initcall(cpufreq_gov_msm_init);