blob: d46f552c1d3721f3c772228592cce77e31bfc2a0 [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
Steve Mucklef132c6c2012-06-06 18:30:57 -070014#include <linux/export.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/kernel.h>
16
Steve Mucklef132c6c2012-06-06 18:30:57 -070017#include <asm/page.h>
18
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070019#include "kgsl.h"
20#include "kgsl_pwrscale.h"
21#include "kgsl_device.h"
22
23struct kgsl_pwrscale_attribute {
24 struct attribute attr;
25 ssize_t (*show)(struct kgsl_device *device, char *buf);
26 ssize_t (*store)(struct kgsl_device *device, const char *buf,
27 size_t count);
28};
29
30#define to_pwrscale(k) container_of(k, struct kgsl_pwrscale, kobj)
31#define pwrscale_to_device(p) container_of(p, struct kgsl_device, pwrscale)
32#define to_device(k) container_of(k, struct kgsl_device, pwrscale_kobj)
33#define to_pwrscale_attr(a) \
34container_of(a, struct kgsl_pwrscale_attribute, attr)
35#define to_policy_attr(a) \
36container_of(a, struct kgsl_pwrscale_policy_attribute, attr)
37
38#define PWRSCALE_ATTR(_name, _mode, _show, _store) \
39struct kgsl_pwrscale_attribute pwrscale_attr_##_name = \
40__ATTR(_name, _mode, _show, _store)
41
42/* Master list of available policies */
43
44static struct kgsl_pwrscale_policy *kgsl_pwrscale_policies[] = {
45#ifdef CONFIG_MSM_SCM
46 &kgsl_pwrscale_policy_tz,
47#endif
Lynus Vazdde09ee2012-01-05 13:28:22 +053048#ifdef CONFIG_MSM_SLEEP_STATS_DEVICE
Lucille Sylvester591ea032011-07-21 16:08:37 -060049 &kgsl_pwrscale_policy_idlestats,
50#endif
Lucille Sylvester6e362412011-12-09 16:21:42 -070051#ifdef CONFIG_MSM_DCVS
52 &kgsl_pwrscale_policy_msm,
53#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054 NULL
55};
56
57static ssize_t pwrscale_policy_store(struct kgsl_device *device,
58 const char *buf, size_t count)
59{
60 int i;
61 struct kgsl_pwrscale_policy *policy = NULL;
62
63 /* The special keyword none allows the user to detach all
64 policies */
65 if (!strncmp("none", buf, 4)) {
66 kgsl_pwrscale_detach_policy(device);
67 return count;
68 }
69
70 for (i = 0; kgsl_pwrscale_policies[i]; i++) {
71 if (!strncmp(kgsl_pwrscale_policies[i]->name, buf,
72 strnlen(kgsl_pwrscale_policies[i]->name,
73 PAGE_SIZE))) {
74 policy = kgsl_pwrscale_policies[i];
75 break;
76 }
77 }
78
79 if (policy)
80 if (kgsl_pwrscale_attach_policy(device, policy))
81 return -EIO;
82
83 return count;
84}
85
86static ssize_t pwrscale_policy_show(struct kgsl_device *device, char *buf)
87{
88 int ret;
89
Jordan Crousedb817582012-04-11 12:59:05 -060090 if (device->pwrscale.policy) {
91 ret = snprintf(buf, PAGE_SIZE, "%s",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092 device->pwrscale.policy->name);
Jordan Crousedb817582012-04-11 12:59:05 -060093 if (device->pwrscale.enabled == 0)
94 ret += snprintf(buf + ret, PAGE_SIZE - ret,
95 " (disabled)");
96 ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
97 } else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098 ret = snprintf(buf, PAGE_SIZE, "none\n");
99
100 return ret;
101}
102
Praveena Pachipulusu263467d2011-12-22 18:07:16 +0530103PWRSCALE_ATTR(policy, 0664, pwrscale_policy_show, pwrscale_policy_store);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104
105static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device,
106 char *buf)
107{
108 int i, ret = 0;
109
110 for (i = 0; kgsl_pwrscale_policies[i]; i++) {
111 ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s ",
112 kgsl_pwrscale_policies[i]->name);
113 }
114
115 ret += snprintf(buf + ret, PAGE_SIZE - ret, "none\n");
116 return ret;
117}
118PWRSCALE_ATTR(avail_policies, 0444, pwrscale_avail_policies_show, NULL);
119
120static struct attribute *pwrscale_attrs[] = {
121 &pwrscale_attr_policy.attr,
122 &pwrscale_attr_avail_policies.attr,
123 NULL
124};
125
126static ssize_t policy_sysfs_show(struct kobject *kobj,
127 struct attribute *attr, char *buf)
128{
129 struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
130 struct kgsl_device *device = pwrscale_to_device(pwrscale);
131 struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
132 ssize_t ret;
133
134 if (pattr->show)
135 ret = pattr->show(device, pwrscale, buf);
136 else
137 ret = -EIO;
138
139 return ret;
140}
141
142static ssize_t policy_sysfs_store(struct kobject *kobj,
143 struct attribute *attr,
144 const char *buf, size_t count)
145{
146 struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
147 struct kgsl_device *device = pwrscale_to_device(pwrscale);
148 struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
149 ssize_t ret;
150
151 if (pattr->store)
152 ret = pattr->store(device, pwrscale, buf, count);
153 else
154 ret = -EIO;
155
156 return ret;
157}
158
159static void policy_sysfs_release(struct kobject *kobj)
160{
161}
162
163static ssize_t pwrscale_sysfs_show(struct kobject *kobj,
164 struct attribute *attr, char *buf)
165{
166 struct kgsl_device *device = to_device(kobj);
167 struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
168 ssize_t ret;
169
170 if (pattr->show)
171 ret = pattr->show(device, buf);
172 else
173 ret = -EIO;
174
175 return ret;
176}
177
178static ssize_t pwrscale_sysfs_store(struct kobject *kobj,
179 struct attribute *attr,
180 const char *buf, size_t count)
181{
182 struct kgsl_device *device = to_device(kobj);
183 struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
184 ssize_t ret;
185
186 if (pattr->store)
187 ret = pattr->store(device, buf, count);
188 else
189 ret = -EIO;
190
191 return ret;
192}
193
194static void pwrscale_sysfs_release(struct kobject *kobj)
195{
196}
197
198static const struct sysfs_ops policy_sysfs_ops = {
199 .show = policy_sysfs_show,
200 .store = policy_sysfs_store
201};
202
203static const struct sysfs_ops pwrscale_sysfs_ops = {
204 .show = pwrscale_sysfs_show,
205 .store = pwrscale_sysfs_store
206};
207
208static struct kobj_type ktype_pwrscale_policy = {
209 .sysfs_ops = &policy_sysfs_ops,
210 .default_attrs = NULL,
211 .release = policy_sysfs_release
212};
213
214static struct kobj_type ktype_pwrscale = {
215 .sysfs_ops = &pwrscale_sysfs_ops,
216 .default_attrs = pwrscale_attrs,
217 .release = pwrscale_sysfs_release
218};
219
Jordan Crousedb817582012-04-11 12:59:05 -0600220#define PWRSCALE_ACTIVE(_d) \
221 ((_d)->pwrscale.policy && (_d)->pwrscale.enabled)
222
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223void kgsl_pwrscale_sleep(struct kgsl_device *device)
224{
Jordan Crousedb817582012-04-11 12:59:05 -0600225 if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->sleep)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 device->pwrscale.policy->sleep(device, &device->pwrscale);
227}
228EXPORT_SYMBOL(kgsl_pwrscale_sleep);
229
230void kgsl_pwrscale_wake(struct kgsl_device *device)
231{
Jordan Crousedb817582012-04-11 12:59:05 -0600232 if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->wake)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233 device->pwrscale.policy->wake(device, &device->pwrscale);
234}
235EXPORT_SYMBOL(kgsl_pwrscale_wake);
236
237void kgsl_pwrscale_busy(struct kgsl_device *device)
238{
Jordan Crousedb817582012-04-11 12:59:05 -0600239 if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->busy)
Suman Tatiraju3de11cc2012-06-20 10:33:32 -0700240 if (device->requested_state != KGSL_STATE_SLUMBER)
Lucille Sylvester28e99c82011-08-18 17:23:10 -0600241 device->pwrscale.policy->busy(device,
242 &device->pwrscale);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243}
244
Lucille Sylvester721f7e72012-08-21 16:31:26 -0600245void kgsl_pwrscale_idle(struct kgsl_device *device)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246{
Jordan Crousedb817582012-04-11 12:59:05 -0600247 if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->idle)
Carter Cooper7a2621b2012-04-25 17:17:06 -0600248 if (device->requested_state != KGSL_STATE_SLUMBER &&
249 device->requested_state != KGSL_STATE_SLEEP)
Lucille Sylvesterb2679c02012-03-13 17:09:27 -0600250 device->pwrscale.policy->idle(device,
Lucille Sylvester721f7e72012-08-21 16:31:26 -0600251 &device->pwrscale);
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);
Jordan Crouse2ddfc8a2012-11-27 11:33:06 -0700302
303 /*
304 * Try to set max pwrlevel which will be limited to thermal by
305 * kgsl_pwrctrl_pwrlevel_change if thermal is indeed lower
306 */
307
Lucille Sylvesterb0568f42011-10-19 13:40:54 -0600308 kgsl_pwrctrl_pwrlevel_change(device,
Jordan Crouse2ddfc8a2012-11-27 11:33:06 -0700309 device->pwrctrl.max_pwrlevel);
Lucille Sylvesterb0568f42011-10-19 13:40:54 -0600310 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311 device->pwrscale.policy = NULL;
312}
313
314void kgsl_pwrscale_detach_policy(struct kgsl_device *device)
315{
316 mutex_lock(&device->mutex);
317 _kgsl_pwrscale_detach_policy(device);
318 mutex_unlock(&device->mutex);
319}
320EXPORT_SYMBOL(kgsl_pwrscale_detach_policy);
321
322int kgsl_pwrscale_attach_policy(struct kgsl_device *device,
323 struct kgsl_pwrscale_policy *policy)
324{
325 int ret = 0;
326
327 mutex_lock(&device->mutex);
328
329 if (device->pwrscale.policy == policy)
330 goto done;
331
Lynus Vaz3f050222012-01-10 11:49:31 +0530332 if (device->pwrctrl.num_pwrlevels < 3) {
333 ret = -EINVAL;
334 goto done;
335 }
336
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700337 if (device->pwrscale.policy != NULL)
338 _kgsl_pwrscale_detach_policy(device);
339
340 device->pwrscale.policy = policy;
341
Jordan Crousedb817582012-04-11 12:59:05 -0600342 /* Pwrscale is enabled by default at attach time */
343 kgsl_pwrscale_enable(device);
344
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 if (policy) {
346 ret = device->pwrscale.policy->init(device, &device->pwrscale);
347 if (ret)
348 device->pwrscale.policy = NULL;
349 }
350
351done:
352 mutex_unlock(&device->mutex);
353
354 return ret;
355}
356EXPORT_SYMBOL(kgsl_pwrscale_attach_policy);
357
358int kgsl_pwrscale_init(struct kgsl_device *device)
359{
360 int ret;
361
362 ret = kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale,
363 &device->dev->kobj, "pwrscale");
364
365 if (ret)
366 return ret;
367
368 kobject_init(&device->pwrscale.kobj, &ktype_pwrscale_policy);
369 return ret;
370}
371EXPORT_SYMBOL(kgsl_pwrscale_init);
372
373void kgsl_pwrscale_close(struct kgsl_device *device)
374{
375 kobject_put(&device->pwrscale_kobj);
376}
377EXPORT_SYMBOL(kgsl_pwrscale_close);