Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /** |
2 | * This confidential and proprietary software may be used only as | |
3 | * authorised by a licensing agreement from ARM Limited | |
db9a41fa | 4 | * (C) COPYRIGHT 2011-2015 ARM Limited |
6fa3eb70 S |
5 | * ALL RIGHTS RESERVED |
6 | * The entire notice above must be reproduced on all authorised | |
7 | * copies and copies may only be made to the extent permitted | |
8 | * by a licensing agreement from ARM Limited. | |
9 | */ | |
10 | ||
11 | ||
12 | /** | |
13 | * @file mali_kernel_sysfs.c | |
14 | * Implementation of some sysfs data exports | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/fs.h> | |
19 | #include <linux/device.h> | |
20 | #include <linux/module.h> | |
21 | #include "mali_kernel_license.h" | |
22 | #include "mali_kernel_common.h" | |
23 | #include "mali_ukk.h" | |
24 | ||
25 | #if MALI_LICENSE_IS_GPL | |
26 | ||
27 | #include <linux/seq_file.h> | |
28 | #include <linux/debugfs.h> | |
29 | #include <asm/uaccess.h> | |
30 | #include <linux/module.h> | |
31 | #include <linux/mali/mali_utgard.h> | |
6fa3eb70 S |
32 | #include "mali_kernel_sysfs.h" |
33 | #if defined(CONFIG_MALI400_INTERNAL_PROFILING) | |
34 | #include <linux/slab.h> | |
35 | #include "mali_osk_profiling.h" | |
36 | #endif | |
37 | ||
38 | #include <linux/mali/mali_utgard.h> | |
39 | #include "mali_pm.h" | |
40 | #include "mali_pmu.h" | |
41 | #include "mali_group.h" | |
42 | #include "mali_gp.h" | |
43 | #include "mali_pp.h" | |
44 | #include "mali_l2_cache.h" | |
45 | #include "mali_hw_core.h" | |
46 | #include "mali_kernel_core.h" | |
47 | #include "mali_user_settings_db.h" | |
48 | #include "mali_profiling_internal.h" | |
49 | #include "mali_gp_job.h" | |
50 | #include "mali_pp_job.h" | |
db9a41fa | 51 | #include "mali_executor.h" |
6fa3eb70 S |
52 | |
53 | #define PRIVATE_DATA_COUNTER_MAKE_GP(src) (src) | |
54 | #define PRIVATE_DATA_COUNTER_MAKE_PP(src) ((1 << 24) | src) | |
55 | #define PRIVATE_DATA_COUNTER_MAKE_PP_SUB_JOB(src, sub_job) ((1 << 24) | (1 << 16) | (sub_job << 8) | src) | |
56 | #define PRIVATE_DATA_COUNTER_IS_PP(a) ((((a) >> 24) & 0xFF) ? MALI_TRUE : MALI_FALSE) | |
57 | #define PRIVATE_DATA_COUNTER_GET_SRC(a) (a & 0xFF) | |
58 | #define PRIVATE_DATA_COUNTER_IS_SUB_JOB(a) ((((a) >> 16) & 0xFF) ? MALI_TRUE : MALI_FALSE) | |
59 | #define PRIVATE_DATA_COUNTER_GET_SUB_JOB(a) (((a) >> 8) & 0xFF) | |
60 | ||
61 | #define POWER_BUFFER_SIZE 3 | |
62 | ||
63 | static struct dentry *mali_debugfs_dir = NULL; | |
64 | ||
65 | typedef enum { | |
66 | _MALI_DEVICE_SUSPEND, | |
67 | _MALI_DEVICE_RESUME, | |
68 | _MALI_DEVICE_DVFS_PAUSE, | |
69 | _MALI_DEVICE_DVFS_RESUME, | |
70 | _MALI_MAX_EVENTS | |
71 | } _mali_device_debug_power_events; | |
72 | ||
db9a41fa | 73 | static const char *const mali_power_events[_MALI_MAX_EVENTS] = { |
6fa3eb70 S |
74 | [_MALI_DEVICE_SUSPEND] = "suspend", |
75 | [_MALI_DEVICE_RESUME] = "resume", | |
76 | [_MALI_DEVICE_DVFS_PAUSE] = "dvfs_pause", | |
77 | [_MALI_DEVICE_DVFS_RESUME] = "dvfs_resume", | |
78 | }; | |
79 | ||
80 | static mali_bool power_always_on_enabled = MALI_FALSE; | |
81 | ||
6fa3eb70 S |
82 | static int open_copy_private_data(struct inode *inode, struct file *filp) |
83 | { | |
84 | filp->private_data = inode->i_private; | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static ssize_t group_enabled_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) | |
89 | { | |
90 | int r; | |
91 | char buffer[64]; | |
92 | struct mali_group *group; | |
93 | ||
94 | group = (struct mali_group *)filp->private_data; | |
95 | MALI_DEBUG_ASSERT_POINTER(group); | |
96 | ||
db9a41fa S |
97 | r = snprintf(buffer, 64, "%u\n", |
98 | mali_executor_group_is_disabled(group) ? 0 : 1); | |
6fa3eb70 S |
99 | |
100 | return simple_read_from_buffer(buf, count, offp, buffer, r); | |
101 | } | |
102 | ||
103 | static ssize_t group_enabled_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) | |
104 | { | |
105 | int r; | |
106 | char buffer[64]; | |
107 | unsigned long val; | |
108 | struct mali_group *group; | |
109 | ||
110 | group = (struct mali_group *)filp->private_data; | |
111 | MALI_DEBUG_ASSERT_POINTER(group); | |
112 | ||
113 | if (count >= sizeof(buffer)) { | |
114 | return -ENOMEM; | |
115 | } | |
116 | ||
117 | if (copy_from_user(&buffer[0], buf, count)) { | |
118 | return -EFAULT; | |
119 | } | |
120 | buffer[count] = '\0'; | |
121 | ||
db9a41fa | 122 | r = kstrtoul(&buffer[0], 10, &val); |
6fa3eb70 S |
123 | if (0 != r) { |
124 | return -EINVAL; | |
125 | } | |
126 | ||
127 | switch (val) { | |
128 | case 1: | |
db9a41fa | 129 | mali_executor_group_enable(group); |
6fa3eb70 S |
130 | break; |
131 | case 0: | |
db9a41fa | 132 | mali_executor_group_disable(group); |
6fa3eb70 S |
133 | break; |
134 | default: | |
135 | return -EINVAL; | |
136 | break; | |
137 | } | |
138 | ||
139 | *offp += count; | |
140 | return count; | |
141 | } | |
142 | ||
143 | static const struct file_operations group_enabled_fops = { | |
144 | .owner = THIS_MODULE, | |
145 | .open = open_copy_private_data, | |
146 | .read = group_enabled_read, | |
147 | .write = group_enabled_write, | |
148 | }; | |
149 | ||
150 | static ssize_t hw_core_base_addr_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) | |
151 | { | |
152 | int r; | |
153 | char buffer[64]; | |
154 | struct mali_hw_core *hw_core; | |
155 | ||
156 | hw_core = (struct mali_hw_core *)filp->private_data; | |
157 | MALI_DEBUG_ASSERT_POINTER(hw_core); | |
158 | ||
db9a41fa | 159 | r = snprintf(buffer, 64, "0x%lX\n", hw_core->phys_addr); |
6fa3eb70 S |
160 | |
161 | return simple_read_from_buffer(buf, count, offp, buffer, r); | |
162 | } | |
163 | ||
164 | static const struct file_operations hw_core_base_addr_fops = { | |
165 | .owner = THIS_MODULE, | |
166 | .open = open_copy_private_data, | |
167 | .read = hw_core_base_addr_read, | |
168 | }; | |
169 | ||
170 | static ssize_t profiling_counter_src_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
171 | { | |
db9a41fa S |
172 | u32 is_pp = PRIVATE_DATA_COUNTER_IS_PP((uintptr_t)filp->private_data); |
173 | u32 src_id = PRIVATE_DATA_COUNTER_GET_SRC((uintptr_t)filp->private_data); | |
174 | mali_bool is_sub_job = PRIVATE_DATA_COUNTER_IS_SUB_JOB((uintptr_t)filp->private_data); | |
175 | u32 sub_job = PRIVATE_DATA_COUNTER_GET_SUB_JOB((uintptr_t)filp->private_data); | |
6fa3eb70 S |
176 | char buf[64]; |
177 | int r; | |
178 | u32 val; | |
179 | ||
180 | if (MALI_TRUE == is_pp) { | |
181 | /* PP counter */ | |
182 | if (MALI_TRUE == is_sub_job) { | |
183 | /* Get counter for a particular sub job */ | |
184 | if (0 == src_id) { | |
185 | val = mali_pp_job_get_pp_counter_sub_job_src0(sub_job); | |
186 | } else { | |
187 | val = mali_pp_job_get_pp_counter_sub_job_src1(sub_job); | |
188 | } | |
189 | } else { | |
190 | /* Get default counter for all PP sub jobs */ | |
191 | if (0 == src_id) { | |
192 | val = mali_pp_job_get_pp_counter_global_src0(); | |
193 | } else { | |
194 | val = mali_pp_job_get_pp_counter_global_src1(); | |
195 | } | |
196 | } | |
197 | } else { | |
198 | /* GP counter */ | |
199 | if (0 == src_id) { | |
200 | val = mali_gp_job_get_gp_counter_src0(); | |
201 | } else { | |
202 | val = mali_gp_job_get_gp_counter_src1(); | |
203 | } | |
204 | } | |
205 | ||
206 | if (MALI_HW_CORE_NO_COUNTER == val) { | |
db9a41fa | 207 | r = snprintf(buf, 64, "-1\n"); |
6fa3eb70 | 208 | } else { |
db9a41fa | 209 | r = snprintf(buf, 64, "%u\n", val); |
6fa3eb70 S |
210 | } |
211 | ||
212 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | |
213 | } | |
214 | ||
215 | static ssize_t profiling_counter_src_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) | |
216 | { | |
db9a41fa S |
217 | u32 is_pp = PRIVATE_DATA_COUNTER_IS_PP((uintptr_t)filp->private_data); |
218 | u32 src_id = PRIVATE_DATA_COUNTER_GET_SRC((uintptr_t)filp->private_data); | |
219 | mali_bool is_sub_job = PRIVATE_DATA_COUNTER_IS_SUB_JOB((uintptr_t)filp->private_data); | |
220 | u32 sub_job = PRIVATE_DATA_COUNTER_GET_SUB_JOB((uintptr_t)filp->private_data); | |
6fa3eb70 S |
221 | char buf[64]; |
222 | long val; | |
223 | int ret; | |
224 | ||
225 | if (cnt >= sizeof(buf)) { | |
226 | return -EINVAL; | |
227 | } | |
228 | ||
229 | if (copy_from_user(&buf, ubuf, cnt)) { | |
230 | return -EFAULT; | |
231 | } | |
232 | ||
233 | buf[cnt] = 0; | |
234 | ||
db9a41fa | 235 | ret = kstrtol(buf, 10, &val); |
6fa3eb70 S |
236 | if (ret < 0) { |
237 | return ret; | |
238 | } | |
239 | ||
240 | if (val < 0) { | |
241 | /* any negative input will disable counter */ | |
242 | val = MALI_HW_CORE_NO_COUNTER; | |
243 | } | |
244 | ||
245 | if (MALI_TRUE == is_pp) { | |
246 | /* PP counter */ | |
247 | if (MALI_TRUE == is_sub_job) { | |
248 | /* Set counter for a particular sub job */ | |
249 | if (0 == src_id) { | |
250 | mali_pp_job_set_pp_counter_sub_job_src0(sub_job, (u32)val); | |
251 | } else { | |
252 | mali_pp_job_set_pp_counter_sub_job_src1(sub_job, (u32)val); | |
253 | } | |
254 | } else { | |
255 | /* Set default counter for all PP sub jobs */ | |
256 | if (0 == src_id) { | |
257 | mali_pp_job_set_pp_counter_global_src0((u32)val); | |
258 | } else { | |
259 | mali_pp_job_set_pp_counter_global_src1((u32)val); | |
260 | } | |
261 | } | |
262 | } else { | |
263 | /* GP counter */ | |
264 | if (0 == src_id) { | |
265 | mali_gp_job_set_gp_counter_src0((u32)val); | |
266 | } else { | |
267 | mali_gp_job_set_gp_counter_src1((u32)val); | |
268 | } | |
269 | } | |
270 | ||
271 | *ppos += cnt; | |
272 | return cnt; | |
273 | } | |
274 | ||
275 | static const struct file_operations profiling_counter_src_fops = { | |
276 | .owner = THIS_MODULE, | |
277 | .open = open_copy_private_data, | |
278 | .read = profiling_counter_src_read, | |
279 | .write = profiling_counter_src_write, | |
280 | }; | |
281 | ||
282 | static ssize_t l2_l2x_counter_srcx_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos, u32 src_id) | |
283 | { | |
284 | char buf[64]; | |
285 | int r; | |
286 | u32 val; | |
287 | struct mali_l2_cache_core *l2_core = (struct mali_l2_cache_core *)filp->private_data; | |
288 | ||
289 | if (0 == src_id) { | |
290 | val = mali_l2_cache_core_get_counter_src0(l2_core); | |
291 | } else { | |
292 | val = mali_l2_cache_core_get_counter_src1(l2_core); | |
293 | } | |
294 | ||
295 | if (MALI_HW_CORE_NO_COUNTER == val) { | |
db9a41fa | 296 | r = snprintf(buf, 64, "-1\n"); |
6fa3eb70 | 297 | } else { |
db9a41fa | 298 | r = snprintf(buf, 64, "%u\n", val); |
6fa3eb70 S |
299 | } |
300 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | |
301 | } | |
302 | ||
303 | static ssize_t l2_l2x_counter_srcx_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos, u32 src_id) | |
304 | { | |
305 | struct mali_l2_cache_core *l2_core = (struct mali_l2_cache_core *)filp->private_data; | |
306 | char buf[64]; | |
307 | long val; | |
308 | int ret; | |
309 | ||
310 | if (cnt >= sizeof(buf)) { | |
311 | return -EINVAL; | |
312 | } | |
313 | ||
314 | if (copy_from_user(&buf, ubuf, cnt)) { | |
315 | return -EFAULT; | |
316 | } | |
317 | ||
318 | buf[cnt] = 0; | |
319 | ||
db9a41fa | 320 | ret = kstrtol(buf, 10, &val); |
6fa3eb70 S |
321 | if (ret < 0) { |
322 | return ret; | |
323 | } | |
324 | ||
325 | if (val < 0) { | |
326 | /* any negative input will disable counter */ | |
327 | val = MALI_HW_CORE_NO_COUNTER; | |
328 | } | |
329 | ||
db9a41fa | 330 | mali_l2_cache_core_set_counter_src(l2_core, src_id, (u32)val); |
6fa3eb70 S |
331 | |
332 | *ppos += cnt; | |
333 | return cnt; | |
334 | } | |
335 | ||
336 | static ssize_t l2_all_counter_srcx_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos, u32 src_id) | |
337 | { | |
338 | char buf[64]; | |
339 | long val; | |
340 | int ret; | |
341 | u32 l2_id; | |
342 | struct mali_l2_cache_core *l2_cache; | |
343 | ||
344 | if (cnt >= sizeof(buf)) { | |
345 | return -EINVAL; | |
346 | } | |
347 | ||
348 | if (copy_from_user(&buf, ubuf, cnt)) { | |
349 | return -EFAULT; | |
350 | } | |
351 | ||
352 | buf[cnt] = 0; | |
353 | ||
db9a41fa | 354 | ret = kstrtol(buf, 10, &val); |
6fa3eb70 S |
355 | if (ret < 0) { |
356 | return ret; | |
357 | } | |
358 | ||
359 | if (val < 0) { | |
360 | /* any negative input will disable counter */ | |
361 | val = MALI_HW_CORE_NO_COUNTER; | |
362 | } | |
363 | ||
364 | l2_id = 0; | |
365 | l2_cache = mali_l2_cache_core_get_glob_l2_core(l2_id); | |
366 | while (NULL != l2_cache) { | |
db9a41fa | 367 | mali_l2_cache_core_set_counter_src(l2_cache, src_id, (u32)val); |
6fa3eb70 S |
368 | |
369 | /* try next L2 */ | |
370 | l2_id++; | |
371 | l2_cache = mali_l2_cache_core_get_glob_l2_core(l2_id); | |
372 | } | |
373 | ||
374 | *ppos += cnt; | |
375 | return cnt; | |
376 | } | |
377 | ||
378 | static ssize_t l2_l2x_counter_src0_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
379 | { | |
380 | return l2_l2x_counter_srcx_read(filp, ubuf, cnt, ppos, 0); | |
381 | } | |
382 | ||
383 | static ssize_t l2_l2x_counter_src1_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
384 | { | |
385 | return l2_l2x_counter_srcx_read(filp, ubuf, cnt, ppos, 1); | |
386 | } | |
387 | ||
388 | static ssize_t l2_l2x_counter_src0_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) | |
389 | { | |
390 | return l2_l2x_counter_srcx_write(filp, ubuf, cnt, ppos, 0); | |
391 | } | |
392 | ||
393 | static ssize_t l2_l2x_counter_src1_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) | |
394 | { | |
395 | return l2_l2x_counter_srcx_write(filp, ubuf, cnt, ppos, 1); | |
396 | } | |
397 | ||
398 | static ssize_t l2_all_counter_src0_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) | |
399 | { | |
400 | return l2_all_counter_srcx_write(filp, ubuf, cnt, ppos, 0); | |
401 | } | |
402 | ||
403 | static ssize_t l2_all_counter_src1_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) | |
404 | { | |
405 | return l2_all_counter_srcx_write(filp, ubuf, cnt, ppos, 1); | |
406 | } | |
407 | ||
408 | static const struct file_operations l2_l2x_counter_src0_fops = { | |
409 | .owner = THIS_MODULE, | |
410 | .open = open_copy_private_data, | |
411 | .read = l2_l2x_counter_src0_read, | |
412 | .write = l2_l2x_counter_src0_write, | |
413 | }; | |
414 | ||
415 | static const struct file_operations l2_l2x_counter_src1_fops = { | |
416 | .owner = THIS_MODULE, | |
417 | .open = open_copy_private_data, | |
418 | .read = l2_l2x_counter_src1_read, | |
419 | .write = l2_l2x_counter_src1_write, | |
420 | }; | |
421 | ||
422 | static const struct file_operations l2_all_counter_src0_fops = { | |
423 | .owner = THIS_MODULE, | |
424 | .write = l2_all_counter_src0_write, | |
425 | }; | |
426 | ||
427 | static const struct file_operations l2_all_counter_src1_fops = { | |
428 | .owner = THIS_MODULE, | |
429 | .write = l2_all_counter_src1_write, | |
430 | }; | |
431 | ||
db9a41fa S |
432 | static ssize_t l2_l2x_counter_valx_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos, u32 src_id) |
433 | { | |
434 | char buf[64]; | |
435 | int r; | |
436 | u32 src0 = 0; | |
437 | u32 val0 = 0; | |
438 | u32 src1 = 0; | |
439 | u32 val1 = 0; | |
440 | u32 val = -1; | |
441 | struct mali_l2_cache_core *l2_core = (struct mali_l2_cache_core *)filp->private_data; | |
442 | ||
443 | mali_l2_cache_core_get_counter_values(l2_core, &src0, &val0, &src1, &val1); | |
444 | ||
445 | if (0 == src_id) { | |
446 | if (MALI_HW_CORE_NO_COUNTER != val0) { | |
447 | val = val0; | |
448 | } | |
449 | } else { | |
450 | if (MALI_HW_CORE_NO_COUNTER != val1) { | |
451 | val = val1; | |
452 | } | |
453 | } | |
454 | ||
455 | r = snprintf(buf, 64, "%u\n", val); | |
456 | ||
457 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | |
458 | } | |
459 | ||
460 | static ssize_t l2_l2x_counter_val0_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
461 | { | |
462 | return l2_l2x_counter_valx_read(filp, ubuf, cnt, ppos, 0); | |
463 | } | |
464 | ||
465 | static ssize_t l2_l2x_counter_val1_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
466 | { | |
467 | return l2_l2x_counter_valx_read(filp, ubuf, cnt, ppos, 1); | |
468 | } | |
469 | ||
470 | static const struct file_operations l2_l2x_counter_val0_fops = { | |
471 | .owner = THIS_MODULE, | |
472 | .open = open_copy_private_data, | |
473 | .read = l2_l2x_counter_val0_read, | |
474 | }; | |
475 | ||
476 | static const struct file_operations l2_l2x_counter_val1_fops = { | |
477 | .owner = THIS_MODULE, | |
478 | .open = open_copy_private_data, | |
479 | .read = l2_l2x_counter_val1_read, | |
480 | }; | |
481 | ||
6fa3eb70 S |
482 | static ssize_t power_always_on_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) |
483 | { | |
484 | unsigned long val; | |
485 | int ret; | |
486 | char buf[32]; | |
487 | ||
488 | cnt = min(cnt, sizeof(buf) - 1); | |
489 | if (copy_from_user(buf, ubuf, cnt)) { | |
490 | return -EFAULT; | |
491 | } | |
492 | buf[cnt] = '\0'; | |
493 | ||
db9a41fa | 494 | ret = kstrtoul(buf, 10, &val); |
6fa3eb70 S |
495 | if (0 != ret) { |
496 | return ret; | |
497 | } | |
498 | ||
499 | /* Update setting (not exactly thread safe) */ | |
500 | if (1 == val && MALI_FALSE == power_always_on_enabled) { | |
501 | power_always_on_enabled = MALI_TRUE; | |
db9a41fa | 502 | _mali_osk_pm_dev_ref_get_sync(); |
6fa3eb70 S |
503 | } else if (0 == val && MALI_TRUE == power_always_on_enabled) { |
504 | power_always_on_enabled = MALI_FALSE; | |
db9a41fa | 505 | _mali_osk_pm_dev_ref_put(); |
6fa3eb70 S |
506 | } |
507 | ||
508 | *ppos += cnt; | |
509 | return cnt; | |
510 | } | |
511 | ||
512 | static ssize_t power_always_on_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
513 | { | |
514 | if (MALI_TRUE == power_always_on_enabled) { | |
515 | return simple_read_from_buffer(ubuf, cnt, ppos, "1\n", 2); | |
516 | } else { | |
517 | return simple_read_from_buffer(ubuf, cnt, ppos, "0\n", 2); | |
518 | } | |
519 | } | |
520 | ||
521 | static const struct file_operations power_always_on_fops = { | |
522 | .owner = THIS_MODULE, | |
523 | .read = power_always_on_read, | |
524 | .write = power_always_on_write, | |
525 | }; | |
526 | ||
527 | static ssize_t power_power_events_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) | |
528 | { | |
db9a41fa S |
529 | if (!strncmp(ubuf, mali_power_events[_MALI_DEVICE_SUSPEND], strlen(mali_power_events[_MALI_DEVICE_SUSPEND]) - 1)) { |
530 | mali_pm_os_suspend(MALI_TRUE); | |
531 | } else if (!strncmp(ubuf, mali_power_events[_MALI_DEVICE_RESUME], strlen(mali_power_events[_MALI_DEVICE_RESUME]) - 1)) { | |
6fa3eb70 | 532 | mali_pm_os_resume(); |
db9a41fa | 533 | } else if (!strncmp(ubuf, mali_power_events[_MALI_DEVICE_DVFS_PAUSE], strlen(mali_power_events[_MALI_DEVICE_DVFS_PAUSE]) - 1)) { |
6fa3eb70 | 534 | mali_dev_pause(); |
db9a41fa | 535 | } else if (!strncmp(ubuf, mali_power_events[_MALI_DEVICE_DVFS_RESUME], strlen(mali_power_events[_MALI_DEVICE_DVFS_RESUME]) - 1)) { |
6fa3eb70 S |
536 | mali_dev_resume(); |
537 | } | |
538 | *ppos += cnt; | |
539 | return cnt; | |
540 | } | |
541 | ||
542 | static loff_t power_power_events_seek(struct file *file, loff_t offset, int orig) | |
543 | { | |
544 | file->f_pos = offset; | |
545 | return 0; | |
546 | } | |
547 | ||
548 | static const struct file_operations power_power_events_fops = { | |
549 | .owner = THIS_MODULE, | |
550 | .write = power_power_events_write, | |
551 | .llseek = power_power_events_seek, | |
552 | }; | |
553 | ||
554 | #if MALI_STATE_TRACKING | |
555 | static int mali_seq_internal_state_show(struct seq_file *seq_file, void *v) | |
556 | { | |
557 | u32 len = 0; | |
558 | u32 size; | |
559 | char *buf; | |
560 | ||
561 | size = seq_get_buf(seq_file, &buf); | |
562 | ||
db9a41fa | 563 | if (!size) { |
6fa3eb70 S |
564 | return -ENOMEM; |
565 | } | |
566 | ||
567 | /* Create the internal state dump. */ | |
db9a41fa S |
568 | len = snprintf(buf + len, size - len, "Mali device driver %s\n", SVN_REV_STRING); |
569 | len += snprintf(buf + len, size - len, "License: %s\n\n", MALI_KERNEL_LINUX_LICENSE); | |
6fa3eb70 S |
570 | |
571 | len += _mali_kernel_core_dump_state(buf + len, size - len); | |
572 | ||
573 | seq_commit(seq_file, len); | |
574 | ||
575 | return 0; | |
576 | } | |
577 | ||
578 | static int mali_seq_internal_state_open(struct inode *inode, struct file *file) | |
579 | { | |
580 | return single_open(file, mali_seq_internal_state_show, NULL); | |
581 | } | |
582 | ||
583 | static const struct file_operations mali_seq_internal_state_fops = { | |
584 | .owner = THIS_MODULE, | |
585 | .open = mali_seq_internal_state_open, | |
586 | .read = seq_read, | |
587 | .llseek = seq_lseek, | |
588 | .release = single_release, | |
589 | }; | |
590 | #endif /* MALI_STATE_TRACKING */ | |
591 | ||
592 | #if defined(CONFIG_MALI400_INTERNAL_PROFILING) | |
593 | static ssize_t profiling_record_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
594 | { | |
595 | char buf[64]; | |
596 | int r; | |
597 | ||
db9a41fa | 598 | r = snprintf(buf, 64, "%u\n", _mali_internal_profiling_is_recording() ? 1 : 0); |
6fa3eb70 S |
599 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); |
600 | } | |
601 | ||
602 | static ssize_t profiling_record_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) | |
603 | { | |
604 | char buf[64]; | |
605 | unsigned long val; | |
606 | int ret; | |
607 | ||
608 | if (cnt >= sizeof(buf)) { | |
609 | return -EINVAL; | |
610 | } | |
611 | ||
612 | if (copy_from_user(&buf, ubuf, cnt)) { | |
613 | return -EFAULT; | |
614 | } | |
615 | ||
616 | buf[cnt] = 0; | |
617 | ||
db9a41fa | 618 | ret = kstrtoul(buf, 10, &val); |
6fa3eb70 S |
619 | if (ret < 0) { |
620 | return ret; | |
621 | } | |
622 | ||
623 | if (val != 0) { | |
624 | u32 limit = MALI_PROFILING_MAX_BUFFER_ENTRIES; /* This can be made configurable at a later stage if we need to */ | |
625 | ||
626 | /* check if we are already recording */ | |
627 | if (MALI_TRUE == _mali_internal_profiling_is_recording()) { | |
628 | MALI_DEBUG_PRINT(3, ("Recording of profiling events already in progress\n")); | |
629 | return -EFAULT; | |
630 | } | |
631 | ||
632 | /* check if we need to clear out an old recording first */ | |
633 | if (MALI_TRUE == _mali_internal_profiling_have_recording()) { | |
634 | if (_MALI_OSK_ERR_OK != _mali_internal_profiling_clear()) { | |
635 | MALI_DEBUG_PRINT(3, ("Failed to clear existing recording of profiling events\n")); | |
636 | return -EFAULT; | |
637 | } | |
638 | } | |
639 | ||
640 | /* start recording profiling data */ | |
641 | if (_MALI_OSK_ERR_OK != _mali_internal_profiling_start(&limit)) { | |
642 | MALI_DEBUG_PRINT(3, ("Failed to start recording of profiling events\n")); | |
643 | return -EFAULT; | |
644 | } | |
645 | ||
646 | MALI_DEBUG_PRINT(3, ("Profiling recording started (max %u events)\n", limit)); | |
647 | } else { | |
648 | /* stop recording profiling data */ | |
649 | u32 count = 0; | |
650 | if (_MALI_OSK_ERR_OK != _mali_internal_profiling_stop(&count)) { | |
651 | MALI_DEBUG_PRINT(2, ("Failed to stop recording of profiling events\n")); | |
652 | return -EFAULT; | |
653 | } | |
654 | ||
655 | MALI_DEBUG_PRINT(2, ("Profiling recording stopped (recorded %u events)\n", count)); | |
656 | } | |
657 | ||
658 | *ppos += cnt; | |
659 | return cnt; | |
660 | } | |
661 | ||
662 | static const struct file_operations profiling_record_fops = { | |
663 | .owner = THIS_MODULE, | |
664 | .read = profiling_record_read, | |
665 | .write = profiling_record_write, | |
666 | }; | |
667 | ||
668 | static void *profiling_events_start(struct seq_file *s, loff_t *pos) | |
669 | { | |
670 | loff_t *spos; | |
671 | ||
672 | /* check if we have data avaiable */ | |
673 | if (MALI_TRUE != _mali_internal_profiling_have_recording()) { | |
674 | return NULL; | |
675 | } | |
676 | ||
677 | spos = kmalloc(sizeof(loff_t), GFP_KERNEL); | |
678 | if (NULL == spos) { | |
679 | return NULL; | |
680 | } | |
681 | ||
682 | *spos = *pos; | |
683 | return spos; | |
684 | } | |
685 | ||
686 | static void *profiling_events_next(struct seq_file *s, void *v, loff_t *pos) | |
687 | { | |
688 | loff_t *spos = v; | |
689 | ||
690 | /* check if we have data avaiable */ | |
691 | if (MALI_TRUE != _mali_internal_profiling_have_recording()) { | |
692 | return NULL; | |
693 | } | |
694 | ||
695 | /* check if the next entry actually is avaiable */ | |
696 | if (_mali_internal_profiling_get_count() <= (u32)(*spos + 1)) { | |
697 | return NULL; | |
698 | } | |
699 | ||
700 | *pos = ++*spos; | |
701 | return spos; | |
702 | } | |
703 | ||
704 | static void profiling_events_stop(struct seq_file *s, void *v) | |
705 | { | |
706 | kfree(v); | |
707 | } | |
708 | ||
709 | static int profiling_events_show(struct seq_file *seq_file, void *v) | |
710 | { | |
711 | loff_t *spos = v; | |
712 | u32 index; | |
713 | u64 timestamp; | |
714 | u32 event_id; | |
715 | u32 data[5]; | |
716 | ||
db9a41fa | 717 | index = (u32) * spos; |
6fa3eb70 S |
718 | |
719 | /* Retrieve all events */ | |
720 | if (_MALI_OSK_ERR_OK == _mali_internal_profiling_get_event(index, ×tamp, &event_id, data)) { | |
721 | seq_printf(seq_file, "%llu %u %u %u %u %u %u\n", timestamp, event_id, data[0], data[1], data[2], data[3], data[4]); | |
722 | return 0; | |
723 | } | |
724 | ||
725 | return 0; | |
726 | } | |
727 | ||
728 | static int profiling_events_show_human_readable(struct seq_file *seq_file, void *v) | |
729 | { | |
730 | #define MALI_EVENT_ID_IS_HW(event_id) (((event_id & 0x00FF0000) >= MALI_PROFILING_EVENT_CHANNEL_GP0) && ((event_id & 0x00FF0000) <= MALI_PROFILING_EVENT_CHANNEL_PP7)) | |
731 | ||
732 | static u64 start_time = 0; | |
733 | loff_t *spos = v; | |
734 | u32 index; | |
735 | u64 timestamp; | |
736 | u32 event_id; | |
737 | u32 data[5]; | |
738 | ||
db9a41fa | 739 | index = (u32) * spos; |
6fa3eb70 S |
740 | |
741 | /* Retrieve all events */ | |
742 | if (_MALI_OSK_ERR_OK == _mali_internal_profiling_get_event(index, ×tamp, &event_id, data)) { | |
743 | seq_printf(seq_file, "%llu %u %u %u %u %u %u # ", timestamp, event_id, data[0], data[1], data[2], data[3], data[4]); | |
744 | ||
745 | if (0 == index) { | |
746 | start_time = timestamp; | |
747 | } | |
748 | ||
749 | seq_printf(seq_file, "[%06u] ", index); | |
750 | ||
db9a41fa | 751 | switch (event_id & 0x0F000000) { |
6fa3eb70 S |
752 | case MALI_PROFILING_EVENT_TYPE_SINGLE: |
753 | seq_printf(seq_file, "SINGLE | "); | |
754 | break; | |
755 | case MALI_PROFILING_EVENT_TYPE_START: | |
756 | seq_printf(seq_file, "START | "); | |
757 | break; | |
758 | case MALI_PROFILING_EVENT_TYPE_STOP: | |
759 | seq_printf(seq_file, "STOP | "); | |
760 | break; | |
761 | case MALI_PROFILING_EVENT_TYPE_SUSPEND: | |
762 | seq_printf(seq_file, "SUSPEND | "); | |
763 | break; | |
764 | case MALI_PROFILING_EVENT_TYPE_RESUME: | |
765 | seq_printf(seq_file, "RESUME | "); | |
766 | break; | |
767 | default: | |
768 | seq_printf(seq_file, "0x%01X | ", (event_id & 0x0F000000) >> 24); | |
769 | break; | |
770 | } | |
771 | ||
db9a41fa | 772 | switch (event_id & 0x00FF0000) { |
6fa3eb70 S |
773 | case MALI_PROFILING_EVENT_CHANNEL_SOFTWARE: |
774 | seq_printf(seq_file, "SW | "); | |
775 | break; | |
776 | case MALI_PROFILING_EVENT_CHANNEL_GP0: | |
777 | seq_printf(seq_file, "GP0 | "); | |
778 | break; | |
779 | case MALI_PROFILING_EVENT_CHANNEL_PP0: | |
780 | seq_printf(seq_file, "PP0 | "); | |
781 | break; | |
782 | case MALI_PROFILING_EVENT_CHANNEL_PP1: | |
783 | seq_printf(seq_file, "PP1 | "); | |
784 | break; | |
785 | case MALI_PROFILING_EVENT_CHANNEL_PP2: | |
786 | seq_printf(seq_file, "PP2 | "); | |
787 | break; | |
788 | case MALI_PROFILING_EVENT_CHANNEL_PP3: | |
789 | seq_printf(seq_file, "PP3 | "); | |
790 | break; | |
791 | case MALI_PROFILING_EVENT_CHANNEL_PP4: | |
792 | seq_printf(seq_file, "PP4 | "); | |
793 | break; | |
794 | case MALI_PROFILING_EVENT_CHANNEL_PP5: | |
795 | seq_printf(seq_file, "PP5 | "); | |
796 | break; | |
797 | case MALI_PROFILING_EVENT_CHANNEL_PP6: | |
798 | seq_printf(seq_file, "PP6 | "); | |
799 | break; | |
800 | case MALI_PROFILING_EVENT_CHANNEL_PP7: | |
801 | seq_printf(seq_file, "PP7 | "); | |
802 | break; | |
803 | case MALI_PROFILING_EVENT_CHANNEL_GPU: | |
804 | seq_printf(seq_file, "GPU | "); | |
805 | break; | |
806 | default: | |
807 | seq_printf(seq_file, "0x%02X | ", (event_id & 0x00FF0000) >> 16); | |
808 | break; | |
809 | } | |
810 | ||
811 | if (MALI_EVENT_ID_IS_HW(event_id)) { | |
812 | if (((event_id & 0x0F000000) == MALI_PROFILING_EVENT_TYPE_START) || ((event_id & 0x0F000000) == MALI_PROFILING_EVENT_TYPE_STOP)) { | |
db9a41fa | 813 | switch (event_id & 0x0000FFFF) { |
6fa3eb70 S |
814 | case MALI_PROFILING_EVENT_REASON_START_STOP_HW_PHYSICAL: |
815 | seq_printf(seq_file, "PHYSICAL | "); | |
816 | break; | |
817 | case MALI_PROFILING_EVENT_REASON_START_STOP_HW_VIRTUAL: | |
818 | seq_printf(seq_file, "VIRTUAL | "); | |
819 | break; | |
820 | default: | |
821 | seq_printf(seq_file, "0x%04X | ", event_id & 0x0000FFFF); | |
822 | break; | |
823 | } | |
824 | } else { | |
825 | seq_printf(seq_file, "0x%04X | ", event_id & 0x0000FFFF); | |
826 | } | |
827 | } else { | |
828 | seq_printf(seq_file, "0x%04X | ", event_id & 0x0000FFFF); | |
829 | } | |
830 | ||
831 | seq_printf(seq_file, "T0 + 0x%016llX\n", timestamp - start_time); | |
832 | ||
833 | return 0; | |
834 | } | |
835 | ||
836 | return 0; | |
837 | } | |
838 | ||
839 | static const struct seq_operations profiling_events_seq_ops = { | |
840 | .start = profiling_events_start, | |
841 | .next = profiling_events_next, | |
842 | .stop = profiling_events_stop, | |
843 | .show = profiling_events_show | |
844 | }; | |
845 | ||
846 | static int profiling_events_open(struct inode *inode, struct file *file) | |
847 | { | |
848 | return seq_open(file, &profiling_events_seq_ops); | |
849 | } | |
850 | ||
851 | static const struct file_operations profiling_events_fops = { | |
852 | .owner = THIS_MODULE, | |
853 | .open = profiling_events_open, | |
854 | .read = seq_read, | |
855 | .llseek = seq_lseek, | |
856 | .release = seq_release, | |
857 | }; | |
858 | ||
859 | static const struct seq_operations profiling_events_human_readable_seq_ops = { | |
860 | .start = profiling_events_start, | |
861 | .next = profiling_events_next, | |
862 | .stop = profiling_events_stop, | |
863 | .show = profiling_events_show_human_readable | |
864 | }; | |
865 | ||
866 | static int profiling_events_human_readable_open(struct inode *inode, struct file *file) | |
867 | { | |
868 | return seq_open(file, &profiling_events_human_readable_seq_ops); | |
869 | } | |
870 | ||
871 | static const struct file_operations profiling_events_human_readable_fops = { | |
872 | .owner = THIS_MODULE, | |
873 | .open = profiling_events_human_readable_open, | |
874 | .read = seq_read, | |
875 | .llseek = seq_lseek, | |
876 | .release = seq_release, | |
877 | }; | |
878 | ||
879 | #endif | |
880 | ||
db9a41fa | 881 | static int memory_debugfs_show(struct seq_file *s, void *private_data) |
6fa3eb70 | 882 | { |
db9a41fa S |
883 | seq_printf(s, " %-25s %-10s %-10s %-15s %-15s %-10s %-10s\n"\ |
884 | "==============================================================================================================\n", | |
885 | "Name (:bytes)", "pid", "mali_mem", "max_mali_mem", | |
886 | "external_mem", "ump_mem", "dma_mem"); | |
887 | mali_session_memory_tracking(s); | |
888 | return 0; | |
889 | } | |
6fa3eb70 | 890 | |
db9a41fa S |
891 | static int memory_debugfs_open(struct inode *inode, struct file *file) |
892 | { | |
893 | return single_open(file, memory_debugfs_show, inode->i_private); | |
6fa3eb70 S |
894 | } |
895 | ||
896 | static const struct file_operations memory_usage_fops = { | |
897 | .owner = THIS_MODULE, | |
db9a41fa S |
898 | .open = memory_debugfs_open, |
899 | .read = seq_read, | |
900 | .llseek = seq_lseek, | |
901 | .release = single_release, | |
6fa3eb70 S |
902 | }; |
903 | ||
904 | static ssize_t utilization_gp_pp_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
905 | { | |
906 | char buf[64]; | |
907 | size_t r; | |
db9a41fa | 908 | u32 uval = _mali_ukk_utilization_gp_pp(); |
6fa3eb70 S |
909 | |
910 | r = snprintf(buf, 64, "%u\n", uval); | |
911 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | |
912 | } | |
913 | ||
914 | static ssize_t utilization_gp_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
915 | { | |
916 | char buf[64]; | |
917 | size_t r; | |
db9a41fa | 918 | u32 uval = _mali_ukk_utilization_gp(); |
6fa3eb70 S |
919 | |
920 | r = snprintf(buf, 64, "%u\n", uval); | |
921 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | |
922 | } | |
923 | ||
924 | static ssize_t utilization_pp_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
925 | { | |
926 | char buf[64]; | |
927 | size_t r; | |
db9a41fa | 928 | u32 uval = _mali_ukk_utilization_pp(); |
6fa3eb70 S |
929 | |
930 | r = snprintf(buf, 64, "%u\n", uval); | |
931 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | |
932 | } | |
933 | ||
934 | ||
935 | static const struct file_operations utilization_gp_pp_fops = { | |
936 | .owner = THIS_MODULE, | |
937 | .read = utilization_gp_pp_read, | |
938 | }; | |
939 | ||
940 | static const struct file_operations utilization_gp_fops = { | |
941 | .owner = THIS_MODULE, | |
942 | .read = utilization_gp_read, | |
943 | }; | |
944 | ||
945 | static const struct file_operations utilization_pp_fops = { | |
946 | .owner = THIS_MODULE, | |
947 | .read = utilization_pp_read, | |
948 | }; | |
949 | ||
950 | static ssize_t user_settings_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) | |
951 | { | |
952 | unsigned long val; | |
953 | int ret; | |
954 | _mali_uk_user_setting_t setting; | |
955 | char buf[32]; | |
956 | ||
957 | cnt = min(cnt, sizeof(buf) - 1); | |
958 | if (copy_from_user(buf, ubuf, cnt)) { | |
959 | return -EFAULT; | |
960 | } | |
961 | buf[cnt] = '\0'; | |
962 | ||
db9a41fa | 963 | ret = kstrtoul(buf, 10, &val); |
6fa3eb70 S |
964 | if (0 != ret) { |
965 | return ret; | |
966 | } | |
967 | ||
968 | /* Update setting */ | |
969 | setting = (_mali_uk_user_setting_t)(filp->private_data); | |
970 | mali_set_user_setting(setting, val); | |
971 | ||
972 | *ppos += cnt; | |
973 | return cnt; | |
974 | } | |
975 | ||
976 | static ssize_t user_settings_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |
977 | { | |
978 | char buf[64]; | |
979 | size_t r; | |
980 | u32 value; | |
981 | _mali_uk_user_setting_t setting; | |
982 | ||
983 | setting = (_mali_uk_user_setting_t)(filp->private_data); | |
984 | value = mali_get_user_setting(setting); | |
985 | ||
986 | r = snprintf(buf, 64, "%u\n", value); | |
987 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | |
988 | } | |
989 | ||
990 | static const struct file_operations user_settings_fops = { | |
991 | .owner = THIS_MODULE, | |
992 | .open = open_copy_private_data, | |
993 | .read = user_settings_read, | |
994 | .write = user_settings_write, | |
995 | }; | |
996 | ||
997 | static int mali_sysfs_user_settings_register(void) | |
998 | { | |
999 | struct dentry *mali_user_settings_dir = debugfs_create_dir("userspace_settings", mali_debugfs_dir); | |
1000 | ||
1001 | if (mali_user_settings_dir != NULL) { | |
db9a41fa | 1002 | long i; |
6fa3eb70 | 1003 | for (i = 0; i < _MALI_UK_USER_SETTING_MAX; i++) { |
db9a41fa S |
1004 | debugfs_create_file(_mali_uk_user_setting_descriptions[i], |
1005 | 0600, mali_user_settings_dir, (void *)i, | |
1006 | &user_settings_fops); | |
6fa3eb70 S |
1007 | } |
1008 | } | |
1009 | ||
1010 | return 0; | |
1011 | } | |
1012 | ||
6fa3eb70 S |
1013 | static ssize_t pp_num_cores_enabled_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) |
1014 | { | |
1015 | int ret; | |
1016 | char buffer[32]; | |
1017 | unsigned long val; | |
1018 | ||
1019 | if (count >= sizeof(buffer)) { | |
1020 | return -ENOMEM; | |
1021 | } | |
1022 | ||
1023 | if (copy_from_user(&buffer[0], buf, count)) { | |
1024 | return -EFAULT; | |
1025 | } | |
1026 | buffer[count] = '\0'; | |
1027 | ||
db9a41fa | 1028 | ret = kstrtoul(&buffer[0], 10, &val); |
6fa3eb70 S |
1029 | if (0 != ret) { |
1030 | return -EINVAL; | |
1031 | } | |
1032 | ||
db9a41fa | 1033 | ret = mali_executor_set_perf_level(val, MALI_TRUE); /* override even if core scaling is disabled */ |
6fa3eb70 S |
1034 | if (ret) { |
1035 | return ret; | |
1036 | } | |
1037 | ||
1038 | *offp += count; | |
1039 | return count; | |
1040 | } | |
1041 | ||
1042 | static ssize_t pp_num_cores_enabled_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) | |
1043 | { | |
1044 | int r; | |
1045 | char buffer[64]; | |
1046 | ||
db9a41fa | 1047 | r = snprintf(buffer, 64, "%u\n", mali_executor_get_num_cores_enabled()); |
6fa3eb70 S |
1048 | |
1049 | return simple_read_from_buffer(buf, count, offp, buffer, r); | |
1050 | } | |
1051 | ||
1052 | static const struct file_operations pp_num_cores_enabled_fops = { | |
1053 | .owner = THIS_MODULE, | |
1054 | .write = pp_num_cores_enabled_write, | |
1055 | .read = pp_num_cores_enabled_read, | |
1056 | .llseek = default_llseek, | |
1057 | }; | |
1058 | ||
1059 | static ssize_t pp_num_cores_total_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) | |
1060 | { | |
1061 | int r; | |
1062 | char buffer[64]; | |
1063 | ||
db9a41fa | 1064 | r = snprintf(buffer, 64, "%u\n", mali_executor_get_num_cores_total()); |
6fa3eb70 S |
1065 | |
1066 | return simple_read_from_buffer(buf, count, offp, buffer, r); | |
1067 | } | |
1068 | ||
1069 | static const struct file_operations pp_num_cores_total_fops = { | |
1070 | .owner = THIS_MODULE, | |
1071 | .read = pp_num_cores_total_read, | |
1072 | }; | |
1073 | ||
1074 | static ssize_t pp_core_scaling_enabled_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) | |
1075 | { | |
1076 | int ret; | |
1077 | char buffer[32]; | |
1078 | unsigned long val; | |
1079 | ||
1080 | if (count >= sizeof(buffer)) { | |
1081 | return -ENOMEM; | |
1082 | } | |
1083 | ||
1084 | if (copy_from_user(&buffer[0], buf, count)) { | |
1085 | return -EFAULT; | |
1086 | } | |
1087 | buffer[count] = '\0'; | |
1088 | ||
db9a41fa | 1089 | ret = kstrtoul(&buffer[0], 10, &val); |
6fa3eb70 S |
1090 | if (0 != ret) { |
1091 | return -EINVAL; | |
1092 | } | |
1093 | ||
1094 | switch (val) { | |
1095 | case 1: | |
db9a41fa | 1096 | mali_executor_core_scaling_enable(); |
6fa3eb70 S |
1097 | break; |
1098 | case 0: | |
db9a41fa | 1099 | mali_executor_core_scaling_disable(); |
6fa3eb70 S |
1100 | break; |
1101 | default: | |
1102 | return -EINVAL; | |
1103 | break; | |
1104 | } | |
1105 | ||
1106 | *offp += count; | |
1107 | return count; | |
1108 | } | |
1109 | ||
1110 | static ssize_t pp_core_scaling_enabled_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) | |
1111 | { | |
db9a41fa | 1112 | return simple_read_from_buffer(buf, count, offp, mali_executor_core_scaling_is_enabled() ? "1\n" : "0\n", 2); |
6fa3eb70 S |
1113 | } |
1114 | static const struct file_operations pp_core_scaling_enabled_fops = { | |
1115 | .owner = THIS_MODULE, | |
1116 | .write = pp_core_scaling_enabled_write, | |
1117 | .read = pp_core_scaling_enabled_read, | |
1118 | .llseek = default_llseek, | |
1119 | }; | |
1120 | ||
1121 | static ssize_t version_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) | |
1122 | { | |
1123 | int r = 0; | |
1124 | char buffer[64]; | |
1125 | ||
1126 | switch (mali_kernel_core_get_product_id()) { | |
1127 | case _MALI_PRODUCT_ID_MALI200: | |
db9a41fa | 1128 | r = snprintf(buffer, 64, "Mali-200\n"); |
6fa3eb70 S |
1129 | break; |
1130 | case _MALI_PRODUCT_ID_MALI300: | |
db9a41fa | 1131 | r = snprintf(buffer, 64, "Mali-300\n"); |
6fa3eb70 S |
1132 | break; |
1133 | case _MALI_PRODUCT_ID_MALI400: | |
db9a41fa | 1134 | r = snprintf(buffer, 64, "Mali-400 MP\n"); |
6fa3eb70 S |
1135 | break; |
1136 | case _MALI_PRODUCT_ID_MALI450: | |
db9a41fa S |
1137 | r = snprintf(buffer, 64, "Mali-450 MP\n"); |
1138 | break; | |
1139 | case _MALI_PRODUCT_ID_MALI470: | |
1140 | r = snprintf(buffer, 64, "Mali-470 MP\n"); | |
6fa3eb70 S |
1141 | break; |
1142 | case _MALI_PRODUCT_ID_UNKNOWN: | |
1143 | return -EINVAL; | |
1144 | break; | |
1145 | }; | |
1146 | ||
1147 | return simple_read_from_buffer(buf, count, offp, buffer, r); | |
1148 | } | |
1149 | ||
1150 | static const struct file_operations version_fops = { | |
1151 | .owner = THIS_MODULE, | |
1152 | .read = version_read, | |
1153 | }; | |
1154 | ||
db9a41fa S |
1155 | #if defined(DEBUG) |
1156 | static int timeline_debugfs_show(struct seq_file *s, void *private_data) | |
1157 | { | |
1158 | struct mali_session_data *session, *tmp; | |
1159 | u32 session_seq = 1; | |
1160 | ||
1161 | seq_printf(s, "timeline system info: \n=================\n\n"); | |
1162 | ||
1163 | mali_session_lock(); | |
1164 | MALI_SESSION_FOREACH(session, tmp, link) { | |
1165 | seq_printf(s, "session %d <%p> start:\n", session_seq, session); | |
1166 | mali_timeline_debug_print_system(session->timeline_system, s); | |
1167 | seq_printf(s, "session %d end\n\n\n", session_seq++); | |
1168 | } | |
1169 | mali_session_unlock(); | |
1170 | ||
1171 | return 0; | |
1172 | } | |
1173 | ||
1174 | static int timeline_debugfs_open(struct inode *inode, struct file *file) | |
1175 | { | |
1176 | return single_open(file, timeline_debugfs_show, inode->i_private); | |
1177 | } | |
1178 | ||
1179 | static const struct file_operations timeline_dump_fops = { | |
1180 | .owner = THIS_MODULE, | |
1181 | .open = timeline_debugfs_open, | |
1182 | .read = seq_read, | |
1183 | .llseek = seq_lseek, | |
1184 | .release = single_release | |
1185 | }; | |
1186 | #endif | |
1187 | ||
6fa3eb70 S |
1188 | int mali_sysfs_register(const char *mali_dev_name) |
1189 | { | |
1190 | mali_debugfs_dir = debugfs_create_dir(mali_dev_name, NULL); | |
db9a41fa | 1191 | if (ERR_PTR(-ENODEV) == mali_debugfs_dir) { |
6fa3eb70 S |
1192 | /* Debugfs not supported. */ |
1193 | mali_debugfs_dir = NULL; | |
1194 | } else { | |
db9a41fa | 1195 | if (NULL != mali_debugfs_dir) { |
6fa3eb70 | 1196 | /* Debugfs directory created successfully; create files now */ |
6fa3eb70 S |
1197 | struct dentry *mali_power_dir; |
1198 | struct dentry *mali_gp_dir; | |
1199 | struct dentry *mali_pp_dir; | |
1200 | struct dentry *mali_l2_dir; | |
1201 | struct dentry *mali_profiling_dir; | |
1202 | ||
1203 | debugfs_create_file("version", 0400, mali_debugfs_dir, NULL, &version_fops); | |
1204 | ||
6fa3eb70 S |
1205 | mali_power_dir = debugfs_create_dir("power", mali_debugfs_dir); |
1206 | if (mali_power_dir != NULL) { | |
1207 | debugfs_create_file("always_on", 0600, mali_power_dir, NULL, &power_always_on_fops); | |
1208 | debugfs_create_file("power_events", 0200, mali_power_dir, NULL, &power_power_events_fops); | |
1209 | } | |
1210 | ||
1211 | mali_gp_dir = debugfs_create_dir("gp", mali_debugfs_dir); | |
1212 | if (mali_gp_dir != NULL) { | |
1213 | u32 num_groups; | |
db9a41fa | 1214 | long i; |
6fa3eb70 S |
1215 | |
1216 | num_groups = mali_group_get_glob_num_groups(); | |
1217 | for (i = 0; i < num_groups; i++) { | |
1218 | struct mali_group *group = mali_group_get_glob_group(i); | |
1219 | ||
1220 | struct mali_gp_core *gp_core = mali_group_get_gp_core(group); | |
1221 | if (NULL != gp_core) { | |
1222 | struct dentry *mali_gp_gpx_dir; | |
1223 | mali_gp_gpx_dir = debugfs_create_dir("gp0", mali_gp_dir); | |
1224 | if (NULL != mali_gp_gpx_dir) { | |
1225 | debugfs_create_file("base_addr", 0400, mali_gp_gpx_dir, &gp_core->hw_core, &hw_core_base_addr_fops); | |
1226 | debugfs_create_file("enabled", 0600, mali_gp_gpx_dir, group, &group_enabled_fops); | |
1227 | } | |
1228 | break; /* no need to look for any other GP cores */ | |
1229 | } | |
1230 | ||
1231 | } | |
1232 | } | |
1233 | ||
1234 | mali_pp_dir = debugfs_create_dir("pp", mali_debugfs_dir); | |
1235 | if (mali_pp_dir != NULL) { | |
1236 | u32 num_groups; | |
db9a41fa | 1237 | long i; |
6fa3eb70 S |
1238 | |
1239 | debugfs_create_file("num_cores_total", 0400, mali_pp_dir, NULL, &pp_num_cores_total_fops); | |
1240 | debugfs_create_file("num_cores_enabled", 0600, mali_pp_dir, NULL, &pp_num_cores_enabled_fops); | |
1241 | debugfs_create_file("core_scaling_enabled", 0600, mali_pp_dir, NULL, &pp_core_scaling_enabled_fops); | |
1242 | ||
1243 | num_groups = mali_group_get_glob_num_groups(); | |
1244 | for (i = 0; i < num_groups; i++) { | |
1245 | struct mali_group *group = mali_group_get_glob_group(i); | |
1246 | ||
1247 | struct mali_pp_core *pp_core = mali_group_get_pp_core(group); | |
1248 | if (NULL != pp_core) { | |
1249 | char buf[16]; | |
1250 | struct dentry *mali_pp_ppx_dir; | |
1251 | _mali_osk_snprintf(buf, sizeof(buf), "pp%u", mali_pp_core_get_id(pp_core)); | |
1252 | mali_pp_ppx_dir = debugfs_create_dir(buf, mali_pp_dir); | |
1253 | if (NULL != mali_pp_ppx_dir) { | |
1254 | debugfs_create_file("base_addr", 0400, mali_pp_ppx_dir, &pp_core->hw_core, &hw_core_base_addr_fops); | |
1255 | if (!mali_group_is_virtual(group)) { | |
1256 | debugfs_create_file("enabled", 0600, mali_pp_ppx_dir, group, &group_enabled_fops); | |
1257 | } | |
1258 | } | |
1259 | } | |
1260 | } | |
1261 | } | |
1262 | ||
1263 | mali_l2_dir = debugfs_create_dir("l2", mali_debugfs_dir); | |
1264 | if (mali_l2_dir != NULL) { | |
1265 | struct dentry *mali_l2_all_dir; | |
1266 | u32 l2_id; | |
1267 | struct mali_l2_cache_core *l2_cache; | |
1268 | ||
1269 | mali_l2_all_dir = debugfs_create_dir("all", mali_l2_dir); | |
1270 | if (mali_l2_all_dir != NULL) { | |
1271 | debugfs_create_file("counter_src0", 0200, mali_l2_all_dir, NULL, &l2_all_counter_src0_fops); | |
1272 | debugfs_create_file("counter_src1", 0200, mali_l2_all_dir, NULL, &l2_all_counter_src1_fops); | |
1273 | } | |
1274 | ||
1275 | l2_id = 0; | |
1276 | l2_cache = mali_l2_cache_core_get_glob_l2_core(l2_id); | |
1277 | while (NULL != l2_cache) { | |
1278 | char buf[16]; | |
1279 | struct dentry *mali_l2_l2x_dir; | |
1280 | _mali_osk_snprintf(buf, sizeof(buf), "l2%u", l2_id); | |
1281 | mali_l2_l2x_dir = debugfs_create_dir(buf, mali_l2_dir); | |
1282 | if (NULL != mali_l2_l2x_dir) { | |
1283 | debugfs_create_file("counter_src0", 0600, mali_l2_l2x_dir, l2_cache, &l2_l2x_counter_src0_fops); | |
1284 | debugfs_create_file("counter_src1", 0600, mali_l2_l2x_dir, l2_cache, &l2_l2x_counter_src1_fops); | |
db9a41fa S |
1285 | debugfs_create_file("counter_val0", 0600, mali_l2_l2x_dir, l2_cache, &l2_l2x_counter_val0_fops); |
1286 | debugfs_create_file("counter_val1", 0600, mali_l2_l2x_dir, l2_cache, &l2_l2x_counter_val1_fops); | |
6fa3eb70 S |
1287 | debugfs_create_file("base_addr", 0400, mali_l2_l2x_dir, &l2_cache->hw_core, &hw_core_base_addr_fops); |
1288 | } | |
1289 | ||
1290 | /* try next L2 */ | |
1291 | l2_id++; | |
1292 | l2_cache = mali_l2_cache_core_get_glob_l2_core(l2_id); | |
1293 | } | |
1294 | } | |
1295 | ||
db9a41fa | 1296 | debugfs_create_file("gpu_memory", 0444, mali_debugfs_dir, NULL, &memory_usage_fops); |
6fa3eb70 S |
1297 | |
1298 | debugfs_create_file("utilization_gp_pp", 0400, mali_debugfs_dir, NULL, &utilization_gp_pp_fops); | |
1299 | debugfs_create_file("utilization_gp", 0400, mali_debugfs_dir, NULL, &utilization_gp_fops); | |
1300 | debugfs_create_file("utilization_pp", 0400, mali_debugfs_dir, NULL, &utilization_pp_fops); | |
1301 | ||
1302 | mali_profiling_dir = debugfs_create_dir("profiling", mali_debugfs_dir); | |
1303 | if (mali_profiling_dir != NULL) { | |
1304 | u32 max_sub_jobs; | |
db9a41fa | 1305 | long i; |
6fa3eb70 S |
1306 | struct dentry *mali_profiling_gp_dir; |
1307 | struct dentry *mali_profiling_pp_dir; | |
1308 | #if defined(CONFIG_MALI400_INTERNAL_PROFILING) | |
1309 | struct dentry *mali_profiling_proc_dir; | |
1310 | #endif | |
1311 | /* | |
1312 | * Create directory where we can set GP HW counters. | |
1313 | */ | |
1314 | mali_profiling_gp_dir = debugfs_create_dir("gp", mali_profiling_dir); | |
1315 | if (mali_profiling_gp_dir != NULL) { | |
db9a41fa S |
1316 | debugfs_create_file("counter_src0", 0600, mali_profiling_gp_dir, (void *)PRIVATE_DATA_COUNTER_MAKE_GP(0), &profiling_counter_src_fops); |
1317 | debugfs_create_file("counter_src1", 0600, mali_profiling_gp_dir, (void *)PRIVATE_DATA_COUNTER_MAKE_GP(1), &profiling_counter_src_fops); | |
6fa3eb70 S |
1318 | } |
1319 | ||
1320 | /* | |
1321 | * Create directory where we can set PP HW counters. | |
1322 | * Possible override with specific HW counters for a particular sub job | |
1323 | * (Disable core scaling before using the override!) | |
1324 | */ | |
1325 | mali_profiling_pp_dir = debugfs_create_dir("pp", mali_profiling_dir); | |
1326 | if (mali_profiling_pp_dir != NULL) { | |
db9a41fa S |
1327 | debugfs_create_file("counter_src0", 0600, mali_profiling_pp_dir, (void *)PRIVATE_DATA_COUNTER_MAKE_PP(0), &profiling_counter_src_fops); |
1328 | debugfs_create_file("counter_src1", 0600, mali_profiling_pp_dir, (void *)PRIVATE_DATA_COUNTER_MAKE_PP(1), &profiling_counter_src_fops); | |
6fa3eb70 S |
1329 | } |
1330 | ||
db9a41fa | 1331 | max_sub_jobs = mali_executor_get_num_cores_total(); |
6fa3eb70 S |
1332 | for (i = 0; i < max_sub_jobs; i++) { |
1333 | char buf[16]; | |
1334 | struct dentry *mali_profiling_pp_x_dir; | |
1335 | _mali_osk_snprintf(buf, sizeof(buf), "%u", i); | |
1336 | mali_profiling_pp_x_dir = debugfs_create_dir(buf, mali_profiling_pp_dir); | |
1337 | if (NULL != mali_profiling_pp_x_dir) { | |
db9a41fa S |
1338 | debugfs_create_file("counter_src0", |
1339 | 0600, mali_profiling_pp_x_dir, | |
1340 | (void *)PRIVATE_DATA_COUNTER_MAKE_PP_SUB_JOB(0, i), | |
1341 | &profiling_counter_src_fops); | |
1342 | debugfs_create_file("counter_src1", | |
1343 | 0600, mali_profiling_pp_x_dir, | |
1344 | (void *)PRIVATE_DATA_COUNTER_MAKE_PP_SUB_JOB(1, i), | |
1345 | &profiling_counter_src_fops); | |
6fa3eb70 S |
1346 | } |
1347 | } | |
1348 | ||
1349 | #if defined(CONFIG_MALI400_INTERNAL_PROFILING) | |
1350 | mali_profiling_proc_dir = debugfs_create_dir("proc", mali_profiling_dir); | |
1351 | if (mali_profiling_proc_dir != NULL) { | |
1352 | struct dentry *mali_profiling_proc_default_dir = debugfs_create_dir("default", mali_profiling_proc_dir); | |
1353 | if (mali_profiling_proc_default_dir != NULL) { | |
db9a41fa | 1354 | debugfs_create_file("enable", 0600, mali_profiling_proc_default_dir, (void *)_MALI_UK_USER_SETTING_SW_EVENTS_ENABLE, &user_settings_fops); |
6fa3eb70 S |
1355 | } |
1356 | } | |
1357 | debugfs_create_file("record", 0600, mali_profiling_dir, NULL, &profiling_record_fops); | |
1358 | debugfs_create_file("events", 0400, mali_profiling_dir, NULL, &profiling_events_fops); | |
1359 | debugfs_create_file("events_human_readable", 0400, mali_profiling_dir, NULL, &profiling_events_human_readable_fops); | |
1360 | #endif | |
1361 | } | |
1362 | ||
1363 | #if MALI_STATE_TRACKING | |
1364 | debugfs_create_file("state_dump", 0400, mali_debugfs_dir, NULL, &mali_seq_internal_state_fops); | |
1365 | #endif | |
1366 | ||
db9a41fa S |
1367 | #if defined(DEBUG) |
1368 | debugfs_create_file("timeline_dump", 0400, mali_debugfs_dir, NULL, &timeline_dump_fops); | |
1369 | #endif | |
6fa3eb70 S |
1370 | if (mali_sysfs_user_settings_register()) { |
1371 | /* Failed to create the debugfs entries for the user settings DB. */ | |
1372 | MALI_DEBUG_PRINT(2, ("Failed to create user setting debugfs files. Ignoring...\n")); | |
1373 | } | |
1374 | } | |
1375 | } | |
1376 | ||
1377 | /* Success! */ | |
1378 | return 0; | |
1379 | } | |
1380 | ||
1381 | int mali_sysfs_unregister(void) | |
1382 | { | |
db9a41fa | 1383 | if (NULL != mali_debugfs_dir) { |
6fa3eb70 S |
1384 | debugfs_remove_recursive(mali_debugfs_dir); |
1385 | } | |
1386 | return 0; | |
1387 | } | |
1388 | ||
1389 | #else /* MALI_LICENSE_IS_GPL */ | |
1390 | ||
1391 | /* Dummy implementations for non-GPL */ | |
1392 | ||
1393 | int mali_sysfs_register(struct mali_dev *device, dev_t dev, const char *mali_dev_name) | |
1394 | { | |
1395 | return 0; | |
1396 | } | |
1397 | ||
1398 | int mali_sysfs_unregister(void) | |
1399 | { | |
1400 | return 0; | |
1401 | } | |
1402 | ||
1403 | #endif /* MALI_LICENSE_IS_GPL */ |