linux/drivers/gpu/host1x/debug.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2010 Google, Inc.
   4 * Author: Erik Gilling <konkers@android.com>
   5 *
   6 * Copyright (C) 2011-2013 NVIDIA Corporation
   7 */
   8
   9#include <linux/debugfs.h>
  10#include <linux/seq_file.h>
  11#include <linux/uaccess.h>
  12
  13#include <linux/io.h>
  14
  15#include "dev.h"
  16#include "debug.h"
  17#include "channel.h"
  18
  19static DEFINE_MUTEX(debug_lock);
  20
  21unsigned int host1x_debug_trace_cmdbuf;
  22
  23static pid_t host1x_debug_force_timeout_pid;
  24static u32 host1x_debug_force_timeout_val;
  25static u32 host1x_debug_force_timeout_channel;
  26
  27void host1x_debug_output(struct output *o, const char *fmt, ...)
  28{
  29        va_list args;
  30        int len;
  31
  32        va_start(args, fmt);
  33        len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
  34        va_end(args);
  35
  36        o->fn(o->ctx, o->buf, len, false);
  37}
  38
  39void host1x_debug_cont(struct output *o, const char *fmt, ...)
  40{
  41        va_list args;
  42        int len;
  43
  44        va_start(args, fmt);
  45        len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
  46        va_end(args);
  47
  48        o->fn(o->ctx, o->buf, len, true);
  49}
  50
  51static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)
  52{
  53        struct host1x *m = dev_get_drvdata(ch->dev->parent);
  54        struct output *o = data;
  55
  56        mutex_lock(&ch->cdma.lock);
  57        mutex_lock(&debug_lock);
  58
  59        if (show_fifo)
  60                host1x_hw_show_channel_fifo(m, ch, o);
  61
  62        host1x_hw_show_channel_cdma(m, ch, o);
  63
  64        mutex_unlock(&debug_lock);
  65        mutex_unlock(&ch->cdma.lock);
  66
  67        return 0;
  68}
  69
  70static void show_syncpts(struct host1x *m, struct output *o)
  71{
  72        struct list_head *pos;
  73        unsigned int i;
  74
  75        host1x_debug_output(o, "---- syncpts ----\n");
  76
  77        for (i = 0; i < host1x_syncpt_nb_pts(m); i++) {
  78                u32 max = host1x_syncpt_read_max(m->syncpt + i);
  79                u32 min = host1x_syncpt_load(m->syncpt + i);
  80                unsigned int waiters = 0;
  81
  82                spin_lock(&m->syncpt[i].intr.lock);
  83                list_for_each(pos, &m->syncpt[i].intr.wait_head)
  84                        waiters++;
  85                spin_unlock(&m->syncpt[i].intr.lock);
  86
  87                if (!min && !max && !waiters)
  88                        continue;
  89
  90                host1x_debug_output(o,
  91                                    "id %u (%s) min %d max %d (%d waiters)\n",
  92                                    i, m->syncpt[i].name, min, max, waiters);
  93        }
  94
  95        for (i = 0; i < host1x_syncpt_nb_bases(m); i++) {
  96                u32 base_val;
  97
  98                base_val = host1x_syncpt_load_wait_base(m->syncpt + i);
  99                if (base_val)
 100                        host1x_debug_output(o, "waitbase id %u val %d\n", i,
 101                                            base_val);
 102        }
 103
 104        host1x_debug_output(o, "\n");
 105}
 106
 107static void show_all(struct host1x *m, struct output *o, bool show_fifo)
 108{
 109        unsigned int i;
 110
 111        host1x_hw_show_mlocks(m, o);
 112        show_syncpts(m, o);
 113        host1x_debug_output(o, "---- channels ----\n");
 114
 115        for (i = 0; i < m->info->nb_channels; ++i) {
 116                struct host1x_channel *ch = host1x_channel_get_index(m, i);
 117
 118                if (ch) {
 119                        show_channel(ch, o, show_fifo);
 120                        host1x_channel_put(ch);
 121                }
 122        }
 123}
 124
 125static int host1x_debug_show_all(struct seq_file *s, void *unused)
 126{
 127        struct output o = {
 128                .fn = write_to_seqfile,
 129                .ctx = s
 130        };
 131
 132        show_all(s->private, &o, true);
 133
 134        return 0;
 135}
 136
 137static int host1x_debug_show(struct seq_file *s, void *unused)
 138{
 139        struct output o = {
 140                .fn = write_to_seqfile,
 141                .ctx = s
 142        };
 143
 144        show_all(s->private, &o, false);
 145
 146        return 0;
 147}
 148
 149static int host1x_debug_open_all(struct inode *inode, struct file *file)
 150{
 151        return single_open(file, host1x_debug_show_all, inode->i_private);
 152}
 153
 154static const struct file_operations host1x_debug_all_fops = {
 155        .open = host1x_debug_open_all,
 156        .read = seq_read,
 157        .llseek = seq_lseek,
 158        .release = single_release,
 159};
 160
 161static int host1x_debug_open(struct inode *inode, struct file *file)
 162{
 163        return single_open(file, host1x_debug_show, inode->i_private);
 164}
 165
 166static const struct file_operations host1x_debug_fops = {
 167        .open = host1x_debug_open,
 168        .read = seq_read,
 169        .llseek = seq_lseek,
 170        .release = single_release,
 171};
 172
 173static void host1x_debugfs_init(struct host1x *host1x)
 174{
 175        struct dentry *de = debugfs_create_dir("tegra-host1x", NULL);
 176
 177        /* Store the created entry */
 178        host1x->debugfs = de;
 179
 180        debugfs_create_file("status", S_IRUGO, de, host1x, &host1x_debug_fops);
 181        debugfs_create_file("status_all", S_IRUGO, de, host1x,
 182                            &host1x_debug_all_fops);
 183
 184        debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, de,
 185                           &host1x_debug_trace_cmdbuf);
 186
 187        host1x_hw_debug_init(host1x, de);
 188
 189        debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de,
 190                           &host1x_debug_force_timeout_pid);
 191        debugfs_create_u32("force_timeout_val", S_IRUGO|S_IWUSR, de,
 192                           &host1x_debug_force_timeout_val);
 193        debugfs_create_u32("force_timeout_channel", S_IRUGO|S_IWUSR, de,
 194                           &host1x_debug_force_timeout_channel);
 195}
 196
 197static void host1x_debugfs_exit(struct host1x *host1x)
 198{
 199        debugfs_remove_recursive(host1x->debugfs);
 200}
 201
 202void host1x_debug_init(struct host1x *host1x)
 203{
 204        if (IS_ENABLED(CONFIG_DEBUG_FS))
 205                host1x_debugfs_init(host1x);
 206}
 207
 208void host1x_debug_deinit(struct host1x *host1x)
 209{
 210        if (IS_ENABLED(CONFIG_DEBUG_FS))
 211                host1x_debugfs_exit(host1x);
 212}
 213
 214void host1x_debug_dump(struct host1x *host1x)
 215{
 216        struct output o = {
 217                .fn = write_to_printk
 218        };
 219
 220        show_all(host1x, &o, true);
 221}
 222
 223void host1x_debug_dump_syncpts(struct host1x *host1x)
 224{
 225        struct output o = {
 226                .fn = write_to_printk
 227        };
 228
 229        show_syncpts(host1x, &o);
 230}
 231