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