gpu: pvr: debugfs: add hwrec context dump
[sgx.git] / pvr / pvr_debugfs.c
1 /*
2  * Copyright (c) 2010-2011 Imre Deak <imre.deak@nokia.com>
3  * Copyright (c) 2010-2011 Luc Verhaegen <libv@codethink.co.uk>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19
20 /*
21  *
22  * Debugfs interface living in pvr/ subdirectory.
23  *
24  */
25 #include <linux/kernel.h>
26 #include <linux/debugfs.h>
27 #include <linux/vmalloc.h>
28 #include <linux/mutex.h>
29 #include <linux/uaccess.h>
30 #include <linux/io.h>
31
32 #include "img_types.h"
33 #include "servicesext.h"
34 #include "services.h"
35 #include "sgxinfokm.h"
36 #include "syscommon.h"
37 #include "pvr_bridge_km.h"
38 #include "sgxutils.h"
39 #include "pvr_debugfs.h"
40 #include "mmu.h"
41
42 static struct dentry *debugfs_dir;
43 static u32 pvr_reset;
44
45 /*
46  *
47  */
48 static struct PVRSRV_DEVICE_NODE *get_sgx_node(void)
49 {
50         struct SYS_DATA *sysdata;
51         struct PVRSRV_DEVICE_NODE *node;
52
53         if (SysAcquireData(&sysdata) != PVRSRV_OK)
54                 return NULL;
55
56         for (node = sysdata->psDeviceNodeList; node; node = node->psNext)
57                 if (node->sDevId.eDeviceType == PVRSRV_DEVICE_TYPE_SGX)
58                         break;
59
60         return node;
61 }
62
63 static int pvr_debugfs_reset(void *data, u64 val)
64 {
65         struct PVRSRV_DEVICE_NODE *node;
66         enum PVRSRV_ERROR err;
67         int r = 0;
68
69         if (val != 1)
70                 return 0;
71
72         pvr_lock();
73
74         if (pvr_is_disabled()) {
75                 r = -ENODEV;
76                 goto exit;
77         }
78
79         node = get_sgx_node();
80         if (!node) {
81                 r =  -ENODEV;
82                 goto exit;
83         }
84
85         err = PVRSRVSetDevicePowerStateKM(node->sDevId.ui32DeviceIndex,
86                                           PVRSRV_POWER_STATE_D0);
87         if (err != PVRSRV_OK) {
88                 r = -EIO;
89                 goto exit;
90         }
91
92         HWRecoveryResetSGX(node);
93
94         SGXTestActivePowerEvent(node);
95 exit:
96         pvr_unlock();
97
98         return r;
99 }
100
101 static int pvr_debugfs_set(void *data, u64 val)
102 {
103         u32 *var = data;
104
105         if (var == &pvr_reset)
106                 return pvr_debugfs_reset(data, val);
107
108         BUG();
109 }
110
111 DEFINE_SIMPLE_ATTRIBUTE(pvr_debugfs_fops, NULL, pvr_debugfs_set, "%llu\n");
112
113 /*
114  *
115  */
116 struct edm_buf_info {
117         size_t len;
118         char data[1];
119 };
120
121 static int pvr_debugfs_edm_open(struct inode *inode, struct file *file)
122 {
123         struct PVRSRV_DEVICE_NODE *node;
124         struct PVRSRV_SGXDEV_INFO *sgx_info;
125         struct edm_buf_info *bi;
126         size_t size;
127
128         /* Take a snapshot of the EDM trace buffer */
129         size = SGXMK_TRACE_BUFFER_SIZE * SGXMK_TRACE_BUF_STR_LEN;
130         bi = vmalloc(sizeof(*bi) + size);
131         if (!bi)
132                 return -ENOMEM;
133
134         node = get_sgx_node();
135         sgx_info = node->pvDevice;
136         bi->len = snprint_edm_trace(sgx_info, bi->data, size);
137         file->private_data = bi;
138
139         return 0;
140 }
141
142 static int pvr_debugfs_edm_release(struct inode *inode, struct file *file)
143 {
144         vfree(file->private_data);
145
146         return 0;
147 }
148
149 static ssize_t pvr_debugfs_edm_read(struct file *file, char __user *buffer,
150                                 size_t count, loff_t *ppos)
151 {
152         struct edm_buf_info *bi = file->private_data;
153
154         return simple_read_from_buffer(buffer, count, ppos, bi->data, bi->len);
155 }
156
157 static const struct file_operations pvr_debugfs_edm_fops = {
158         .owner          = THIS_MODULE,
159         .open           = pvr_debugfs_edm_open,
160         .read           = pvr_debugfs_edm_read,
161         .release        = pvr_debugfs_edm_release,
162 };
163
164
165 /*
166  *
167  * HW Recovery dumping support.
168  *
169  */
170 static struct mutex hwrec_mutex[1];
171 static struct timeval hwrec_time;
172 static int hwrec_open_count;
173 static DECLARE_WAIT_QUEUE_HEAD(hwrec_wait_queue);
174 static int hwrec_event;
175
176 /* add extra locking to keep us from overwriting things during dumping. */
177 static int hwrec_event_open_count;
178 static int hwrec_event_file_lock;
179
180 /* While these could get moved into PVRSRV_SGXDEV_INFO, the more future-proof
181  * way of handling hw recovery events is by providing 1 single hwrecovery dump
182  * at a time, and adding a hwrec_info debugfs file with: process information,
183  * general driver information, and the instance of the (then multicore) pvr
184  * where the hwrec event happened.
185  */
186 static u32 *hwrec_registers;
187
188 #ifdef CONFIG_PVR_DEBUG
189 static size_t hwrec_mem_size;
190 #define HWREC_MEM_PAGES (4 * PAGE_SIZE)
191 static unsigned long hwrec_mem_pages[HWREC_MEM_PAGES];
192 #endif /* CONFIG_PVR_DEBUG */
193
194 static void
195 hwrec_registers_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
196 {
197         int i;
198
199         if (!hwrec_registers) {
200                 hwrec_registers = (u32 *) __get_free_page(GFP_KERNEL);
201                 if (!hwrec_registers) {
202                         pr_err("%s: failed to get free page.\n", __func__);
203                         return;
204                 }
205         }
206
207         for (i = 0; i < 1024; i++)
208                 hwrec_registers[i] = readl(psDevInfo->pvRegsBaseKM + 4 * i);
209 }
210
211 static void
212 hwrec_pages_free(size_t *size, u32 *pages)
213 {
214         int i;
215
216         if (!(*size))
217                 return;
218
219         for (i = 0; (i * PAGE_SIZE) < *size; i++) {
220                 free_page(pages[i]);
221                 pages[i] = 0;
222         }
223
224         *size = 0;
225 }
226
227 static int
228 hwrec_pages_write(u8 *buffer, size_t size, size_t *current_size, u32 *pages,
229                   int array_size)
230 {
231         size_t ret = 0;
232
233         while (size) {
234                 size_t count = size;
235                 size_t offset = *current_size & ~PAGE_MASK;
236                 int page = *current_size / PAGE_SIZE;
237
238                 if (!offset) {
239                         if (((*current_size) / PAGE_SIZE) >= array_size) {
240                                 pr_err("%s: Size overrun!\n", __func__);
241                                 return -ENOMEM;
242                         }
243
244                         pages[page] = __get_free_page(GFP_KERNEL);
245                         if (!pages[page]) {
246                                 pr_err("%s: failed to get free page.\n",
247                                        __func__);
248                                 return -ENOMEM;
249                         }
250                 }
251
252                 if (count > (PAGE_SIZE - offset))
253                         count = PAGE_SIZE - offset;
254
255                 memcpy(((u8 *) pages[page]) + offset, buffer, count);
256
257                 buffer += count;
258                 size -= count;
259                 ret += count;
260                 *current_size += count;
261         }
262
263         return ret;
264 }
265
266 #ifdef CONFIG_PVR_DEBUG
267 static void
268 hwrec_mem_free(void)
269 {
270         hwrec_pages_free(&hwrec_mem_size, hwrec_mem_pages);
271 }
272
273 int
274 hwrec_mem_write(u8 *buffer, size_t size)
275 {
276         return hwrec_pages_write(buffer, size, &hwrec_mem_size,
277                                  hwrec_mem_pages, ARRAY_SIZE(hwrec_mem_pages));
278 }
279
280 int
281 hwrec_mem_print(char *format, ...)
282 {
283         char tmp[25];
284         va_list ap;
285         size_t size;
286
287         va_start(ap, format);
288         size = vscnprintf(tmp, sizeof(tmp), format, ap);
289         va_end(ap);
290
291         return hwrec_mem_write(tmp, size);
292 }
293 #endif /* CONFIG_PVR_DEBUG */
294
295 void
296 pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
297 {
298         mutex_lock(hwrec_mutex);
299
300         if (hwrec_open_count || hwrec_event_file_lock) {
301                 pr_err("%s: previous hwrec dump is still locked!\n", __func__);
302                 mutex_unlock(hwrec_mutex);
303                 return;
304         }
305
306         do_gettimeofday(&hwrec_time);
307         pr_info("HW Recovery dump generated at %010ld%06ld\n",
308                 hwrec_time.tv_sec, hwrec_time.tv_usec);
309
310         hwrec_registers_dump(psDevInfo);
311
312 #ifdef CONFIG_PVR_DEBUG
313         hwrec_mem_free();
314         mmu_hwrec_mem_dump(psDevInfo);
315 #endif /* CONFIG_PVR_DEBUG */
316
317         hwrec_event = 1;
318
319         mutex_unlock(hwrec_mutex);
320
321         wake_up_interruptible(&hwrec_wait_queue);
322 }
323
324 /*
325  * helpers.
326  */
327 static int
328 hwrec_file_open(struct inode *inode, struct file *filp)
329 {
330         mutex_lock(hwrec_mutex);
331
332         hwrec_open_count++;
333
334         mutex_unlock(hwrec_mutex);
335         return 0;
336 }
337
338 static int
339 hwrec_file_release(struct inode *inode, struct file *filp)
340 {
341         mutex_lock(hwrec_mutex);
342
343         hwrec_open_count--;
344
345         mutex_unlock(hwrec_mutex);
346         return 0;
347 }
348
349 static loff_t
350 hwrec_llseek_helper(struct file *filp, loff_t offset, int whence, loff_t max)
351 {
352         loff_t f_pos;
353
354         switch (whence) {
355         case SEEK_SET:
356                 if ((offset > max) || (offset < 0))
357                         f_pos = -EINVAL;
358                 else
359                         f_pos = offset;
360                 break;
361         case SEEK_CUR:
362                 if (((filp->f_pos + offset) > max) ||
363                     ((filp->f_pos + offset) < 0))
364                         f_pos = -EINVAL;
365                 else
366                         f_pos = filp->f_pos + offset;
367                 break;
368         case SEEK_END:
369                 if ((offset > 0) ||
370                     (offset < -max))
371                         f_pos = -EINVAL;
372                 else
373                         f_pos = max + offset;
374                 break;
375         default:
376                 f_pos = -EINVAL;
377                 break;
378         }
379
380         if (f_pos >= 0)
381                 filp->f_pos = f_pos;
382
383         return f_pos;
384 }
385
386 /*
387  * Provides a hwrec timestamp for unique dumping.
388  */
389 static ssize_t
390 hwrec_time_read(struct file *filp, char __user *buf, size_t size,
391                 loff_t *f_pos)
392 {
393         char tmp[20];
394
395         mutex_lock(hwrec_mutex);
396         snprintf(tmp, sizeof(tmp), "%010ld%06ld",
397                  hwrec_time.tv_sec, hwrec_time.tv_usec);
398         mutex_unlock(hwrec_mutex);
399
400         return simple_read_from_buffer(buf, size, f_pos, tmp, strlen(tmp));
401 }
402
403 static const struct file_operations hwrec_time_fops = {
404         .owner = THIS_MODULE,
405         .llseek = no_llseek,
406         .read = hwrec_time_read,
407         .open = hwrec_file_open,
408         .release = hwrec_file_release,
409 };
410
411 /*
412  * Blocks the reader until a HWRec happens.
413  */
414 static int
415 hwrec_event_open(struct inode *inode, struct file *filp)
416 {
417         int ret;
418
419         mutex_lock(hwrec_mutex);
420
421         if (hwrec_event_open_count)
422                 ret = -EUSERS;
423         else {
424                 hwrec_event_open_count++;
425                 ret = 0;
426         }
427
428         mutex_unlock(hwrec_mutex);
429
430         return ret;
431 }
432
433 static int
434 hwrec_event_release(struct inode *inode, struct file *filp)
435 {
436         mutex_lock(hwrec_mutex);
437
438         hwrec_event_open_count--;
439
440         mutex_unlock(hwrec_mutex);
441
442         return 0;
443 }
444
445
446 static ssize_t
447 hwrec_event_read(struct file *filp, char __user *buf, size_t size,
448                  loff_t *f_pos)
449 {
450         int ret = 0;
451
452         mutex_lock(hwrec_mutex);
453
454         hwrec_event_file_lock = 0;
455
456         mutex_unlock(hwrec_mutex);
457
458         ret = wait_event_interruptible(hwrec_wait_queue, hwrec_event);
459         if (!ret) {
460                 mutex_lock(hwrec_mutex);
461
462                 hwrec_event = 0;
463                 hwrec_event_file_lock = 1;
464
465                 mutex_unlock(hwrec_mutex);
466         }
467
468         return ret;
469 }
470
471 static const struct file_operations hwrec_event_fops = {
472         .owner = THIS_MODULE,
473         .llseek = no_llseek,
474         .read = hwrec_event_read,
475         .open = hwrec_event_open,
476         .release = hwrec_event_release,
477 };
478
479 /*
480  * Reads out all readable registers.
481  */
482 #define HWREC_REGS_LINE_SIZE 17
483
484 static loff_t
485 hwrec_regs_llseek(struct file *filp, loff_t offset, int whence)
486 {
487         loff_t f_pos;
488
489         mutex_lock(hwrec_mutex);
490
491         if (hwrec_registers)
492                 f_pos = hwrec_llseek_helper(filp, offset, whence,
493                                             1024 * HWREC_REGS_LINE_SIZE);
494         else
495                 f_pos = 0;
496
497         mutex_unlock(hwrec_mutex);
498
499         return f_pos;
500 }
501
502 static ssize_t
503 hwrec_regs_read(struct file *filp, char __user *buf, size_t size,
504                 loff_t *f_pos)
505 {
506         char tmp[HWREC_REGS_LINE_SIZE + 1];
507         int i;
508
509         if ((*f_pos < 0) || (size < (sizeof(tmp) - 1)))
510                 return 0;
511
512         i = ((int) *f_pos) / (sizeof(tmp) - 1);
513         if (i >= 1024)
514                 return 0;
515
516         mutex_lock(hwrec_mutex);
517
518         if (!hwrec_registers)
519                 size = 0;
520         else
521                 size = snprintf(tmp, sizeof(tmp), "0x%03X 0x%08X\n", i * 4,
522                                 hwrec_registers[i]);
523
524         mutex_unlock(hwrec_mutex);
525
526         if (size > 0) {
527                 if (copy_to_user(buf, tmp + *f_pos - (i * (sizeof(tmp) - 1)),
528                                  size))
529                         return -EFAULT;
530
531                 *f_pos += size;
532                 return size;
533         } else
534                 return 0;
535 }
536
537 static const struct file_operations hwrec_regs_fops = {
538         .owner = THIS_MODULE,
539         .llseek = hwrec_regs_llseek,
540         .read = hwrec_regs_read,
541         .open = hwrec_file_open,
542         .release = hwrec_file_release,
543 };
544
545 #ifdef CONFIG_PVR_DEBUG
546 /*
547  * Provides a full context dump: page directory, page tables, and all mapped
548  * pages.
549  */
550 static loff_t
551 hwrec_mem_llseek(struct file *filp, loff_t offset, int whence)
552 {
553         loff_t f_pos;
554
555         mutex_lock(hwrec_mutex);
556
557         if (hwrec_mem_size)
558                 f_pos = hwrec_llseek_helper(filp, offset, whence,
559                                             hwrec_mem_size);
560         else
561                 f_pos = 0;
562
563         mutex_unlock(hwrec_mutex);
564
565         return f_pos;
566 }
567
568 static ssize_t
569 hwrec_mem_read(struct file *filp, char __user *buf, size_t size,
570                loff_t *f_pos)
571 {
572         mutex_lock(hwrec_mutex);
573
574         if ((*f_pos >= 0) && (*f_pos < hwrec_mem_size)) {
575                 int page, offset;
576
577                 size = min(size, (size_t) hwrec_mem_size - (size_t) *f_pos);
578
579                 page = (*f_pos) / PAGE_SIZE;
580                 offset = (*f_pos) & ~PAGE_MASK;
581
582                 size = min(size, (size_t) PAGE_SIZE - offset);
583
584                 if (copy_to_user(buf,
585                                  ((u8 *) hwrec_mem_pages[page]) + offset,
586                                  size)) {
587                         mutex_unlock(hwrec_mutex);
588                         return -EFAULT;
589                 }
590         } else
591                 size = 0;
592
593         mutex_unlock(hwrec_mutex);
594
595         *f_pos += size;
596         return size;
597 }
598
599 static const struct file_operations hwrec_mem_fops = {
600         .owner = THIS_MODULE,
601         .llseek = hwrec_mem_llseek,
602         .read = hwrec_mem_read,
603         .open = hwrec_file_open,
604         .release = hwrec_file_release,
605 };
606 #endif /* CONFIG_PVR_DEBUG */
607
608 /*
609  *
610  */
611 int pvr_debugfs_init(void)
612 {
613         mutex_init(hwrec_mutex);
614
615         debugfs_dir = debugfs_create_dir("pvr", NULL);
616         if (!debugfs_dir)
617                 return -ENODEV;
618
619         if (!debugfs_create_file("reset_sgx", S_IWUSR, debugfs_dir, &pvr_reset,
620                                  &pvr_debugfs_fops)) {
621                 debugfs_remove(debugfs_dir);
622                 return -ENODEV;
623         }
624
625         if (!debugfs_create_file("edm_trace", S_IRUGO, debugfs_dir, NULL,
626                                  &pvr_debugfs_edm_fops)) {
627                 debugfs_remove_recursive(debugfs_dir);
628                 return -ENODEV;
629         }
630
631         if (!debugfs_create_file("hwrec_event", S_IRUSR, debugfs_dir, NULL,
632                                  &hwrec_event_fops)) {
633                 debugfs_remove_recursive(debugfs_dir);
634                 return -ENODEV;
635         }
636
637         if (!debugfs_create_file("hwrec_time", S_IRUSR, debugfs_dir, NULL,
638                                  &hwrec_time_fops)) {
639                 debugfs_remove_recursive(debugfs_dir);
640                 return -ENODEV;
641         }
642
643         if (!debugfs_create_file("hwrec_regs", S_IRUSR, debugfs_dir, NULL,
644                                  &hwrec_regs_fops)) {
645                 debugfs_remove_recursive(debugfs_dir);
646                 return -ENODEV;
647         }
648
649 #ifdef CONFIG_PVR_DEBUG
650         if (!debugfs_create_file("hwrec_mem", S_IRUSR, debugfs_dir, NULL,
651                                  &hwrec_mem_fops)) {
652                 debugfs_remove_recursive(debugfs_dir);
653                 return -ENODEV;
654         }
655 #endif /* CONFIG_PVR_DEBUG */
656
657         return 0;
658 }
659
660 void pvr_debugfs_cleanup(void)
661 {
662         debugfs_remove_recursive(debugfs_dir);
663
664         if (hwrec_registers)
665                 free_page((u32) hwrec_registers);
666
667 #ifdef CONFIG_PVR_DEBUG
668         hwrec_mem_free();
669 #endif /* CONFIG_PVR_DEBUG */
670
671 }