diff options
Diffstat (limited to 'drivers/gpu/host1x/debug.c')
| -rw-r--r-- | drivers/gpu/host1x/debug.c | 210 | 
1 files changed, 210 insertions, 0 deletions
diff --git a/drivers/gpu/host1x/debug.c b/drivers/gpu/host1x/debug.c new file mode 100644 index 00000000000..3ec7d77de24 --- /dev/null +++ b/drivers/gpu/host1x/debug.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2010 Google, Inc. + * Author: Erik Gilling <konkers@android.com> + * + * Copyright (C) 2011-2013 NVIDIA Corporation + * + * 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/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> + +#include <linux/io.h> + +#include "dev.h" +#include "debug.h" +#include "channel.h" + +unsigned int host1x_debug_trace_cmdbuf; + +static pid_t host1x_debug_force_timeout_pid; +static u32 host1x_debug_force_timeout_val; +static u32 host1x_debug_force_timeout_channel; + +void host1x_debug_output(struct output *o, const char *fmt, ...) +{ +	va_list args; +	int len; + +	va_start(args, fmt); +	len = vsnprintf(o->buf, sizeof(o->buf), fmt, args); +	va_end(args); +	o->fn(o->ctx, o->buf, len); +} + +static int show_channels(struct host1x_channel *ch, void *data, bool show_fifo) +{ +	struct host1x *m = dev_get_drvdata(ch->dev->parent); +	struct output *o = data; + +	mutex_lock(&ch->reflock); +	if (ch->refcount) { +		mutex_lock(&ch->cdma.lock); +		if (show_fifo) +			host1x_hw_show_channel_fifo(m, ch, o); +		host1x_hw_show_channel_cdma(m, ch, o); +		mutex_unlock(&ch->cdma.lock); +	} +	mutex_unlock(&ch->reflock); + +	return 0; +} + +static void show_syncpts(struct host1x *m, struct output *o) +{ +	int i; +	host1x_debug_output(o, "---- syncpts ----\n"); +	for (i = 0; i < host1x_syncpt_nb_pts(m); i++) { +		u32 max = host1x_syncpt_read_max(m->syncpt + i); +		u32 min = host1x_syncpt_load(m->syncpt + i); +		if (!min && !max) +			continue; +		host1x_debug_output(o, "id %d (%s) min %d max %d\n", +				    i, m->syncpt[i].name, min, max); +	} + +	for (i = 0; i < host1x_syncpt_nb_bases(m); i++) { +		u32 base_val; +		base_val = host1x_syncpt_load_wait_base(m->syncpt + i); +		if (base_val) +			host1x_debug_output(o, "waitbase id %d val %d\n", i, +					    base_val); +	} + +	host1x_debug_output(o, "\n"); +} + +static void show_all(struct host1x *m, struct output *o) +{ +	struct host1x_channel *ch; + +	host1x_hw_show_mlocks(m, o); +	show_syncpts(m, o); +	host1x_debug_output(o, "---- channels ----\n"); + +	host1x_for_each_channel(m, ch) +		show_channels(ch, o, true); +} + +#ifdef CONFIG_DEBUG_FS +static void show_all_no_fifo(struct host1x *host1x, struct output *o) +{ +	struct host1x_channel *ch; + +	host1x_hw_show_mlocks(host1x, o); +	show_syncpts(host1x, o); +	host1x_debug_output(o, "---- channels ----\n"); + +	host1x_for_each_channel(host1x, ch) +		show_channels(ch, o, false); +} + +static int host1x_debug_show_all(struct seq_file *s, void *unused) +{ +	struct output o = { +		.fn = write_to_seqfile, +		.ctx = s +	}; +	show_all(s->private, &o); +	return 0; +} + +static int host1x_debug_show(struct seq_file *s, void *unused) +{ +	struct output o = { +		.fn = write_to_seqfile, +		.ctx = s +	}; +	show_all_no_fifo(s->private, &o); +	return 0; +} + +static int host1x_debug_open_all(struct inode *inode, struct file *file) +{ +	return single_open(file, host1x_debug_show_all, inode->i_private); +} + +static const struct file_operations host1x_debug_all_fops = { +	.open		= host1x_debug_open_all, +	.read		= seq_read, +	.llseek		= seq_lseek, +	.release	= single_release, +}; + +static int host1x_debug_open(struct inode *inode, struct file *file) +{ +	return single_open(file, host1x_debug_show, inode->i_private); +} + +static const struct file_operations host1x_debug_fops = { +	.open		= host1x_debug_open, +	.read		= seq_read, +	.llseek		= seq_lseek, +	.release	= single_release, +}; + +void host1x_debug_init(struct host1x *host1x) +{ +	struct dentry *de = debugfs_create_dir("tegra-host1x", NULL); + +	if (!de) +		return; + +	/* Store the created entry */ +	host1x->debugfs = de; + +	debugfs_create_file("status", S_IRUGO, de, host1x, &host1x_debug_fops); +	debugfs_create_file("status_all", S_IRUGO, de, host1x, +			    &host1x_debug_all_fops); + +	debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, de, +			   &host1x_debug_trace_cmdbuf); + +	host1x_hw_debug_init(host1x, de); + +	debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de, +			   &host1x_debug_force_timeout_pid); +	debugfs_create_u32("force_timeout_val", S_IRUGO|S_IWUSR, de, +			   &host1x_debug_force_timeout_val); +	debugfs_create_u32("force_timeout_channel", S_IRUGO|S_IWUSR, de, +			   &host1x_debug_force_timeout_channel); +} + +void host1x_debug_deinit(struct host1x *host1x) +{ +	debugfs_remove_recursive(host1x->debugfs); +} +#else +void host1x_debug_init(struct host1x *host1x) +{ +} +void host1x_debug_deinit(struct host1x *host1x) +{ +} +#endif + +void host1x_debug_dump(struct host1x *host1x) +{ +	struct output o = { +		.fn = write_to_printk +	}; +	show_all(host1x, &o); +} + +void host1x_debug_dump_syncpts(struct host1x *host1x) +{ +	struct output o = { +		.fn = write_to_printk +	}; +	show_syncpts(host1x, &o); +}  |