Skip to content

Commit

Permalink
gpu: host1x: Add debug support
Browse files Browse the repository at this point in the history
Add support for host1x debugging. Adds debugfs entries, and dumps
channel state to UART in case of stuck job.

Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Erik Faye-Lund <kusmabite@gmail.com>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
  • Loading branch information
Terje Bergstrom authored and Thierry Reding committed Apr 22, 2013
1 parent 6579324 commit 6236451
Show file tree
Hide file tree
Showing 15 changed files with 807 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/host1x/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ host1x-y = \
cdma.o \
channel.o \
job.o \
debug.o \
hw/host1x01.o

obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
4 changes: 4 additions & 0 deletions drivers/gpu/host1x/cdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,10 @@ void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2)
struct push_buffer *pb = &cdma->push_buffer;
u32 slots_free = cdma->slots_free;

if (host1x_debug_trace_cmdbuf)
trace_host1x_cdma_push(dev_name(cdma_to_channel(cdma)->dev),
op1, op2);

if (slots_free == 0) {
host1x_hw_cdma_flush(host1x, cdma);
slots_free = host1x_cdma_wait_locked(cdma,
Expand Down
210 changes: 210 additions & 0 deletions drivers/gpu/host1x/debug.c
Original file line number Diff line number Diff line change
@@ -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);
}
51 changes: 51 additions & 0 deletions drivers/gpu/host1x/debug.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Tegra host1x Debug
*
* Copyright (c) 2011-2013 NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __HOST1X_DEBUG_H
#define __HOST1X_DEBUG_H

#include <linux/debugfs.h>
#include <linux/seq_file.h>

struct host1x;

struct output {
void (*fn)(void *ctx, const char *str, size_t len);
void *ctx;
char buf[256];
};

static inline void write_to_seqfile(void *ctx, const char *str, size_t len)
{
seq_write((struct seq_file *)ctx, str, len);
}

static inline void write_to_printk(void *ctx, const char *str, size_t len)
{
pr_info("%s", str);
}

void __printf(2, 3) host1x_debug_output(struct output *o, const char *fmt, ...);

extern unsigned int host1x_debug_trace_cmdbuf;

void host1x_debug_init(struct host1x *host1x);
void host1x_debug_deinit(struct host1x *host1x);
void host1x_debug_dump(struct host1x *host1x);
void host1x_debug_dump_syncpts(struct host1x *host1x);

#endif
3 changes: 3 additions & 0 deletions drivers/gpu/host1x/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "dev.h"
#include "intr.h"
#include "channel.h"
#include "debug.h"
#include "hw/host1x01.h"

void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
Expand Down Expand Up @@ -147,6 +148,8 @@ static int host1x_probe(struct platform_device *pdev)
goto fail_deinit_syncpt;
}

host1x_debug_init(host);

return 0;

fail_deinit_syncpt:
Expand Down
42 changes: 42 additions & 0 deletions drivers/gpu/host1x/dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ struct host1x_channel;
struct host1x_cdma;
struct host1x_job;
struct push_buffer;
struct output;
struct dentry;

struct host1x_channel_ops {
int (*init)(struct host1x_channel *channel, struct host1x *host,
Expand All @@ -54,6 +56,18 @@ struct host1x_pushbuffer_ops {
void (*init)(struct push_buffer *pb);
};

struct host1x_debug_ops {
void (*debug_init)(struct dentry *de);
void (*show_channel_cdma)(struct host1x *host,
struct host1x_channel *ch,
struct output *o);
void (*show_channel_fifo)(struct host1x *host,
struct host1x_channel *ch,
struct output *o);
void (*show_mlocks)(struct host1x *host, struct output *output);

};

struct host1x_syncpt_ops {
void (*restore)(struct host1x_syncpt *syncpt);
void (*restore_wait_base)(struct host1x_syncpt *syncpt);
Expand Down Expand Up @@ -100,13 +114,16 @@ struct host1x {
const struct host1x_channel_ops *channel_op;
const struct host1x_cdma_ops *cdma_op;
const struct host1x_pushbuffer_ops *cdma_pb_op;
const struct host1x_debug_ops *debug_op;

struct host1x_syncpt *nop_sp;

struct mutex chlist_mutex;
struct host1x_channel chlist;
unsigned long allocated_channels;
unsigned int num_allocated_channels;

struct dentry *debugfs;
};

void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v);
Expand Down Expand Up @@ -257,4 +274,29 @@ static inline void host1x_hw_pushbuffer_init(struct host1x *host,
host->cdma_pb_op->init(pb);
}

static inline void host1x_hw_debug_init(struct host1x *host, struct dentry *de)
{
if (host->debug_op && host->debug_op->debug_init)
host->debug_op->debug_init(de);
}

static inline void host1x_hw_show_channel_cdma(struct host1x *host,
struct host1x_channel *channel,
struct output *o)
{
host->debug_op->show_channel_cdma(host, channel, o);
}

static inline void host1x_hw_show_channel_fifo(struct host1x *host,
struct host1x_channel *channel,
struct output *o)
{
host->debug_op->show_channel_fifo(host, channel, o);
}

static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o)
{
host->debug_op->show_mlocks(host, o);
}

#endif
2 changes: 2 additions & 0 deletions drivers/gpu/host1x/hw/cdma_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ static void cdma_timeout_handler(struct work_struct *work)
host1x = cdma_to_host1x(cdma);
ch = cdma_to_channel(cdma);

host1x_debug_dump(cdma_to_host1x(cdma));

mutex_lock(&cdma->lock);

if (!cdma->timeout.client) {
Expand Down
Loading

0 comments on commit 6236451

Please sign in to comment.