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, bool show_all) 79 { 80 unsigned long irqflags; 81 struct list_head *pos; 82 unsigned int i; 83 int err; 84 85 host1x_debug_output(o, "---- syncpts ----\n"); 86 87 err = pm_runtime_resume_and_get(m->dev); 88 if (err < 0) 89 return; 90 91 for (i = 0; i < host1x_syncpt_nb_pts(m); i++) { 92 u32 max = host1x_syncpt_read_max(m->syncpt + i); 93 u32 min = host1x_syncpt_load(m->syncpt + i); 94 unsigned int waiters = 0; 95 96 spin_lock_irqsave(&m->syncpt[i].fences.lock, irqflags); 97 list_for_each(pos, &m->syncpt[i].fences.list) 98 waiters++; 99 spin_unlock_irqrestore(&m->syncpt[i].fences.lock, irqflags); 100 101 if (!kref_read(&m->syncpt[i].ref)) 102 continue; 103 104 if (!show_all && !min && !max && !waiters) 105 continue; 106 107 host1x_debug_output(o, 108 "id %u (%s) min %d max %d (%d waiters)\n", 109 i, m->syncpt[i].name, min, max, waiters); 110 } 111 112 for (i = 0; i < host1x_syncpt_nb_bases(m); i++) { 113 u32 base_val; 114 115 base_val = host1x_syncpt_load_wait_base(m->syncpt + i); 116 if (base_val) 117 host1x_debug_output(o, "waitbase id %u val %d\n", i, 118 base_val); 119 } 120 121 pm_runtime_put(m->dev); 122 123 host1x_debug_output(o, "\n"); 124 } 125 126 static void show_all(struct host1x *m, struct output *o, bool show_fifo) 127 { 128 unsigned int i; 129 130 host1x_hw_show_mlocks(m, o); 131 show_syncpts(m, o, true); 132 host1x_debug_output(o, "---- channels ----\n"); 133 134 for (i = 0; i < m->info->nb_channels; ++i) { 135 struct host1x_channel *ch = host1x_channel_get_index(m, i); 136 137 if (ch) { 138 show_channel(ch, o, show_fifo); 139 host1x_channel_put(ch); 140 } 141 } 142 } 143 144 static int host1x_debug_all_show(struct seq_file *s, void *unused) 145 { 146 struct output o = { 147 .fn = write_to_seqfile, 148 .ctx = s 149 }; 150 151 show_all(s->private, &o, true); 152 153 return 0; 154 } 155 DEFINE_SHOW_ATTRIBUTE(host1x_debug_all); 156 157 static int host1x_debug_show(struct seq_file *s, void *unused) 158 { 159 struct output o = { 160 .fn = write_to_seqfile, 161 .ctx = s 162 }; 163 164 show_all(s->private, &o, false); 165 166 return 0; 167 } 168 DEFINE_SHOW_ATTRIBUTE(host1x_debug); 169 170 static void host1x_debugfs_init(struct host1x *host1x) 171 { 172 struct dentry *de = debugfs_create_dir("tegra-host1x", NULL); 173 174 /* Store the created entry */ 175 host1x->debugfs = de; 176 177 debugfs_create_file("status", S_IRUGO, de, host1x, &host1x_debug_fops); 178 debugfs_create_file("status_all", S_IRUGO, de, host1x, 179 &host1x_debug_all_fops); 180 181 debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, de, 182 &host1x_debug_trace_cmdbuf); 183 184 host1x_hw_debug_init(host1x, de); 185 186 debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de, 187 &host1x_debug_force_timeout_pid); 188 debugfs_create_u32("force_timeout_val", S_IRUGO|S_IWUSR, de, 189 &host1x_debug_force_timeout_val); 190 debugfs_create_u32("force_timeout_channel", S_IRUGO|S_IWUSR, de, 191 &host1x_debug_force_timeout_channel); 192 } 193 194 static void host1x_debugfs_exit(struct host1x *host1x) 195 { 196 debugfs_remove_recursive(host1x->debugfs); 197 } 198 199 void host1x_debug_init(struct host1x *host1x) 200 { 201 if (IS_ENABLED(CONFIG_DEBUG_FS)) 202 host1x_debugfs_init(host1x); 203 } 204 205 void host1x_debug_deinit(struct host1x *host1x) 206 { 207 if (IS_ENABLED(CONFIG_DEBUG_FS)) 208 host1x_debugfs_exit(host1x); 209 } 210 211 void host1x_debug_dump(struct host1x *host1x) 212 { 213 struct output o = { 214 .fn = write_to_printk 215 }; 216 217 show_all(host1x, &o, true); 218 } 219 220 void host1x_debug_dump_syncpts(struct host1x *host1x) 221 { 222 struct output o = { 223 .fn = write_to_printk 224 }; 225 226 show_syncpts(host1x, &o, false); 227 } 228