blob: 4c9a23999bd1506300f9263e78bd648ad9dc7488 [file] [log] [blame]
Lynus Vazdde09ee2012-01-05 13:28:22 +05301/* Copyright (c) 2010-2012, Code Aurora Forum. 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
14#include <linux/kernel.h>
15
16#include "kgsl.h"
17#include "kgsl_pwrscale.h"
18#include "kgsl_device.h"
19
20struct kgsl_pwrscale_attribute {
21 struct attribute attr;
22 ssize_t (*show)(struct kgsl_device *device, char *buf);
23 ssize_t (*store)(struct kgsl_device *device, const char *buf,
24 size_t count);
25};
26
27#define to_pwrscale(k) container_of(k, struct kgsl_pwrscale, kobj)
28#define pwrscale_to_device(p) container_of(p, struct kgsl_device, pwrscale)
29#define to_device(k) container_of(k, struct kgsl_device, pwrscale_kobj)
30#define to_pwrscale_attr(a) \
31container_of(a, struct kgsl_pwrscale_attribute, attr)
32#define to_policy_attr(a) \
33container_of(a, struct kgsl_pwrscale_policy_attribute, attr)
34
35#define PWRSCALE_ATTR(_name, _mode, _show, _store) \
36struct kgsl_pwrscale_attribute pwrscale_attr_##_name = \
37__ATTR(_name, _mode, _show, _store)
38
39/* Master list of available policies */
40
41static struct kgsl_pwrscale_policy *kgsl_pwrscale_policies[] = {
42#ifdef CONFIG_MSM_SCM
43 &kgsl_pwrscale_policy_tz,
44#endif
Lynus Vazdde09ee2012-01-05 13:28:22 +053045#ifdef CONFIG_MSM_SLEEP_STATS_DEVICE
Lucille Sylvester591ea032011-07-21 16:08:37 -060046 &kgsl_pwrscale_policy_idlestats,
47#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048 NULL
49};
50
51static ssize_t pwrscale_policy_store(struct kgsl_device *device,
52 const char *buf, size_t count)
53{
54 int i;
55 struct kgsl_pwrscale_policy *policy = NULL;
56
57 /* The special keyword none allows the user to detach all
58 policies */
59 if (!strncmp("none", buf, 4)) {
60 kgsl_pwrscale_detach_policy(device);
61 return count;
62 }
63
64 for (i = 0; kgsl_pwrscale_policies[i]; i++) {
65 if (!strncmp(kgsl_pwrscale_policies[i]->name, buf,
66 strnlen(kgsl_pwrscale_policies[i]->name,
67 PAGE_SIZE))) {
68 policy = kgsl_pwrscale_policies[i];
69 break;
70 }
71 }
72
73 if (policy)
74 if (kgsl_pwrscale_attach_policy(device, policy))
75 return -EIO;
76
77 return count;
78}
79
80static ssize_t pwrscale_policy_show(struct kgsl_device *device, char *buf)
81{
82 int ret;
83
84 if (device->pwrscale.policy)
85 ret = snprintf(buf, PAGE_SIZE, "%s\n",
86 device->pwrscale.policy->name);
87 else
88 ret = snprintf(buf, PAGE_SIZE, "none\n");
89
90 return ret;
91}
92
Lucille Sylvester67138c92011-12-07 17:26:29 -070093PWRSCALE_ATTR(policy, 0666, pwrscale_policy_show, pwrscale_policy_store);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094
95static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device,
96 char *buf)
97{
98 int i, ret = 0;
99
100 for (i = 0; kgsl_pwrscale_policies[i]; i++) {
101 ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s ",
102 kgsl_pwrscale_policies[i]->name);
103 }
104
105 ret += snprintf(buf + ret, PAGE_SIZE - ret, "none\n");
106 return ret;
107}
108PWRSCALE_ATTR(avail_policies, 0444, pwrscale_avail_policies_show, NULL);
109
110static struct attribute *pwrscale_attrs[] = {
111 &pwrscale_attr_policy.attr,
112 &pwrscale_attr_avail_policies.attr,
113 NULL
114};
115
116static ssize_t policy_sysfs_show(struct kobject *kobj,
117 struct attribute *attr, char *buf)
118{
119 struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
120 struct kgsl_device *device = pwrscale_to_device(pwrscale);
121 struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
122 ssize_t ret;
123
124 if (pattr->show)
125 ret = pattr->show(device, pwrscale, buf);
126 else
127 ret = -EIO;
128
129 return ret;
130}
131
132static ssize_t policy_sysfs_store(struct kobject *kobj,
133 struct attribute *attr,
134 const char *buf, size_t count)
135{
136 struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
137 struct kgsl_device *device = pwrscale_to_device(pwrscale);
138 struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
139 ssize_t ret;
140
141 if (pattr->store)
142 ret = pattr->store(device, pwrscale, buf, count);
143 else
144 ret = -EIO;
145
146 return ret;
147}
148
149static void policy_sysfs_release(struct kobject *kobj)
150{
151}
152
153static ssize_t pwrscale_sysfs_show(struct kobject *kobj,
154 struct attribute *attr, char *buf)
155{
156 struct kgsl_device *device = to_device(kobj);
157 struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
158 ssize_t ret;
159
160 if (pattr->show)
161 ret = pattr->show(device, buf);
162 else
163 ret = -EIO;
164
165 return ret;
166}
167
168static ssize_t pwrscale_sysfs_store(struct kobject *kobj,
169 struct attribute *attr,
170 const char *buf, size_t count)
171{
172 struct kgsl_device *device = to_device(kobj);
173 struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
174 ssize_t ret;
175
176 if (pattr->store)
177 ret = pattr->store(device, buf, count);
178 else
179 ret = -EIO;
180
181 return ret;
182}
183
184static void pwrscale_sysfs_release(struct kobject *kobj)
185{
186}
187
188static const struct sysfs_ops policy_sysfs_ops = {
189 .show = policy_sysfs_show,
190 .store = policy_sysfs_store
191};
192
193static const struct sysfs_ops pwrscale_sysfs_ops = {
194 .show = pwrscale_sysfs_show,
195 .store = pwrscale_sysfs_store
196};
197
198static struct kobj_type ktype_pwrscale_policy = {
199 .sysfs_ops = &policy_sysfs_ops,
200 .default_attrs = NULL,
201 .release = policy_sysfs_release
202};
203
204static struct kobj_type ktype_pwrscale = {
205 .sysfs_ops = &pwrscale_sysfs_ops,
206 .default_attrs = pwrscale_attrs,
207 .release = pwrscale_sysfs_release
208};
209
210void kgsl_pwrscale_sleep(struct kgsl_device *device)
211{
212 if (device->pwrscale.policy && device->pwrscale.policy->sleep)
213 device->pwrscale.policy->sleep(device, &device->pwrscale);
214}
215EXPORT_SYMBOL(kgsl_pwrscale_sleep);
216
217void kgsl_pwrscale_wake(struct kgsl_device *device)
218{
219 if (device->pwrscale.policy && device->pwrscale.policy->wake)
220 device->pwrscale.policy->wake(device, &device->pwrscale);
221}
222EXPORT_SYMBOL(kgsl_pwrscale_wake);
223
224void kgsl_pwrscale_busy(struct kgsl_device *device)
225{
226 if (device->pwrscale.policy && device->pwrscale.policy->busy)
Lucille Sylvester1e99fcb2011-08-26 16:58:56 -0600227 if (!device->pwrscale.gpu_busy)
Lucille Sylvester28e99c82011-08-18 17:23:10 -0600228 device->pwrscale.policy->busy(device,
229 &device->pwrscale);
Lucille Sylvester1e99fcb2011-08-26 16:58:56 -0600230 device->pwrscale.gpu_busy = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700231}
232
233void kgsl_pwrscale_idle(struct kgsl_device *device)
234{
235 if (device->pwrscale.policy && device->pwrscale.policy->idle)
Lucille Sylvester1e99fcb2011-08-26 16:58:56 -0600236 device->pwrscale.policy->idle(device, &device->pwrscale);
237 device->pwrscale.gpu_busy = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700238}
239EXPORT_SYMBOL(kgsl_pwrscale_idle);
240
241int kgsl_pwrscale_policy_add_files(struct kgsl_device *device,
242 struct kgsl_pwrscale *pwrscale,
243 struct attribute_group *attr_group)
244{
245 int ret;
246
247 ret = kobject_add(&pwrscale->kobj, &device->pwrscale_kobj,
248 "%s", pwrscale->policy->name);
249
250 if (ret)
251 return ret;
252
253 ret = sysfs_create_group(&pwrscale->kobj, attr_group);
254
255 if (ret) {
256 kobject_del(&pwrscale->kobj);
257 kobject_put(&pwrscale->kobj);
258 }
259
260 return ret;
261}
262
263void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device,
264 struct kgsl_pwrscale *pwrscale,
265 struct attribute_group *attr_group)
266{
267 sysfs_remove_group(&pwrscale->kobj, attr_group);
268 kobject_del(&pwrscale->kobj);
269 kobject_put(&pwrscale->kobj);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270}
271
272static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device)
273{
Lucille Sylvesterb0568f42011-10-19 13:40:54 -0600274 if (device->pwrscale.policy != NULL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 device->pwrscale.policy->close(device, &device->pwrscale);
Lucille Sylvesterb0568f42011-10-19 13:40:54 -0600276 kgsl_pwrctrl_pwrlevel_change(device,
277 device->pwrctrl.thermal_pwrlevel);
278 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 device->pwrscale.policy = NULL;
280}
281
282void kgsl_pwrscale_detach_policy(struct kgsl_device *device)
283{
284 mutex_lock(&device->mutex);
285 _kgsl_pwrscale_detach_policy(device);
286 mutex_unlock(&device->mutex);
287}
288EXPORT_SYMBOL(kgsl_pwrscale_detach_policy);
289
290int kgsl_pwrscale_attach_policy(struct kgsl_device *device,
291 struct kgsl_pwrscale_policy *policy)
292{
293 int ret = 0;
294
295 mutex_lock(&device->mutex);
296
297 if (device->pwrscale.policy == policy)
298 goto done;
299
Lynus Vaz3f050222012-01-10 11:49:31 +0530300 if (device->pwrctrl.num_pwrlevels < 3) {
301 ret = -EINVAL;
302 goto done;
303 }
304
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 if (device->pwrscale.policy != NULL)
306 _kgsl_pwrscale_detach_policy(device);
307
308 device->pwrscale.policy = policy;
309
310 if (policy) {
311 ret = device->pwrscale.policy->init(device, &device->pwrscale);
312 if (ret)
313 device->pwrscale.policy = NULL;
314 }
315
316done:
317 mutex_unlock(&device->mutex);
318
319 return ret;
320}
321EXPORT_SYMBOL(kgsl_pwrscale_attach_policy);
322
323int kgsl_pwrscale_init(struct kgsl_device *device)
324{
325 int ret;
326
327 ret = kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale,
328 &device->dev->kobj, "pwrscale");
329
330 if (ret)
331 return ret;
332
333 kobject_init(&device->pwrscale.kobj, &ktype_pwrscale_policy);
334 return ret;
335}
336EXPORT_SYMBOL(kgsl_pwrscale_init);
337
338void kgsl_pwrscale_close(struct kgsl_device *device)
339{
340 kobject_put(&device->pwrscale_kobj);
341}
342EXPORT_SYMBOL(kgsl_pwrscale_close);