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 20 static DEFINE_MUTEX(debug_lock); 21 22 unsigned int host1x_debug_trace_cmdbuf; 23 24 static pid_t host1x_debug_force_timeout_pid; 25 static u32 host1x_debug_force_timeout_val; 26 static u32 host1x_debug_force_timeout_channel; 27 28 void 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 40 void 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 52 static 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 78 static void show_syncpts(struct host1x *m, struct output *o) 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 (!min && !max && !waiters) 101 continue; 102 103 host1x_debug_output(o, 104 "id %u (%s) min %d max %d (%d waiters)\n", 105 i, m->syncpt[i].name, min, max, waiters); 106 } 107 108 for (i = 0; i < host1x_syncpt_nb_bases(m); i++) { 109 u32 base_val; 110 111 base_val = host1x_syncpt_load_wait_base(m->syncpt + i); 112 if (base_val) 113 host1x_debug_output(o, "waitbase id %u val %d\n", i, 114 base_val); 115 } 116 117 pm_runtime_put(m->dev); 118 119 host1x_debug_output(o, "\n"); 120 } 121 122 static void show_all(struct host1x *m, struct output *o, bool show_fifo) 123 { 124 unsigned int i; 125 126 host1x_hw_show_mlocks(m, o); 127 show_syncpts(m, o); 128 host1x_debug_output(o, "---- channels ----\n"); 129 130 for (i = 0; i < m->info->nb_channels; ++i) { 131 struct host1x_channel *ch = host1x_channel_get_index(m, i); 132 133 if (ch) { 134 show_channel(ch, o, show_fifo); 135 host1x_channel_put(ch); 136 } 137 } 138 } 139 140 static int host1x_debug_show_all(struct seq_file *s, void *unused) 141 { 142 struct output o = { 143 .fn = write_to_seqfile, 144 .ctx = s 145 }; 146 147 show_all(s->private, &o, true); 148 149 return 0; 150 } 151 152 static int host1x_debug_show(struct seq_file *s, void *unused) 153 { 154 struct output o = { 155 .fn = write_to_seqfile, 156 .ctx = s 157 }; 158 159 show_all(s->private, &o, false); 160 161 return 0; 162 } 163 164 static int host1x_debug_open_all(struct inode *inode, struct file *file) 165 { 166 return single_open(file, host1x_debug_show_all, inode->i_private); 167 } 168 169 static const struct file_operations host1x_debug_all_fops = { 170 .open = host1x_debug_open_all, 171 .read = seq_read, 172 .llseek = seq_lseek, 173 .release = single_release, 174 }; 175 176 static int host1x_debug_open(struct inode *inode, struct file *file) 177 { 178 return single_open(file, host1x_debug_show, inode->i_private); 179 } 180 181 static const struct file_operations host1x_debug_fops = { 182 .open = host1x_debug_open, 183 .read = seq_read, 184 .llseek = seq_lseek, 185 .release = single_release, 186 }; 187 188 static void host1x_debugfs_init(struct host1x *host1x) 189 { 190 struct dentry *de = debugfs_create_dir("tegra-host1x", NULL); 191 192 /* Store the created entry */ 193 host1x->debugfs = de; 194 195 debugfs_create_file("status", S_IRUGO, de, host1x, &host1x_debug_fops); 196 debugfs_create_file("status_all", S_IRUGO, de, host1x, 197 &host1x_debug_all_fops); 198 199 debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, de, 200 &host1x_debug_trace_cmdbuf); 201 202 host1x_hw_debug_init(host1x, de); 203 204 debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de, 205 &host1x_debug_force_timeout_pid); 206 debugfs_create_u32("force_timeout_val", S_IRUGO|S_IWUSR, de, 207 &host1x_debug_force_timeout_val); 208 debugfs_create_u32("force_timeout_channel", S_IRUGO|S_IWUSR, de, 209 &host1x_debug_force_timeout_channel); 210 } 211 212 static void host1x_debugfs_exit(struct host1x *host1x) 213 { 214 debugfs_remove_recursive(host1x->debugfs); 215 } 216 217 void host1x_debug_init(struct host1x *host1x) 218 { 219 if (IS_ENABLED(CONFIG_DEBUG_FS)) 220 host1x_debugfs_init(host1x); 221 } 222 223 void host1x_debug_deinit(struct host1x *host1x) 224 { 225 if (IS_ENABLED(CONFIG_DEBUG_FS)) 226 host1x_debugfs_exit(host1x); 227 } 228 229 void host1x_debug_dump(struct host1x *host1x) 230 { 231 struct output o = { 232 .fn = write_to_printk 233 }; 234 235 show_all(host1x, &o, true); 236 } 237 238 void host1x_debug_dump_syncpts(struct host1x *host1x) 239 { 240 struct output o = { 241 .fn = write_to_printk 242 }; 243 244 show_syncpts(host1x, &o); 245 } 246