/
kcontrol_gpu_msm.c
347 lines (312 loc) · 9.4 KB
/
kcontrol_gpu_msm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/*
* KControl GPU module for msm devices
*
* Copyright (c) 2013 Dennis Rassmann
* Author: Dennis Rassmann <showp1984@gmail.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kallsyms.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
#include "mach-msm/clock-local.h"
#include "gpu_msm/adreno.h"
#include "gpu_msm/kgsl_device.h"
#define THIS_EXPERIMENTAL 0
#define DRIVER_AUTHOR "Dennis Rassmann <showp1984@gmail.com>"
#define DRIVER_DESCRIPTION "KControl GPU module for msm devices"
#define DRIVER_VERSION "1.1"
#define LOGTAG "kcontrol_gpu_msm: "
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
enum vdd_dig_levels {
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
VDD_DIG_HIGH
};
struct global_attr_kcontrol {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj,
struct attribute *attr, char *buf);
ssize_t (*store)(struct kobject *a, struct attribute *b,
const char *c, size_t count);
};
#define define_one_global_ro_kcontrol(_name) \
static struct global_attr_kcontrol _name = \
__ATTR(_name, 0444, show_##_name, NULL)
#define define_one_global_rw_kcontrol(_name) \
static struct global_attr_kcontrol _name = \
__ATTR(_name, 0644, show_##_name, store_##_name)
/* module parameters */
static uint dev_3d0 = 0x00000000;
module_param(dev_3d0, uint, 0444);
static uint gfx2d0_clk = 0x00000000;
module_param(gfx2d0_clk, uint, 0444);
static uint gfx3d_clk = 0x00000000;
module_param(gfx3d_clk, uint, 0444);
static struct adreno_device *adev = NULL;
static struct kgsl_device *kdev = NULL;
static struct kgsl_pwrctrl *kpwr = NULL;
static struct rcg_clk *rcg2d_clk = NULL;
static struct rcg_clk *rcg3d_clk = NULL;
static struct clk_freq_tbl *clk2dtbl = NULL;
static struct clk_freq_tbl *clk3dtbl = NULL;
static struct clk *clk2d = NULL;
static struct clk *clk3d = NULL;
struct kobject *kcontrol_gpu_msm_kobject;
static ssize_t show_kgsl_pwrlevels(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
int i = 0;
if (kpwr != NULL) {
if (kpwr->num_pwrlevels > 0) {
for (i=0; i<kpwr->num_pwrlevels; i++) {
len += sprintf(buf + len, "%u\n", kpwr->pwrlevels[i].gpu_freq);
}
} else {
for (i=0; ; i++) {
len += sprintf(buf + len, "%u\n", kpwr->pwrlevels[i].gpu_freq);
if ((kpwr->pwrlevels[i].bus_freq == 0))
break;
}
}
} else {
len += sprintf(buf + len, "Error! kpwr pointer is null!\n");
}
return len;
}
static ssize_t store_kgsl_pwrlevels(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
int i = 0;
unsigned int pwrlvl = 0;
long unsigned int hz = 0;
const char *chz = NULL;
bool found = false;
if (kpwr != NULL) {
for (i=0; i<count; i++) {
if (buf[i] == ' ') {
sscanf(&buf[(i-1)], "%u", &pwrlvl);
chz = &buf[(i+1)];
found = true;
}
}
if (found == true) {
sscanf(chz, "%lu", &hz);
kpwr->pwrlevels[pwrlvl].gpu_freq = hz;
} else {
pr_err(LOGTAG"Wrong format! accepting only: <pwrlvl hz>, eg: <0 320000000>\n");
}
} else {
pr_err(LOGTAG"Error! kpwr pointer is null!\n");
}
return count;
}
define_one_global_rw_kcontrol(kgsl_pwrlevels);
static ssize_t show_kgsl_iofraction(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
int i = 0;
if (kpwr != NULL) {
if (kpwr->num_pwrlevels > 0) {
for (i=0; i<kpwr->num_pwrlevels; i++) {
len += sprintf(buf + len, "%u\n", kpwr->pwrlevels[i].io_fraction);
}
} else {
for (i=0; ; i++) {
len += sprintf(buf + len, "%u\n", kpwr->pwrlevels[i].io_fraction);
if ((kpwr->pwrlevels[i].bus_freq == 0))
break;
}
}
} else {
len += sprintf(buf + len, "Error! kpwr pointer is null!\n");
}
return len;
}
static ssize_t store_kgsl_iofraction(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
int i = 0;
unsigned int pwrlvl = 0;
long unsigned int io = 0;
const char *cio = NULL;
bool found = false;
if (kpwr != NULL) {
for (i=0; i<count; i++) {
if (buf[i] == ' ') {
sscanf(&buf[(i-1)], "%u", &pwrlvl);
cio = &buf[(i+1)];
found = true;
}
}
if (found == true) {
sscanf(cio, "%lu", &io);
kpwr->pwrlevels[pwrlvl].io_fraction = io;
} else {
pr_err(LOGTAG"Wrong format! accepting only: <pwrlvl iofrac>, eg: <0 66>\n");
}
} else {
pr_err(LOGTAG"Error! kpwr pointer is null!\n");
}
return count;
}
define_one_global_rw_kcontrol(kgsl_iofraction);
static ssize_t show_version(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
len += sprintf(buf + len, DRIVER_VERSION);
len += sprintf(buf + len, "\n");
return len;
}
define_one_global_ro_kcontrol(version);
static ssize_t show_kgsl_avail_2d_clocks(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
int i = 0;
if (clk2dtbl != NULL) {
for (i=0; clk2dtbl[i].freq_hz != FREQ_END; i++) {
len += sprintf(buf + len, "%u\n", clk2dtbl[i].freq_hz);
}
} else {
len += sprintf(buf + len, "Error! clk2dtbl pointer is null!\n");
}
return len;
}
define_one_global_ro_kcontrol(kgsl_avail_2d_clocks);
static ssize_t show_kgsl_avail_3d_clocks(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
int i = 0;
if (clk3dtbl != NULL) {
for (i=0; clk3dtbl[i].freq_hz != FREQ_END; i++) {
len += sprintf(buf + len, "%u\n", clk3dtbl[i].freq_hz);
}
} else {
len += sprintf(buf + len, "Error! clk3dtbl pointer is null!\n");
}
return len;
}
define_one_global_ro_kcontrol(kgsl_avail_3d_clocks);
static ssize_t show_kgsl_2d_fmax_restraints(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
if (clk2d != NULL) {
len += sprintf(buf + len, "LOW: %lu\n", clk2d->fmax[VDD_DIG_LOW]);
len += sprintf(buf + len, "NOMINAL: %lu\n", clk2d->fmax[VDD_DIG_NOMINAL]);
len += sprintf(buf + len, "HIGH: %lu\n", clk2d->fmax[VDD_DIG_HIGH]);
} else {
len += sprintf(buf + len, "Error! clk2d pointer is null!\n");
}
return len;
}
define_one_global_ro_kcontrol(kgsl_2d_fmax_restraints);
static ssize_t show_kgsl_3d_fmax_restraints(struct kobject *a, struct attribute *b,
char *buf)
{
ssize_t len = 0;
if (clk3d != NULL) {
len += sprintf(buf + len, "LOW: %lu\n", clk3d->fmax[VDD_DIG_LOW]);
len += sprintf(buf + len, "NOMINAL: %lu\n", clk3d->fmax[VDD_DIG_NOMINAL]);
len += sprintf(buf + len, "HIGH: %lu\n", clk3d->fmax[VDD_DIG_HIGH]);
} else {
len += sprintf(buf + len, "Error! clk3d pointer is null!\n");
}
return len;
}
define_one_global_ro_kcontrol(kgsl_3d_fmax_restraints);
static struct attribute *kcontrol_gpu_msm_attributes[] = {
&version.attr,
&kgsl_pwrlevels.attr,
&kgsl_iofraction.attr,
&kgsl_avail_2d_clocks.attr,
&kgsl_avail_3d_clocks.attr,
&kgsl_2d_fmax_restraints.attr,
&kgsl_3d_fmax_restraints.attr,
NULL
};
static struct attribute_group kcontrol_gpu_msm_attr_group = {
.attrs = kcontrol_gpu_msm_attributes,
.name = "kcontrol_gpu_msm",
};
static int __init kcontrol_gpu_msm_init(void)
{
int rc = 0;
printk(KERN_WARNING LOGTAG "#######################################\n");
#if THIS_EXPERIMENTAL
printk(KERN_WARNING LOGTAG "WARNING: THIS MODULE IS EXPERIMENTAL!\n");
printk(KERN_WARNING LOGTAG "You have been warned.\n");
#endif
printk(KERN_INFO LOGTAG "%s, version %s\n", DRIVER_DESCRIPTION, DRIVER_VERSION);
printk(KERN_INFO LOGTAG "author: %s\n", DRIVER_AUTHOR);
printk(KERN_WARNING LOGTAG "#######################################\n");
WARN(dev_3d0 == 0x00000000, LOGTAG "dev_3d0 == 0x00000000!");
//WARN(gfx2d0_clk == 0x00000000, LOGTAG "gfx2d0_clk == 0x00000000!");
//WARN(gfx3d_clk == 0x00000000, LOGTAG "gfx3d_clk == 0x00000000!");
if (dev_3d0 != 0x00000000) {
adev = (struct adreno_device *)dev_3d0;
kdev = &adev->dev;
kpwr = &kdev->pwrctrl;
if (gfx3d_clk != 0x00000000) {
rcg3d_clk = (struct rcg_clk *)gfx3d_clk;
if (rcg3d_clk != NULL) {
clk3dtbl = (struct clk_freq_tbl *)rcg3d_clk->freq_tbl;
clk3d = &rcg3d_clk->c;
}
} else {
pr_warn(LOGTAG"gfx3d_clk: No address given.\n");
}
if (gfx2d0_clk != 0x00000000) {
rcg2d_clk = (struct rcg_clk *)gfx2d0_clk;
if (rcg2d_clk != NULL) {
clk2dtbl = (struct clk_freq_tbl *)rcg2d_clk->freq_tbl;
clk2d = &rcg2d_clk->c;
}
} else {
pr_warn(LOGTAG"gfx2d0_clk: No address given.\n");
}
if (kernel_kobj) {
rc = sysfs_create_group(kernel_kobj, &kcontrol_gpu_msm_attr_group);
if (rc) {
pr_warn(LOGTAG"sysfs: ERROR, could not create sysfs group\n");
}
} else
pr_warn(LOGTAG"sysfs: ERROR, could not find sysfs kobj\n");
pr_info(LOGTAG "everything done, have fun!\n");
} else {
pr_err(LOGTAG "Error, you need to insert this module WITH parameters!\n");
pr_err(LOGTAG "Nothing modified, removing myself!\n");
return -EAGAIN;
}
return 0;
}
static void __exit kcontrol_gpu_msm_exit(void)
{
sysfs_remove_group(kernel_kobj, &kcontrol_gpu_msm_attr_group);
printk(KERN_INFO LOGTAG "unloaded\n");
}
module_init(kcontrol_gpu_msm_init);
module_exit(kcontrol_gpu_msm_exit);