blob: f69cceb07e90f12321da3a24e0022bac635e395e [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>
21#include <mach/msm_dcvs.h>
22
23struct msm_gov {
24 int cpu;
25 unsigned int cur_freq;
26 unsigned int min_freq;
27 unsigned int max_freq;
28 struct msm_dcvs_freq gov_notifier;
29 struct cpufreq_policy *policy;
30};
31
32static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex);
33static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info);
34static char core_name[NR_CPUS][10];
35
36static void msm_gov_check_limits(struct cpufreq_policy *policy)
37{
38 struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -070039 struct msm_dcvs_freq *dcvs_notifier =
40 &(per_cpu(msm_gov_info, policy->cpu).gov_notifier);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -070041
42 if (policy->max < gov->cur_freq)
43 __cpufreq_driver_target(policy, policy->max,
44 CPUFREQ_RELATION_H);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -070045 else if (policy->min > gov->cur_freq)
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -070046 __cpufreq_driver_target(policy, policy->min,
47 CPUFREQ_RELATION_L);
48 else
49 __cpufreq_driver_target(policy, gov->cur_freq,
50 CPUFREQ_RELATION_L);
51
52 gov->cur_freq = policy->cur;
53 gov->min_freq = policy->min;
54 gov->max_freq = policy->max;
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -070055 msm_dcvs_update_limits(dcvs_notifier);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -070056}
57
58static int msm_dcvs_freq_set(struct msm_dcvs_freq *self,
59 unsigned int freq)
60{
61 int ret = -EINVAL;
62 struct msm_gov *gov =
63 container_of(self, struct msm_gov, gov_notifier);
64
65 mutex_lock(&per_cpu(gov_mutex, gov->cpu));
66
67 if (freq < gov->min_freq)
68 freq = gov->min_freq;
69 if (freq > gov->max_freq)
70 freq = gov->max_freq;
71
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -070072 mutex_unlock(&per_cpu(gov_mutex, gov->cpu));
73
Abhijeet Dharmapurikar83347e02012-08-26 20:33:43 -070074 ret = cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
75
76 if (!ret) {
77 gov->cur_freq = cpufreq_quick_get(gov->cpu);
78 if (freq != gov->cur_freq)
79 pr_err("cpu %d freq %u gov->cur_freq %u didn't match",
80 gov->cpu, freq, gov->cur_freq);
81 }
82 ret = gov->cur_freq;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -070083
84 return ret;
85}
86
87static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self)
88{
89 struct msm_gov *gov =
90 container_of(self, struct msm_gov, gov_notifier);
91
Abhijeet Dharmapurikar83347e02012-08-26 20:33:43 -070092 /*
93 * the rw_sem in cpufreq is always held when this is called.
94 * The policy->cur won't be updated in this case - so it is safe to
95 * access policy->cur
96 */
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -070097 return gov->policy->cur;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -070098}
99
100static int cpufreq_governor_msm(struct cpufreq_policy *policy,
101 unsigned int event)
102{
103 unsigned int cpu = policy->cpu;
104 int ret = 0;
105 int handle = 0;
106 struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
107 struct msm_dcvs_freq *dcvs_notifier =
108 &(per_cpu(msm_gov_info, cpu).gov_notifier);
109
110 switch (event) {
111 case CPUFREQ_GOV_START:
112 if (!cpu_online(cpu))
113 return -EINVAL;
114 BUG_ON(!policy->cur);
115 mutex_lock(&per_cpu(gov_mutex, cpu));
116 per_cpu(msm_gov_info, cpu).cpu = cpu;
117 gov->policy = policy;
118 dcvs_notifier->core_name = core_name[cpu];
119 dcvs_notifier->set_frequency = msm_dcvs_freq_set;
120 dcvs_notifier->get_frequency = msm_dcvs_freq_get;
121 handle = msm_dcvs_freq_sink_register(dcvs_notifier);
122 BUG_ON(handle < 0);
123 msm_gov_check_limits(policy);
124 mutex_unlock(&per_cpu(gov_mutex, cpu));
125 break;
126
127 case CPUFREQ_GOV_STOP:
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700128 msm_dcvs_freq_sink_unregister(dcvs_notifier);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700129 break;
130
131 case CPUFREQ_GOV_LIMITS:
132 mutex_lock(&per_cpu(gov_mutex, cpu));
133 msm_gov_check_limits(policy);
134 mutex_unlock(&per_cpu(gov_mutex, cpu));
135 break;
136 };
137
138 return ret;
139}
140
141struct cpufreq_governor cpufreq_gov_msm = {
142 .name = "msm-dcvs",
143 .governor = cpufreq_governor_msm,
144 .owner = THIS_MODULE,
145};
146
147static int __devinit msm_gov_probe(struct platform_device *pdev)
148{
149 int ret = 0;
150 int cpu;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700151 struct msm_dcvs_core_info *core = NULL;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700152 int sensor = 0;
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700153
154 core = pdev->dev.platform_data;
155
156 for_each_possible_cpu(cpu) {
157 mutex_init(&per_cpu(gov_mutex, cpu));
158 snprintf(core_name[cpu], 10, "cpu%d", cpu);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700159 if (cpu < core->num_cores)
160 sensor = core->sensors[cpu];
161 ret = msm_dcvs_register_core(core_name[cpu], core, sensor);
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -0700162 if (ret)
163 pr_err("Unable to register core for %d\n", cpu);
164 }
165
166 return cpufreq_register_governor(&cpufreq_gov_msm);
167}
168
169static int __devexit msm_gov_remove(struct platform_device *pdev)
170{
171 platform_set_drvdata(pdev, NULL);
172 return 0;
173}
174
175static struct platform_driver msm_gov_driver = {
176 .probe = msm_gov_probe,
177 .remove = __devexit_p(msm_gov_remove),
178 .driver = {
179 .name = "msm_dcvs_gov",
180 .owner = THIS_MODULE,
181 },
182};
183
184static int __init cpufreq_gov_msm_init(void)
185{
186 return platform_driver_register(&msm_gov_driver);
187}
188late_initcall(cpufreq_gov_msm_init);