gpu: pvr: debugfs: add hwrec register dump
authorLuc Verhaegen <libv@codethink.co.uk>
Tue, 22 Mar 2011 11:38:35 +0000 (12:38 +0100)
committerGrazvydas Ignotas <notasas@gmail.com>
Sun, 20 May 2012 18:43:03 +0000 (21:43 +0300)
Full register dump available after hwrec event in the file hwrec_regs.

The full register page is read out, except [0xA08,0xA50] as this range
results in a SIGBUS.

Signed-off-by: Luc Verhaegen <libv@codethink.co.uk>
Signed-off-by: Imre Deak <imre.deak@nokia.com>
pvr/pvr_debugfs.c
pvr/pvr_debugfs.h
pvr/sgxinit.c

index d549133..54f899e 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/vmalloc.h>
 #include <linux/mutex.h>
 #include <linux/uaccess.h>
+#include <linux/io.h>
 
 #include "img_types.h"
 #include "servicesext.h"
@@ -175,8 +176,33 @@ static int hwrec_event;
 static int hwrec_event_open_count;
 static int hwrec_event_file_lock;
 
+/* While these could get moved into PVRSRV_SGXDEV_INFO, the more future-proof
+ * way of handling hw recovery events is by providing 1 single hwrecovery dump
+ * at a time, and adding a hwrec_info debugfs file with: process information,
+ * general driver information, and the instance of the (then multicore) pvr
+ * where the hwrec event happened.
+ */
+static u32 *hwrec_registers;
+
+static void
+hwrec_registers_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
+{
+       int i;
+
+       if (!hwrec_registers) {
+               hwrec_registers = (u32 *) __get_free_page(GFP_KERNEL);
+               if (!hwrec_registers) {
+                       pr_err("%s: failed to get free page.\n", __func__);
+                       return;
+               }
+       }
+
+       for (i = 0; i < 1024; i++)
+               hwrec_registers[i] = readl(psDevInfo->pvRegsBaseKM + 4 * i);
+}
+
 void
-pvr_hwrec_dump(void)
+pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
 {
        mutex_lock(hwrec_mutex);
 
@@ -190,6 +216,8 @@ pvr_hwrec_dump(void)
        pr_info("HW Recovery dump generated at %010ld%06ld\n",
                hwrec_time.tv_sec, hwrec_time.tv_usec);
 
+       hwrec_registers_dump(psDevInfo);
+
        hwrec_event = 1;
 
        mutex_unlock(hwrec_mutex);
@@ -222,6 +250,43 @@ hwrec_file_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
+static loff_t
+hwrec_llseek_helper(struct file *filp, loff_t offset, int whence, loff_t max)
+{
+       loff_t f_pos;
+
+       switch (whence) {
+       case SEEK_SET:
+               if ((offset > max) || (offset < 0))
+                       f_pos = -EINVAL;
+               else
+                       f_pos = offset;
+               break;
+       case SEEK_CUR:
+               if (((filp->f_pos + offset) > max) ||
+                   ((filp->f_pos + offset) < 0))
+                       f_pos = -EINVAL;
+               else
+                       f_pos = filp->f_pos + offset;
+               break;
+       case SEEK_END:
+               if ((offset > 0) ||
+                   (offset < -max))
+                       f_pos = -EINVAL;
+               else
+                       f_pos = max + offset;
+               break;
+       default:
+               f_pos = -EINVAL;
+               break;
+       }
+
+       if (f_pos >= 0)
+               filp->f_pos = f_pos;
+
+       return f_pos;
+}
+
 /*
  * Provides a hwrec timestamp for unique dumping.
  */
@@ -315,6 +380,72 @@ static const struct file_operations hwrec_event_fops = {
        .release = hwrec_event_release,
 };
 
+/*
+ * Reads out all readable registers.
+ */
+#define HWREC_REGS_LINE_SIZE 17
+
+static loff_t
+hwrec_regs_llseek(struct file *filp, loff_t offset, int whence)
+{
+       loff_t f_pos;
+
+       mutex_lock(hwrec_mutex);
+
+       if (hwrec_registers)
+               f_pos = hwrec_llseek_helper(filp, offset, whence,
+                                           1024 * HWREC_REGS_LINE_SIZE);
+       else
+               f_pos = 0;
+
+       mutex_unlock(hwrec_mutex);
+
+       return f_pos;
+}
+
+static ssize_t
+hwrec_regs_read(struct file *filp, char __user *buf, size_t size,
+               loff_t *f_pos)
+{
+       char tmp[HWREC_REGS_LINE_SIZE + 1];
+       int i;
+
+       if ((*f_pos < 0) || (size < (sizeof(tmp) - 1)))
+               return 0;
+
+       i = ((int) *f_pos) / (sizeof(tmp) - 1);
+       if (i >= 1024)
+               return 0;
+
+       mutex_lock(hwrec_mutex);
+
+       if (!hwrec_registers)
+               size = 0;
+       else
+               size = snprintf(tmp, sizeof(tmp), "0x%03X 0x%08X\n", i * 4,
+                               hwrec_registers[i]);
+
+       mutex_unlock(hwrec_mutex);
+
+       if (size > 0) {
+               if (copy_to_user(buf, tmp + *f_pos - (i * (sizeof(tmp) - 1)),
+                                size))
+                       return -EFAULT;
+
+               *f_pos += size;
+               return size;
+       } else
+               return 0;
+}
+
+static const struct file_operations hwrec_regs_fops = {
+       .owner = THIS_MODULE,
+       .llseek = hwrec_regs_llseek,
+       .read = hwrec_regs_read,
+       .open = hwrec_file_open,
+       .release = hwrec_file_release,
+};
+
 /*
  *
  */
@@ -350,10 +481,19 @@ int pvr_debugfs_init(void)
                return -ENODEV;
        }
 
+       if (!debugfs_create_file("hwrec_regs", S_IRUSR, debugfs_dir, NULL,
+                                &hwrec_regs_fops)) {
+               debugfs_remove_recursive(debugfs_dir);
+               return -ENODEV;
+       }
+
        return 0;
 }
 
 void pvr_debugfs_cleanup(void)
 {
        debugfs_remove_recursive(debugfs_dir);
+
+       if (hwrec_registers)
+               free_page((u32) hwrec_registers);
 }
index 6ec216e..327ef0e 100644 (file)
@@ -27,6 +27,6 @@
 int pvr_debugfs_init(void);
 void pvr_debugfs_cleanup(void);
 
-void pvr_hwrec_dump(void);
+void pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo);
 
 #endif /* _PVR_DEBUGFS_H_ */
index 8518d22..fcf4321 100644 (file)
@@ -992,7 +992,7 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
        dump_sgx_state_bufs(psDeviceNode);
 
 #ifdef CONFIG_DEBUG_FS
-       pvr_hwrec_dump();
+       pvr_hwrec_dump(psDevInfo);
 #endif
 
        PDUMPSUSPEND();