blob: 0d3120fe02f320fd31c53a5a1fde793081507c3e [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)
224 device->pwrscale.policy->busy(device, &device->pwrscale);
225}
226
227void kgsl_pwrscale_idle(struct kgsl_device *device)
228{
229 if (device->pwrscale.policy && device->pwrscale.policy->idle)
230 device->pwrscale.policy->idle(device, &device->pwrscale);
231}
232EXPORT_SYMBOL(kgsl_pwrscale_idle);
233
234int kgsl_pwrscale_policy_add_files(struct kgsl_device *device,
235 struct kgsl_pwrscale *pwrscale,
236 struct attribute_group *attr_group)
237{
238 int ret;
239
240 ret = kobject_add(&pwrscale->kobj, &device->pwrscale_kobj,
241 "%s", pwrscale->policy->name);
242
243 if (ret)
244 return ret;
245
246 ret = sysfs_create_group(&pwrscale->kobj, attr_group);
247
248 if (ret) {
249 kobject_del(&pwrscale->kobj);
250 kobject_put(&pwrscale->kobj);
251 }
252
253 return ret;
254}
255
256void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device,
257 struct kgsl_pwrscale *pwrscale,
258 struct attribute_group *attr_group)
259{
260 sysfs_remove_group(&pwrscale->kobj, attr_group);
261 kobject_del(&pwrscale->kobj);
262 kobject_put(&pwrscale->kobj);
263}
264
265static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device)
266{
267 if (device->pwrscale.policy != NULL)
268 device->pwrscale.policy->close(device, &device->pwrscale);
269 device->pwrscale.policy = NULL;
270}
271
272void kgsl_pwrscale_detach_policy(struct kgsl_device *device)
273{
274 mutex_lock(&device->mutex);
275 _kgsl_pwrscale_detach_policy(device);
276 mutex_unlock(&device->mutex);
277}
278EXPORT_SYMBOL(kgsl_pwrscale_detach_policy);
279
280int kgsl_pwrscale_attach_policy(struct kgsl_device *device,
281 struct kgsl_pwrscale_policy *policy)
282{
283 int ret = 0;
284
285 mutex_lock(&device->mutex);
286
287 if (device->pwrscale.policy == policy)
288 goto done;
289
290 if (device->pwrscale.policy != NULL)
291 _kgsl_pwrscale_detach_policy(device);
292
293 device->pwrscale.policy = policy;
294
295 if (policy) {
296 ret = device->pwrscale.policy->init(device, &device->pwrscale);
297 if (ret)
298 device->pwrscale.policy = NULL;
299 }
300
301done:
302 mutex_unlock(&device->mutex);
303
304 return ret;
305}
306EXPORT_SYMBOL(kgsl_pwrscale_attach_policy);
307
308int kgsl_pwrscale_init(struct kgsl_device *device)
309{
310 int ret;
311
312 ret = kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale,
313 &device->dev->kobj, "pwrscale");
314
315 if (ret)
316 return ret;
317
318 kobject_init(&device->pwrscale.kobj, &ktype_pwrscale_policy);
319 return ret;
320}
321EXPORT_SYMBOL(kgsl_pwrscale_init);
322
323void kgsl_pwrscale_close(struct kgsl_device *device)
324{
325 kobject_put(&device->pwrscale_kobj);
326}
327EXPORT_SYMBOL(kgsl_pwrscale_close);