blob: 9c49f803aa631c19f4290b0f806ed20d4edaac11 [file] [log] [blame]
Praveen Chidambaram5c8adf22012-02-23 18:44:37 -07001/* Copyright (c) 2012, 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
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);
39
40 if (policy->max < gov->cur_freq)
41 __cpufreq_driver_target(policy, policy->max,
42 CPUFREQ_RELATION_H);
43 else if (policy->min > gov->min_freq)
44 __cpufreq_driver_target(policy, policy->min,
45 CPUFREQ_RELATION_L);
46 else
47 __cpufreq_driver_target(policy, gov->cur_freq,
48 CPUFREQ_RELATION_L);
49
50 gov->cur_freq = policy->cur;
51 gov->min_freq = policy->min;
52 gov->max_freq = policy->max;
53}
54
55static int msm_dcvs_freq_set(struct msm_dcvs_freq *self,
56 unsigned int freq)
57{
58 int ret = -EINVAL;
59 struct msm_gov *gov =
60 container_of(self, struct msm_gov, gov_notifier);
61
62 mutex_lock(&per_cpu(gov_mutex, gov->cpu));
63
64 if (freq < gov->min_freq)
65 freq = gov->min_freq;
66 if (freq > gov->max_freq)
67 freq = gov->max_freq;
68
69 ret = __cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
70 gov->cur_freq = gov->policy->cur;
71
72 mutex_unlock(&per_cpu(gov_mutex, gov->cpu));
73
74 if (!ret)
75 return gov->cur_freq;
76
77 return ret;
78}
79
80static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self)
81{
82 struct msm_gov *gov =
83 container_of(self, struct msm_gov, gov_notifier);
84
85 return gov->cur_freq;
86}
87
88static int cpufreq_governor_msm(struct cpufreq_policy *policy,
89 unsigned int event)
90{
91 unsigned int cpu = policy->cpu;
92 int ret = 0;
93 int handle = 0;
94 struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
95 struct msm_dcvs_freq *dcvs_notifier =
96 &(per_cpu(msm_gov_info, cpu).gov_notifier);
97
98 switch (event) {
99 case CPUFREQ_GOV_START:
100 if (!cpu_online(cpu))
101 return -EINVAL;
102 BUG_ON(!policy->cur);
103 mutex_lock(&per_cpu(gov_mutex, cpu));
104 per_cpu(msm_gov_info, cpu).cpu = cpu;
105 gov->policy = policy;
106 dcvs_notifier->core_name = core_name[cpu];
107 dcvs_notifier->set_frequency = msm_dcvs_freq_set;
108 dcvs_notifier->get_frequency = msm_dcvs_freq_get;
109 handle = msm_dcvs_freq_sink_register(dcvs_notifier);
110 BUG_ON(handle < 0);
111 msm_gov_check_limits(policy);
112 mutex_unlock(&per_cpu(gov_mutex, cpu));
113 break;
114
115 case CPUFREQ_GOV_STOP:
116 mutex_lock(&per_cpu(gov_mutex, cpu));
117 msm_dcvs_freq_sink_unregister(dcvs_notifier);
118 mutex_unlock(&per_cpu(gov_mutex, cpu));
119 break;
120
121 case CPUFREQ_GOV_LIMITS:
122 mutex_lock(&per_cpu(gov_mutex, cpu));
123 msm_gov_check_limits(policy);
124 mutex_unlock(&per_cpu(gov_mutex, cpu));
125 break;
126 };
127
128 return ret;
129}
130
131struct cpufreq_governor cpufreq_gov_msm = {
132 .name = "msm-dcvs",
133 .governor = cpufreq_governor_msm,
134 .owner = THIS_MODULE,
135};
136
137static int __devinit msm_gov_probe(struct platform_device *pdev)
138{
139 int ret = 0;
140 int cpu;
141 uint32_t group_id = 0x43505530; /* CPU0 */
142 struct msm_dcvs_core_info *core = NULL;
143
144 core = pdev->dev.platform_data;
145
146 for_each_possible_cpu(cpu) {
147 mutex_init(&per_cpu(gov_mutex, cpu));
148 snprintf(core_name[cpu], 10, "cpu%d", cpu);
149 ret = msm_dcvs_register_core(core_name[cpu], group_id, core);
150 if (ret)
151 pr_err("Unable to register core for %d\n", cpu);
152 }
153
154 return cpufreq_register_governor(&cpufreq_gov_msm);
155}
156
157static int __devexit msm_gov_remove(struct platform_device *pdev)
158{
159 platform_set_drvdata(pdev, NULL);
160 return 0;
161}
162
163static struct platform_driver msm_gov_driver = {
164 .probe = msm_gov_probe,
165 .remove = __devexit_p(msm_gov_remove),
166 .driver = {
167 .name = "msm_dcvs_gov",
168 .owner = THIS_MODULE,
169 },
170};
171
172static int __init cpufreq_gov_msm_init(void)
173{
174 return platform_driver_register(&msm_gov_driver);
175}
176late_initcall(cpufreq_gov_msm_init);