blob: 37e5d2d584fa5c2862ca60d39f0a66621d3345e6 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, 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
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
45 NULL
46};
47
48static ssize_t pwrscale_policy_store(struct kgsl_device *device,
49 const char *buf, size_t count)
50{
51 int i;
52 struct kgsl_pwrscale_policy *policy = NULL;
53
54 /* The special keyword none allows the user to detach all
55 policies */
56 if (!strncmp("none", buf, 4)) {
57 kgsl_pwrscale_detach_policy(device);
58 return count;
59 }
60
61 for (i = 0; kgsl_pwrscale_policies[i]; i++) {
62 if (!strncmp(kgsl_pwrscale_policies[i]->name, buf,
63 strnlen(kgsl_pwrscale_policies[i]->name,
64 PAGE_SIZE))) {
65 policy = kgsl_pwrscale_policies[i];
66 break;
67 }
68 }
69
70 if (policy)
71 if (kgsl_pwrscale_attach_policy(device, policy))
72 return -EIO;
73
74 return count;
75}
76
77static ssize_t pwrscale_policy_show(struct kgsl_device *device, char *buf)
78{
79 int ret;
80
81 if (device->pwrscale.policy)
82 ret = snprintf(buf, PAGE_SIZE, "%s\n",
83 device->pwrscale.policy->name);
84 else
85 ret = snprintf(buf, PAGE_SIZE, "none\n");
86
87 return ret;
88}
89
90PWRSCALE_ATTR(policy, 0644, pwrscale_policy_show, pwrscale_policy_store);
91
92static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device,
93 char *buf)
94{
95 int i, ret = 0;
96
97 for (i = 0; kgsl_pwrscale_policies[i]; i++) {
98 ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s ",
99 kgsl_pwrscale_policies[i]->name);
100 }
101
102 ret += snprintf(buf + ret, PAGE_SIZE - ret, "none\n");
103 return ret;
104}
105PWRSCALE_ATTR(avail_policies, 0444, pwrscale_avail_policies_show, NULL);
106
107static struct attribute *pwrscale_attrs[] = {
108 &pwrscale_attr_policy.attr,
109 &pwrscale_attr_avail_policies.attr,
110 NULL
111};
112
113static ssize_t policy_sysfs_show(struct kobject *kobj,
114 struct attribute *attr, char *buf)
115{
116 struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
117 struct kgsl_device *device = pwrscale_to_device(pwrscale);
118 struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
119 ssize_t ret;
120
121 if (pattr->show)
122 ret = pattr->show(device, pwrscale, buf);
123 else
124 ret = -EIO;
125
126 return ret;
127}
128
129static ssize_t policy_sysfs_store(struct kobject *kobj,
130 struct attribute *attr,
131 const char *buf, size_t count)
132{
133 struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj);
134 struct kgsl_device *device = pwrscale_to_device(pwrscale);
135 struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr);
136 ssize_t ret;
137
138 if (pattr->store)
139 ret = pattr->store(device, pwrscale, buf, count);
140 else
141 ret = -EIO;
142
143 return ret;
144}
145
146static void policy_sysfs_release(struct kobject *kobj)
147{
148}
149
150static ssize_t pwrscale_sysfs_show(struct kobject *kobj,
151 struct attribute *attr, char *buf)
152{
153 struct kgsl_device *device = to_device(kobj);
154 struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
155 ssize_t ret;
156
157 if (pattr->show)
158 ret = pattr->show(device, buf);
159 else
160 ret = -EIO;
161
162 return ret;
163}
164
165static ssize_t pwrscale_sysfs_store(struct kobject *kobj,
166 struct attribute *attr,
167 const char *buf, size_t count)
168{
169 struct kgsl_device *device = to_device(kobj);
170 struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr);
171 ssize_t ret;
172
173 if (pattr->store)
174 ret = pattr->store(device, buf, count);
175 else
176 ret = -EIO;
177
178 return ret;
179}
180
181static void pwrscale_sysfs_release(struct kobject *kobj)
182{
183}
184
185static const struct sysfs_ops policy_sysfs_ops = {
186 .show = policy_sysfs_show,
187 .store = policy_sysfs_store
188};
189
190static const struct sysfs_ops pwrscale_sysfs_ops = {
191 .show = pwrscale_sysfs_show,
192 .store = pwrscale_sysfs_store
193};
194
195static struct kobj_type ktype_pwrscale_policy = {
196 .sysfs_ops = &policy_sysfs_ops,
197 .default_attrs = NULL,
198 .release = policy_sysfs_release
199};
200
201static struct kobj_type ktype_pwrscale = {
202 .sysfs_ops = &pwrscale_sysfs_ops,
203 .default_attrs = pwrscale_attrs,
204 .release = pwrscale_sysfs_release
205};
206
207void kgsl_pwrscale_sleep(struct kgsl_device *device)
208{
209 if (device->pwrscale.policy && device->pwrscale.policy->sleep)
210 device->pwrscale.policy->sleep(device, &device->pwrscale);
211}
212EXPORT_SYMBOL(kgsl_pwrscale_sleep);
213
214void kgsl_pwrscale_wake(struct kgsl_device *device)
215{
216 if (device->pwrscale.policy && device->pwrscale.policy->wake)
217 device->pwrscale.policy->wake(device, &device->pwrscale);
218}
219EXPORT_SYMBOL(kgsl_pwrscale_wake);
220
221void kgsl_pwrscale_busy(struct kgsl_device *device)
222{
223 if (device->pwrscale.policy && device->pwrscale.policy->busy)
Lucille Sylvester28e99c82011-08-18 17:23:10 -0600224 if (!device->pwrscale.gpu_busy) {
225 device->pwrscale.policy->busy(device,
226 &device->pwrscale);
227 device->pwrscale.gpu_busy = 1;
228 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229}
230
231void kgsl_pwrscale_idle(struct kgsl_device *device)
232{
233 if (device->pwrscale.policy && device->pwrscale.policy->idle)
Lucille Sylvester28e99c82011-08-18 17:23:10 -0600234 if (device->pwrscale.gpu_busy) {
235 device->pwrscale.policy->idle(device,
236 &device->pwrscale);
237 device->pwrscale.gpu_busy = 0;
238 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239}
240EXPORT_SYMBOL(kgsl_pwrscale_idle);
241
242int kgsl_pwrscale_policy_add_files(struct kgsl_device *device,
243 struct kgsl_pwrscale *pwrscale,
244 struct attribute_group *attr_group)
245{
246 int ret;
247
248 ret = kobject_add(&pwrscale->kobj, &device->pwrscale_kobj,
249 "%s", pwrscale->policy->name);
250
251 if (ret)
252 return ret;
253
254 ret = sysfs_create_group(&pwrscale->kobj, attr_group);
255
256 if (ret) {
257 kobject_del(&pwrscale->kobj);
258 kobject_put(&pwrscale->kobj);
259 }
260
261 return ret;
262}
263
264void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device,
265 struct kgsl_pwrscale *pwrscale,
266 struct attribute_group *attr_group)
267{
268 sysfs_remove_group(&pwrscale->kobj, attr_group);
269 kobject_del(&pwrscale->kobj);
270 kobject_put(&pwrscale->kobj);
271}
272
273static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device)
274{
275 if (device->pwrscale.policy != NULL)
276 device->pwrscale.policy->close(device, &device->pwrscale);
277 device->pwrscale.policy = NULL;
278}
279
280void kgsl_pwrscale_detach_policy(struct kgsl_device *device)
281{
282 mutex_lock(&device->mutex);
283 _kgsl_pwrscale_detach_policy(device);
284 mutex_unlock(&device->mutex);
285}
286EXPORT_SYMBOL(kgsl_pwrscale_detach_policy);
287
288int kgsl_pwrscale_attach_policy(struct kgsl_device *device,
289 struct kgsl_pwrscale_policy *policy)
290{
291 int ret = 0;
292
293 mutex_lock(&device->mutex);
294
295 if (device->pwrscale.policy == policy)
296 goto done;
297
298 if (device->pwrscale.policy != NULL)
299 _kgsl_pwrscale_detach_policy(device);
300
301 device->pwrscale.policy = policy;
302
303 if (policy) {
304 ret = device->pwrscale.policy->init(device, &device->pwrscale);
305 if (ret)
306 device->pwrscale.policy = NULL;
307 }
308
309done:
310 mutex_unlock(&device->mutex);
311
312 return ret;
313}
314EXPORT_SYMBOL(kgsl_pwrscale_attach_policy);
315
316int kgsl_pwrscale_init(struct kgsl_device *device)
317{
318 int ret;
319
320 ret = kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale,
321 &device->dev->kobj, "pwrscale");
322
323 if (ret)
324 return ret;
325
326 kobject_init(&device->pwrscale.kobj, &ktype_pwrscale_policy);
327 return ret;
328}
329EXPORT_SYMBOL(kgsl_pwrscale_init);
330
331void kgsl_pwrscale_close(struct kgsl_device *device)
332{
333 kobject_put(&device->pwrscale_kobj);
334}
335EXPORT_SYMBOL(kgsl_pwrscale_close);