1 /* 2 * Copyright (C) 2010 Google, Inc. 3 * Author: Erik Gilling <konkers@android.com> 4 * 5 * Copyright (C) 2011-2013 NVIDIA Corporation 6 * 7 * This software is licensed under the terms of the GNU General Public 8 * License version 2, as published by the Free Software Foundation, and 9 * may be copied, distributed, and modified under those terms. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 */ 17 18 #include <linux/debugfs.h> 19 #include <linux/seq_file.h> 20 #include <linux/mm.h> 21 #include <linux/scatterlist.h> 22 23 #include <linux/io.h> 24 25 #include "dev.h" 26 #include "debug.h" 27 #include "cdma.h" 28 #include "channel.h" 29 #include "host1x_bo.h" 30 31 #define HOST1X_DEBUG_MAX_PAGE_OFFSET 102400 32 33 enum { 34 HOST1X_OPCODE_SETCLASS = 0x00, 35 HOST1X_OPCODE_INCR = 0x01, 36 HOST1X_OPCODE_NONINCR = 0x02, 37 HOST1X_OPCODE_MASK = 0x03, 38 HOST1X_OPCODE_IMM = 0x04, 39 HOST1X_OPCODE_RESTART = 0x05, 40 HOST1X_OPCODE_GATHER = 0x06, 41 HOST1X_OPCODE_EXTEND = 0x0e, 42 }; 43 44 enum { 45 HOST1X_OPCODE_EXTEND_ACQUIRE_MLOCK = 0x00, 46 HOST1X_OPCODE_EXTEND_RELEASE_MLOCK = 0x01, 47 }; 48 49 static unsigned int show_channel_command(struct output *o, u32 val) 50 { 51 unsigned mask; 52 unsigned subop; 53 54 switch (val >> 28) { 55 case HOST1X_OPCODE_SETCLASS: 56 mask = val & 0x3f; 57 if (mask) { 58 host1x_debug_output(o, "SETCL(class=%03x, offset=%03x, mask=%02x, [", 59 val >> 6 & 0x3ff, 60 val >> 16 & 0xfff, mask); 61 return hweight8(mask); 62 } else { 63 host1x_debug_output(o, "SETCL(class=%03x)\n", 64 val >> 6 & 0x3ff); 65 return 0; 66 } 67 68 case HOST1X_OPCODE_INCR: 69 host1x_debug_output(o, "INCR(offset=%03x, [", 70 val >> 16 & 0xfff); 71 return val & 0xffff; 72 73 case HOST1X_OPCODE_NONINCR: 74 host1x_debug_output(o, "NONINCR(offset=%03x, [", 75 val >> 16 & 0xfff); 76 return val & 0xffff; 77 78 case HOST1X_OPCODE_MASK: 79 mask = val & 0xffff; 80 host1x_debug_output(o, "MASK(offset=%03x, mask=%03x, [", 81 val >> 16 & 0xfff, mask); 82 return hweight16(mask); 83 84 case HOST1X_OPCODE_IMM: 85 host1x_debug_output(o, "IMM(offset=%03x, data=%03x)\n", 86 val >> 16 & 0xfff, val & 0xffff); 87 return 0; 88 89 case HOST1X_OPCODE_RESTART: 90 host1x_debug_output(o, "RESTART(offset=%08x)\n", val << 4); 91 return 0; 92 93 case HOST1X_OPCODE_GATHER: 94 host1x_debug_output(o, "GATHER(offset=%03x, insert=%d, type=%d, count=%04x, addr=[", 95 val >> 16 & 0xfff, val >> 15 & 0x1, 96 val >> 14 & 0x1, val & 0x3fff); 97 return 1; 98 99 case HOST1X_OPCODE_EXTEND: 100 subop = val >> 24 & 0xf; 101 if (subop == HOST1X_OPCODE_EXTEND_ACQUIRE_MLOCK) 102 host1x_debug_output(o, "ACQUIRE_MLOCK(index=%d)\n", 103 val & 0xff); 104 else if (subop == HOST1X_OPCODE_EXTEND_RELEASE_MLOCK) 105 host1x_debug_output(o, "RELEASE_MLOCK(index=%d)\n", 106 val & 0xff); 107 else 108 host1x_debug_output(o, "EXTEND_UNKNOWN(%08x)\n", val); 109 return 0; 110 111 default: 112 return 0; 113 } 114 } 115 116 static void show_gather(struct output *o, phys_addr_t phys_addr, 117 unsigned int words, struct host1x_cdma *cdma, 118 phys_addr_t pin_addr, u32 *map_addr) 119 { 120 /* Map dmaget cursor to corresponding mem handle */ 121 u32 offset = phys_addr - pin_addr; 122 unsigned int data_count = 0, i; 123 124 /* 125 * Sometimes we're given different hardware address to the same 126 * page - in these cases the offset will get an invalid number and 127 * we just have to bail out. 128 */ 129 if (offset > HOST1X_DEBUG_MAX_PAGE_OFFSET) { 130 host1x_debug_output(o, "[address mismatch]\n"); 131 return; 132 } 133 134 for (i = 0; i < words; i++) { 135 u32 addr = phys_addr + i * 4; 136 u32 val = *(map_addr + offset / 4 + i); 137 138 if (!data_count) { 139 host1x_debug_output(o, "%08x: %08x:", addr, val); 140 data_count = show_channel_command(o, val); 141 } else { 142 host1x_debug_output(o, "%08x%s", val, 143 data_count > 0 ? ", " : "])\n"); 144 data_count--; 145 } 146 } 147 } 148 149 static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma) 150 { 151 struct host1x_job *job; 152 153 list_for_each_entry(job, &cdma->sync_queue, list) { 154 int i; 155 host1x_debug_output(o, "\n%p: JOB, syncpt_id=%d, syncpt_val=%d, first_get=%08x, timeout=%d num_slots=%d, num_handles=%d\n", 156 job, job->syncpt_id, job->syncpt_end, 157 job->first_get, job->timeout, 158 job->num_slots, job->num_unpins); 159 160 for (i = 0; i < job->num_gathers; i++) { 161 struct host1x_job_gather *g = &job->gathers[i]; 162 u32 *mapped; 163 164 if (job->gather_copy_mapped) 165 mapped = (u32 *)job->gather_copy_mapped; 166 else 167 mapped = host1x_bo_mmap(g->bo); 168 169 if (!mapped) { 170 host1x_debug_output(o, "[could not mmap]\n"); 171 continue; 172 } 173 174 host1x_debug_output(o, " GATHER at %08x+%04x, %d words\n", 175 g->base, g->offset, g->words); 176 177 show_gather(o, g->base + g->offset, g->words, cdma, 178 g->base, mapped); 179 180 if (!job->gather_copy_mapped) 181 host1x_bo_munmap(g->bo, mapped); 182 } 183 } 184 } 185 186 static void host1x_debug_show_channel_cdma(struct host1x *host, 187 struct host1x_channel *ch, 188 struct output *o) 189 { 190 struct host1x_cdma *cdma = &ch->cdma; 191 u32 dmaput, dmaget, dmactrl; 192 u32 cbstat, cbread; 193 u32 val, base, baseval; 194 195 dmaput = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT); 196 dmaget = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET); 197 dmactrl = host1x_ch_readl(ch, HOST1X_CHANNEL_DMACTRL); 198 cbread = host1x_sync_readl(host, HOST1X_SYNC_CBREAD(ch->id)); 199 cbstat = host1x_sync_readl(host, HOST1X_SYNC_CBSTAT(ch->id)); 200 201 host1x_debug_output(o, "%d-%s: ", ch->id, dev_name(ch->dev)); 202 203 if (HOST1X_CHANNEL_DMACTRL_DMASTOP_V(dmactrl) || 204 !ch->cdma.push_buffer.mapped) { 205 host1x_debug_output(o, "inactive\n\n"); 206 return; 207 } 208 209 if (HOST1X_SYNC_CBSTAT_CBCLASS_V(cbstat) == HOST1X_CLASS_HOST1X && 210 HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat) == 211 HOST1X_UCLASS_WAIT_SYNCPT) 212 host1x_debug_output(o, "waiting on syncpt %d val %d\n", 213 cbread >> 24, cbread & 0xffffff); 214 else if (HOST1X_SYNC_CBSTAT_CBCLASS_V(cbstat) == 215 HOST1X_CLASS_HOST1X && 216 HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat) == 217 HOST1X_UCLASS_WAIT_SYNCPT_BASE) { 218 219 base = (cbread >> 16) & 0xff; 220 baseval = 221 host1x_sync_readl(host, HOST1X_SYNC_SYNCPT_BASE(base)); 222 val = cbread & 0xffff; 223 host1x_debug_output(o, "waiting on syncpt %d val %d (base %d = %d; offset = %d)\n", 224 cbread >> 24, baseval + val, base, 225 baseval, val); 226 } else 227 host1x_debug_output(o, "active class %02x, offset %04x, val %08x\n", 228 HOST1X_SYNC_CBSTAT_CBCLASS_V(cbstat), 229 HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat), 230 cbread); 231 232 host1x_debug_output(o, "DMAPUT %08x, DMAGET %08x, DMACTL %08x\n", 233 dmaput, dmaget, dmactrl); 234 host1x_debug_output(o, "CBREAD %08x, CBSTAT %08x\n", cbread, cbstat); 235 236 show_channel_gathers(o, cdma); 237 host1x_debug_output(o, "\n"); 238 } 239 240 static void host1x_debug_show_channel_fifo(struct host1x *host, 241 struct host1x_channel *ch, 242 struct output *o) 243 { 244 u32 val, rd_ptr, wr_ptr, start, end; 245 unsigned int data_count = 0; 246 247 host1x_debug_output(o, "%d: fifo:\n", ch->id); 248 249 val = host1x_ch_readl(ch, HOST1X_CHANNEL_FIFOSTAT); 250 host1x_debug_output(o, "FIFOSTAT %08x\n", val); 251 if (HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_V(val)) { 252 host1x_debug_output(o, "[empty]\n"); 253 return; 254 } 255 256 host1x_sync_writel(host, 0x0, HOST1X_SYNC_CFPEEK_CTRL); 257 host1x_sync_writel(host, HOST1X_SYNC_CFPEEK_CTRL_ENA_F(1) | 258 HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(ch->id), 259 HOST1X_SYNC_CFPEEK_CTRL); 260 261 val = host1x_sync_readl(host, HOST1X_SYNC_CFPEEK_PTRS); 262 rd_ptr = HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_V(val); 263 wr_ptr = HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_V(val); 264 265 val = host1x_sync_readl(host, HOST1X_SYNC_CF_SETUP(ch->id)); 266 start = HOST1X_SYNC_CF_SETUP_BASE_V(val); 267 end = HOST1X_SYNC_CF_SETUP_LIMIT_V(val); 268 269 do { 270 host1x_sync_writel(host, 0x0, HOST1X_SYNC_CFPEEK_CTRL); 271 host1x_sync_writel(host, HOST1X_SYNC_CFPEEK_CTRL_ENA_F(1) | 272 HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(ch->id) | 273 HOST1X_SYNC_CFPEEK_CTRL_ADDR_F(rd_ptr), 274 HOST1X_SYNC_CFPEEK_CTRL); 275 val = host1x_sync_readl(host, HOST1X_SYNC_CFPEEK_READ); 276 277 if (!data_count) { 278 host1x_debug_output(o, "%08x:", val); 279 data_count = show_channel_command(o, val); 280 } else { 281 host1x_debug_output(o, "%08x%s", val, 282 data_count > 0 ? ", " : "])\n"); 283 data_count--; 284 } 285 286 if (rd_ptr == end) 287 rd_ptr = start; 288 else 289 rd_ptr++; 290 } while (rd_ptr != wr_ptr); 291 292 if (data_count) 293 host1x_debug_output(o, ", ...])\n"); 294 host1x_debug_output(o, "\n"); 295 296 host1x_sync_writel(host, 0x0, HOST1X_SYNC_CFPEEK_CTRL); 297 } 298 299 static void host1x_debug_show_mlocks(struct host1x *host, struct output *o) 300 { 301 int i; 302 303 host1x_debug_output(o, "---- mlocks ----\n"); 304 for (i = 0; i < host1x_syncpt_nb_mlocks(host); i++) { 305 u32 owner = 306 host1x_sync_readl(host, HOST1X_SYNC_MLOCK_OWNER(i)); 307 if (HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(owner)) 308 host1x_debug_output(o, "%d: locked by channel %d\n", 309 i, HOST1X_SYNC_MLOCK_OWNER_CHID_F(owner)); 310 else if (HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(owner)) 311 host1x_debug_output(o, "%d: locked by cpu\n", i); 312 else 313 host1x_debug_output(o, "%d: unlocked\n", i); 314 } 315 host1x_debug_output(o, "\n"); 316 } 317 318 static const struct host1x_debug_ops host1x_debug_ops = { 319 .show_channel_cdma = host1x_debug_show_channel_cdma, 320 .show_channel_fifo = host1x_debug_show_channel_fifo, 321 .show_mlocks = host1x_debug_show_mlocks, 322 }; 323