Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c
new file mode 100644
index 0000000..b897e10
--- /dev/null
+++ b/drivers/gpu/msm/adreno_debugfs.c
@@ -0,0 +1,451 @@
+/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/delay.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "kgsl.h"
+#include "adreno_postmortem.h"
+#include "adreno.h"
+
+#include "a200_reg.h"
+
+unsigned int kgsl_cff_dump_enable;
+int kgsl_pm_regs_enabled;
+
+static uint32_t kgsl_ib_base;
+static uint32_t kgsl_ib_size;
+
+static struct dentry *pm_d_debugfs;
+
+static int pm_dump_set(void *data, u64 val)
+{
+ struct kgsl_device *device = data;
+
+ if (val) {
+ mutex_lock(&device->mutex);
+ adreno_postmortem_dump(device, 1);
+ mutex_unlock(&device->mutex);
+ }
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(pm_dump_fops,
+ NULL,
+ pm_dump_set, "%llu\n");
+
+static int pm_regs_enabled_set(void *data, u64 val)
+{
+ kgsl_pm_regs_enabled = val ? 1 : 0;
+ return 0;
+}
+
+static int pm_regs_enabled_get(void *data, u64 *val)
+{
+ *val = kgsl_pm_regs_enabled;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(pm_regs_enabled_fops,
+ pm_regs_enabled_get,
+ pm_regs_enabled_set, "%llu\n");
+
+
+static int kgsl_cff_dump_enable_set(void *data, u64 val)
+{
+#ifdef CONFIG_MSM_KGSL_CFF_DUMP
+ kgsl_cff_dump_enable = (val != 0);
+ return 0;
+#else
+ return -EINVAL;
+#endif
+}
+
+static int kgsl_cff_dump_enable_get(void *data, u64 *val)
+{
+ *val = kgsl_cff_dump_enable;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(kgsl_cff_dump_enable_fops, kgsl_cff_dump_enable_get,
+ kgsl_cff_dump_enable_set, "%llu\n");
+
+static int kgsl_dbgfs_open(struct inode *inode, struct file *file)
+{
+ file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int kgsl_dbgfs_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int kgsl_hex_dump(const char *prefix, int c, uint8_t *data,
+ int rowc, int linec, char __user *buff)
+{
+ int ss;
+ /* Prefix of 20 chars max, 32 bytes per row, in groups of four - that's
+ * 8 groups at 8 chars per group plus a space, plus new-line, plus
+ * ending character */
+ char linebuf[20 + 64 + 1 + 1];
+
+ ss = snprintf(linebuf, sizeof(linebuf), prefix, c);
+ hex_dump_to_buffer(data, linec, rowc, 4, linebuf+ss,
+ sizeof(linebuf)-ss, 0);
+ strncat(linebuf, "\n", sizeof(linebuf));
+ linebuf[sizeof(linebuf)-1] = 0;
+ ss = strlen(linebuf);
+ if (copy_to_user(buff, linebuf, ss+1))
+ return -EFAULT;
+ return ss;
+}
+
+static ssize_t kgsl_ib_dump_read(
+ struct file *file,
+ char __user *buff,
+ size_t buff_count,
+ loff_t *ppos)
+{
+ int i, count = kgsl_ib_size, remaining, pos = 0, tot = 0, ss;
+ struct kgsl_device *device = file->private_data;
+ const int rowc = 32;
+ unsigned int pt_base, ib_memsize;
+ uint8_t *base_addr;
+ char linebuf[80];
+
+ if (!ppos || !device || !kgsl_ib_base)
+ return 0;
+
+ kgsl_regread(device, MH_MMU_PT_BASE, &pt_base);
+ base_addr = kgsl_sharedmem_convertaddr(device, pt_base, kgsl_ib_base,
+ &ib_memsize);
+
+ if (!base_addr)
+ return 0;
+
+ pr_info("%s ppos=%ld, buff_count=%d, count=%d\n", __func__, (long)*ppos,
+ buff_count, count);
+ ss = snprintf(linebuf, sizeof(linebuf), "IB: base=%08x(%08x"
+ "), size=%d, memsize=%d\n", kgsl_ib_base,
+ (uint32_t)base_addr, kgsl_ib_size, ib_memsize);
+ if (*ppos == 0) {
+ if (copy_to_user(buff, linebuf, ss+1))
+ return -EFAULT;
+ tot += ss;
+ buff += ss;
+ *ppos += ss;
+ }
+ pos += ss;
+ remaining = count;
+ for (i = 0; i < count; i += rowc) {
+ int linec = min(remaining, rowc);
+
+ remaining -= rowc;
+ ss = kgsl_hex_dump("IB: %05x: ", i, base_addr, rowc, linec,
+ buff);
+ if (ss < 0)
+ return ss;
+
+ if (pos >= *ppos) {
+ if (tot+ss >= buff_count) {
+ ss = copy_to_user(buff, "", 1);
+ return tot;
+ }
+ tot += ss;
+ buff += ss;
+ *ppos += ss;
+ }
+ pos += ss;
+ base_addr += linec;
+ }
+
+ return tot;
+}
+
+static ssize_t kgsl_ib_dump_write(
+ struct file *file,
+ const char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ char local_buff[64];
+
+ if (count >= sizeof(local_buff))
+ return -EFAULT;
+
+ if (copy_from_user(local_buff, buff, count))
+ return -EFAULT;
+
+ local_buff[count] = 0; /* end of string */
+ sscanf(local_buff, "%x %d", &kgsl_ib_base, &kgsl_ib_size);
+
+ pr_info("%s: base=%08X size=%d\n", __func__, kgsl_ib_base,
+ kgsl_ib_size);
+
+ return count;
+}
+
+static const struct file_operations kgsl_ib_dump_fops = {
+ .open = kgsl_dbgfs_open,
+ .release = kgsl_dbgfs_release,
+ .read = kgsl_ib_dump_read,
+ .write = kgsl_ib_dump_write,
+};
+
+static int kgsl_regread_nolock(struct kgsl_device *device,
+ unsigned int offsetwords, unsigned int *value)
+{
+ unsigned int *reg;
+
+ if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) {
+ KGSL_DRV_ERR(device, "invalid offset %d\n", offsetwords);
+ return -ERANGE;
+ }
+
+ reg = (unsigned int *)(device->regspace.mmio_virt_base
+ + (offsetwords << 2));
+ *value = __raw_readl(reg);
+ return 0;
+}
+
+#define KGSL_ISTORE_START 0x5000
+#define KGSL_ISTORE_LENGTH 0x600
+static ssize_t kgsl_istore_read(
+ struct file *file,
+ char __user *buff,
+ size_t buff_count,
+ loff_t *ppos)
+{
+ int i, count = KGSL_ISTORE_LENGTH, remaining, pos = 0, tot = 0;
+ struct kgsl_device *device = file->private_data;
+ const int rowc = 8;
+
+ if (!ppos || !device)
+ return 0;
+
+ remaining = count;
+ for (i = 0; i < count; i += rowc) {
+ unsigned int vals[rowc];
+ int j, ss;
+ int linec = min(remaining, rowc);
+ remaining -= rowc;
+
+ if (pos >= *ppos) {
+ for (j = 0; j < linec; ++j)
+ kgsl_regread_nolock(device,
+ KGSL_ISTORE_START+i+j, vals+j);
+ } else
+ memset(vals, 0, sizeof(vals));
+
+ ss = kgsl_hex_dump("IS: %04x: ", i, (uint8_t *)vals, rowc*4,
+ linec*4, buff);
+ if (ss < 0)
+ return ss;
+
+ if (pos >= *ppos) {
+ if (tot+ss >= buff_count)
+ return tot;
+ tot += ss;
+ buff += ss;
+ *ppos += ss;
+ }
+ pos += ss;
+ }
+
+ return tot;
+}
+
+static const struct file_operations kgsl_istore_fops = {
+ .open = kgsl_dbgfs_open,
+ .release = kgsl_dbgfs_release,
+ .read = kgsl_istore_read,
+ .llseek = default_llseek,
+};
+
+typedef void (*reg_read_init_t)(struct kgsl_device *device);
+typedef void (*reg_read_fill_t)(struct kgsl_device *device, int i,
+ unsigned int *vals, int linec);
+static ssize_t kgsl_reg_read(struct kgsl_device *device, int count,
+ reg_read_init_t reg_read_init,
+ reg_read_fill_t reg_read_fill, const char *prefix, char __user *buff,
+ loff_t *ppos)
+{
+ int i, remaining;
+ const int rowc = 8;
+
+ if (!ppos || *ppos || !device)
+ return 0;
+
+ mutex_lock(&device->mutex);
+ reg_read_init(device);
+ remaining = count;
+ for (i = 0; i < count; i += rowc) {
+ unsigned int vals[rowc];
+ int ss;
+ int linec = min(remaining, rowc);
+ remaining -= rowc;
+
+ reg_read_fill(device, i, vals, linec);
+ ss = kgsl_hex_dump(prefix, i, (uint8_t *)vals, rowc*4, linec*4,
+ buff);
+ if (ss < 0) {
+ mutex_unlock(&device->mutex);
+ return ss;
+ }
+ buff += ss;
+ *ppos += ss;
+ }
+ mutex_unlock(&device->mutex);
+
+ return *ppos;
+}
+
+
+static void kgsl_sx_reg_read_init(struct kgsl_device *device)
+{
+ kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xFF);
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+}
+
+static void kgsl_sx_reg_read_fill(struct kgsl_device *device, int i,
+ unsigned int *vals, int linec)
+{
+ int j;
+
+ for (j = 0; j < linec; ++j) {
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1B00 | i);
+ kgsl_regread(device, REG_RBBM_DEBUG_OUT, vals+j);
+ }
+}
+
+static ssize_t kgsl_sx_debug_read(
+ struct file *file,
+ char __user *buff,
+ size_t buff_count,
+ loff_t *ppos)
+{
+ struct kgsl_device *device = file->private_data;
+ return kgsl_reg_read(device, 0x1B, kgsl_sx_reg_read_init,
+ kgsl_sx_reg_read_fill, "SX: %02x: ", buff, ppos);
+}
+
+static const struct file_operations kgsl_sx_debug_fops = {
+ .open = kgsl_dbgfs_open,
+ .release = kgsl_dbgfs_release,
+ .read = kgsl_sx_debug_read,
+};
+
+static void kgsl_cp_reg_read_init(struct kgsl_device *device)
+{
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+}
+
+static void kgsl_cp_reg_read_fill(struct kgsl_device *device, int i,
+ unsigned int *vals, int linec)
+{
+ int j;
+
+ for (j = 0; j < linec; ++j) {
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1628);
+ kgsl_regread(device, REG_RBBM_DEBUG_OUT, vals+j);
+ msleep(100);
+ }
+}
+
+static ssize_t kgsl_cp_debug_read(
+ struct file *file,
+ char __user *buff,
+ size_t buff_count,
+ loff_t *ppos)
+{
+ struct kgsl_device *device = file->private_data;
+ return kgsl_reg_read(device, 20, kgsl_cp_reg_read_init,
+ kgsl_cp_reg_read_fill,
+ "CP: %02x: ", buff, ppos);
+}
+
+static const struct file_operations kgsl_cp_debug_fops = {
+ .open = kgsl_dbgfs_open,
+ .release = kgsl_dbgfs_release,
+ .read = kgsl_cp_debug_read,
+};
+
+static void kgsl_mh_reg_read_init(struct kgsl_device *device)
+{
+ kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+}
+
+static void kgsl_mh_reg_read_fill(struct kgsl_device *device, int i,
+ unsigned int *vals, int linec)
+{
+ int j;
+
+ for (j = 0; j < linec; ++j) {
+ kgsl_regwrite(device, REG_MH_DEBUG_CTRL, i+j);
+ kgsl_regread(device, REG_MH_DEBUG_DATA, vals+j);
+ }
+}
+
+static ssize_t kgsl_mh_debug_read(
+ struct file *file,
+ char __user *buff,
+ size_t buff_count,
+ loff_t *ppos)
+{
+ struct kgsl_device *device = file->private_data;
+ return kgsl_reg_read(device, 0x40, kgsl_mh_reg_read_init,
+ kgsl_mh_reg_read_fill,
+ "MH: %02x: ", buff, ppos);
+}
+
+static const struct file_operations kgsl_mh_debug_fops = {
+ .open = kgsl_dbgfs_open,
+ .release = kgsl_dbgfs_release,
+ .read = kgsl_mh_debug_read,
+};
+
+void adreno_debugfs_init(struct kgsl_device *device)
+{
+ if (!device->d_debugfs || IS_ERR(device->d_debugfs))
+ return;
+
+ debugfs_create_file("ib_dump", 0600, device->d_debugfs, device,
+ &kgsl_ib_dump_fops);
+ debugfs_create_file("istore", 0400, device->d_debugfs, device,
+ &kgsl_istore_fops);
+ debugfs_create_file("sx_debug", 0400, device->d_debugfs, device,
+ &kgsl_sx_debug_fops);
+ debugfs_create_file("cp_debug", 0400, device->d_debugfs, device,
+ &kgsl_cp_debug_fops);
+ debugfs_create_file("mh_debug", 0400, device->d_debugfs, device,
+ &kgsl_mh_debug_fops);
+ debugfs_create_file("cff_dump", 0644, device->d_debugfs, device,
+ &kgsl_cff_dump_enable_fops);
+
+ /* Create post mortem control files */
+
+ pm_d_debugfs = debugfs_create_dir("postmortem", device->d_debugfs);
+
+ if (IS_ERR(pm_d_debugfs))
+ return;
+
+ debugfs_create_file("dump", 0600, pm_d_debugfs, device,
+ &pm_dump_fops);
+ debugfs_create_file("regs_enabled", 0644, pm_d_debugfs, device,
+ &pm_regs_enabled_fops);
+}