blob: b2b3071c324de9b7d61300ff2c8ad4bb88be8bc3 [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
Lucille Sylvester6e362412011-12-09 16:21:42 -070048#ifdef CONFIG_MSM_DCVS
49 &kgsl_pwrscale_policy_msm,
50#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051 NULL
52};
53
54static ssize_t pwrscale_policy_store(struct kgsl_device *device,
55 const char *buf, size_t count)
56{
57 int i;
58 struct kgsl_pwrscale_policy *policy = NULL;
59
60 /* The special keyword none allows the user to detach all
61 policies */
62 if (!strncmp("none", buf, 4)) {
63 kgsl_pwrscale_detach_policy(device);
64 return count;
65 }
66
67 for (i = 0; kgsl_pwrscale_policies[i]; i++) {
68 if (!strncmp(kgsl_pwrscale_policies[i]->name, buf,
69 strnlen(kgsl_pwrscale_policies[i]->name,
70 PAGE_SIZE))) {
71 policy = kgsl_pwrscale_policies[i];
72 break;
73 }
74 }
75
76 if (policy)
77 if (kgsl_pwrscale_attach_policy(device, policy))
78 return -EIO;
79
80 return count;
81}
82
83static ssize_t pwrscale_policy_show(struct kgsl_device *device, char *buf)
84{
85 int ret;
86
Jordan Crousedb817582012-04-11 12:59:05 -060087 if (device->pwrscale.policy) {
88 ret = snprintf(buf, PAGE_SIZE, "%s",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089 device->pwrscale.policy->name);
Jordan Crousedb817582012-04-11 12:59:05 -060090 if (device->pwrscale.enabled == 0)
91 ret += snprintf(buf + ret, PAGE_SIZE - ret,
92 " (disabled)");
93 ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
94 } else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 ret = snprintf(buf, PAGE_SIZE, "none\n");
96
97 return ret;
98}
99
Praveena Pachipulusu263467d2011-12-22 18:07:16 +0530100PWRSCALE_ATTR(policy, 0664, pwrscale_policy_show, pwrscale_policy_store);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101
102static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device,
103 char *buf)
104{
105 int i, ret = 0;
106
107 for (i = 0; kgsl_pwrscale_policies[i]; i++) {
108 ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s ",
109 kgsl_pwrscale_policies[i]->name);
110 }
111
112 ret += snprintf(buf + ret, PAGE_SIZE - ret, "none\n");
113 return ret;
114}
115PWRSCALE_ATTR(avail_policies, 0444, pwrscale_avail_policies_show, NULL);
116
117static struct attribute *pwrscale_attrs[] = {
118 &pwrscale_attr_policy.attr,
119 &pwrscale_attr_avail_policies.attr,
120 NULL
121};
122
123static ssize_t policy_sysfs_show(struct kobject *kobj,
124 struct attribute *attr, char *buf)
125{
126 struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
127 struct kgsl_device *device = pwrscale_to_device(pwrscale);
128 struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
129 ssize_t ret;
130
131 if (pattr->show)
132 ret = pattr->show(device, pwrscale, buf);
133 else
134 ret = -EIO;
135
136 return ret;
137}
138
139static ssize_t policy_sysfs_store(struct kobject *kobj,
140 struct attribute *attr,
141 const char *buf, size_t count)
142{
143 struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
144 struct kgsl_device *device = pwrscale_to_device(pwrscale);
145 struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
146 ssize_t ret;
147
148 if (pattr->store)
149 ret = pattr->store(device, pwrscale, buf, count);
150 else
151 ret = -EIO;
152
153 return ret;
154}
155
156static void policy_sysfs_release(struct kobject *kobj)
157{
158}
159
160static ssize_t pwrscale_sysfs_show(struct kobject *kobj,
161 struct attribute *attr, char *buf)
162{
163 struct kgsl_device *device = to_device(kobj);
164 struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
165 ssize_t ret;
166
167 if (pattr->show)
168 ret = pattr->show(device, buf);
169 else
170 ret = -EIO;
171
172 return ret;
173}
174
175static ssize_t pwrscale_sysfs_store(struct kobject *kobj,
176 struct attribute *attr,
177 const char *buf, size_t count)
178{
179 struct kgsl_device *device = to_device(kobj);
180 struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
181 ssize_t ret;
182
183 if (pattr->store)
184 ret = pattr->store(device, buf, count);
185 else
186 ret = -EIO;
187
188 return ret;
189}
190
191static void pwrscale_sysfs_release(struct kobject *kobj)
192{
193}
194
195static const struct sysfs_ops policy_sysfs_ops = {
196 .show = policy_sysfs_show,
197 .store = policy_sysfs_store
198};
199
200static const struct sysfs_ops pwrscale_sysfs_ops = {
201 .show = pwrscale_sysfs_show,
202 .store = pwrscale_sysfs_store
203};
204
205static struct kobj_type ktype_pwrscale_policy = {
206 .sysfs_ops = &policy_sysfs_ops,
207 .default_attrs = NULL,
208 .release = policy_sysfs_release
209};
210
211static struct kobj_type ktype_pwrscale = {
212 .sysfs_ops = &pwrscale_sysfs_ops,
213 .default_attrs = pwrscale_attrs,
214 .release = pwrscale_sysfs_release
215};
216
Jordan Crousedb817582012-04-11 12:59:05 -0600217#define PWRSCALE_ACTIVE(_d) \
218 ((_d)->pwrscale.policy && (_d)->pwrscale.enabled)
219
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220void kgsl_pwrscale_sleep(struct kgsl_device *device)
221{
Jordan Crousedb817582012-04-11 12:59:05 -0600222 if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->sleep)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 device->pwrscale.policy->sleep(device, &device->pwrscale);
224}
225EXPORT_SYMBOL(kgsl_pwrscale_sleep);
226
227void kgsl_pwrscale_wake(struct kgsl_device *device)
228{
Jordan Crousedb817582012-04-11 12:59:05 -0600229 if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->wake)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230 device->pwrscale.policy->wake(device, &device->pwrscale);
231}
232EXPORT_SYMBOL(kgsl_pwrscale_wake);
233
234void kgsl_pwrscale_busy(struct kgsl_device *device)
235{
Jordan Crousedb817582012-04-11 12:59:05 -0600236 if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->busy)
Lucille Sylvesterb2679c02012-03-13 17:09:27 -0600237 if ((!device->pwrscale.gpu_busy) &&
238 (device->requested_state != KGSL_STATE_SLUMBER))
Lucille Sylvester28e99c82011-08-18 17:23:10 -0600239 device->pwrscale.policy->busy(device,
240 &device->pwrscale);
Lucille Sylvester1e99fcb2011-08-26 16:58:56 -0600241 device->pwrscale.gpu_busy = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242}
243
244void kgsl_pwrscale_idle(struct kgsl_device *device)
245{
Jordan Crousedb817582012-04-11 12:59:05 -0600246 if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->idle)
Lucille Sylvesterb2679c02012-03-13 17:09:27 -0600247 if (device->requested_state !=
248 (KGSL_STATE_SLUMBER | KGSL_STATE_SLEEP))
249 device->pwrscale.policy->idle(device,
250 &device->pwrscale);
Lucille Sylvester1e99fcb2011-08-26 16:58:56 -0600251 device->pwrscale.gpu_busy = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252}
253EXPORT_SYMBOL(kgsl_pwrscale_idle);
254
Jordan Crousedb817582012-04-11 12:59:05 -0600255void kgsl_pwrscale_disable(struct kgsl_device *device)
256{
257 device->pwrscale.enabled = 0;
258}
259EXPORT_SYMBOL(kgsl_pwrscale_disable);
260
261void kgsl_pwrscale_enable(struct kgsl_device *device)
262{
263 device->pwrscale.enabled = 1;
264}
265EXPORT_SYMBOL(kgsl_pwrscale_enable);
266
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267int kgsl_pwrscale_policy_add_files(struct kgsl_device *device,
268 struct kgsl_pwrscale *pwrscale,
269 struct attribute_group *attr_group)
270{
271 int ret;
272
273 ret = kobject_add(&pwrscale->kobj, &device->pwrscale_kobj,
274 "%s", pwrscale->policy->name);
275
276 if (ret)
277 return ret;
278
279 ret = sysfs_create_group(&pwrscale->kobj, attr_group);
280
281 if (ret) {
282 kobject_del(&pwrscale->kobj);
283 kobject_put(&pwrscale->kobj);
284 }
285
286 return ret;
287}
288
289void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device,
290 struct kgsl_pwrscale *pwrscale,
291 struct attribute_group *attr_group)
292{
293 sysfs_remove_group(&pwrscale->kobj, attr_group);
294 kobject_del(&pwrscale->kobj);
295 kobject_put(&pwrscale->kobj);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296}
297
298static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device)
299{
Lucille Sylvesterb0568f42011-10-19 13:40:54 -0600300 if (device->pwrscale.policy != NULL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 device->pwrscale.policy->close(device, &device->pwrscale);
Lucille Sylvesterb0568f42011-10-19 13:40:54 -0600302 kgsl_pwrctrl_pwrlevel_change(device,
303 device->pwrctrl.thermal_pwrlevel);
304 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 device->pwrscale.policy = NULL;
306}
307
308void kgsl_pwrscale_detach_policy(struct kgsl_device *device)
309{
310 mutex_lock(&device->mutex);
311 _kgsl_pwrscale_detach_policy(device);
312 mutex_unlock(&device->mutex);
313}
314EXPORT_SYMBOL(kgsl_pwrscale_detach_policy);
315
316int kgsl_pwrscale_attach_policy(struct kgsl_device *device,
317 struct kgsl_pwrscale_policy *policy)
318{
319 int ret = 0;
320
321 mutex_lock(&device->mutex);
322
323 if (device->pwrscale.policy == policy)
324 goto done;
325
Lynus Vaz3f050222012-01-10 11:49:31 +0530326 if (device->pwrctrl.num_pwrlevels < 3) {
327 ret = -EINVAL;
328 goto done;
329 }
330
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 if (device->pwrscale.policy != NULL)
332 _kgsl_pwrscale_detach_policy(device);
333
334 device->pwrscale.policy = policy;
335
Jordan Crousedb817582012-04-11 12:59:05 -0600336 /* Pwrscale is enabled by default at attach time */
337 kgsl_pwrscale_enable(device);
338
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700339 if (policy) {
340 ret = device->pwrscale.policy->init(device, &device->pwrscale);
341 if (ret)
342 device->pwrscale.policy = NULL;
343 }
344
345done:
346 mutex_unlock(&device->mutex);
347
348 return ret;
349}
350EXPORT_SYMBOL(kgsl_pwrscale_attach_policy);
351
352int kgsl_pwrscale_init(struct kgsl_device *device)
353{
354 int ret;
355
356 ret = kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale,
357 &device->dev->kobj, "pwrscale");
358
359 if (ret)
360 return ret;
361
362 kobject_init(&device->pwrscale.kobj, &ktype_pwrscale_policy);
363 return ret;
364}
365EXPORT_SYMBOL(kgsl_pwrscale_init);
366
367void kgsl_pwrscale_close(struct kgsl_device *device)
368{
369 kobject_put(&device->pwrscale_kobj);
370}
371EXPORT_SYMBOL(kgsl_pwrscale_close);