1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * IBM Accelerator Family 'GenWQE' |
4 | * |
5 | * (C) Copyright IBM Corp. 2013 |
6 | * |
7 | * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> |
8 | * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> |
9 | * Author: Michael Jung <mijung@gmx.net> |
10 | * Author: Michael Ruettger <michael@ibmra.de> |
11 | */ |
12 | |
13 | /* |
14 | * Debugfs interfaces for the GenWQE card. Help to debug potential |
15 | * problems. Dump internal chip state for debugging and failure |
16 | * determination. |
17 | */ |
18 | |
19 | #include <linux/module.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/debugfs.h> |
22 | #include <linux/seq_file.h> |
23 | #include <linux/uaccess.h> |
24 | |
25 | #include "card_base.h" |
26 | #include "card_ddcb.h" |
27 | |
28 | static void dbg_uidn_show(struct seq_file *s, struct genwqe_reg *regs, |
29 | int entries) |
30 | { |
31 | unsigned int i; |
32 | u32 v_hi, v_lo; |
33 | |
34 | for (i = 0; i < entries; i++) { |
35 | v_hi = (regs[i].val >> 32) & 0xffffffff; |
36 | v_lo = (regs[i].val) & 0xffffffff; |
37 | |
38 | seq_printf(m: s, fmt: " 0x%08x 0x%08x 0x%08x 0x%08x EXT_ERR_REC\n" , |
39 | regs[i].addr, regs[i].idx, v_hi, v_lo); |
40 | } |
41 | } |
42 | |
43 | static int curr_dbg_uidn_show(struct seq_file *s, void *unused, int uid) |
44 | { |
45 | struct genwqe_dev *cd = s->private; |
46 | int entries; |
47 | struct genwqe_reg *regs; |
48 | |
49 | entries = genwqe_ffdc_buff_size(cd, unit_id: uid); |
50 | if (entries < 0) |
51 | return -EINVAL; |
52 | |
53 | if (entries == 0) |
54 | return 0; |
55 | |
56 | regs = kcalloc(n: entries, size: sizeof(*regs), GFP_KERNEL); |
57 | if (regs == NULL) |
58 | return -ENOMEM; |
59 | |
60 | genwqe_stop_traps(cd); /* halt the traps while dumping data */ |
61 | genwqe_ffdc_buff_read(cd, unit_id: uid, regs, max_regs: entries); |
62 | genwqe_start_traps(cd); |
63 | |
64 | dbg_uidn_show(s, regs, entries); |
65 | kfree(objp: regs); |
66 | return 0; |
67 | } |
68 | |
69 | static int curr_dbg_uid0_show(struct seq_file *s, void *unused) |
70 | { |
71 | return curr_dbg_uidn_show(s, unused, uid: 0); |
72 | } |
73 | |
74 | DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid0); |
75 | |
76 | static int curr_dbg_uid1_show(struct seq_file *s, void *unused) |
77 | { |
78 | return curr_dbg_uidn_show(s, unused, uid: 1); |
79 | } |
80 | |
81 | DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid1); |
82 | |
83 | static int curr_dbg_uid2_show(struct seq_file *s, void *unused) |
84 | { |
85 | return curr_dbg_uidn_show(s, unused, uid: 2); |
86 | } |
87 | |
88 | DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid2); |
89 | |
90 | static int prev_dbg_uidn_show(struct seq_file *s, void *unused, int uid) |
91 | { |
92 | struct genwqe_dev *cd = s->private; |
93 | |
94 | dbg_uidn_show(s, regs: cd->ffdc[uid].regs, entries: cd->ffdc[uid].entries); |
95 | return 0; |
96 | } |
97 | |
98 | static int prev_dbg_uid0_show(struct seq_file *s, void *unused) |
99 | { |
100 | return prev_dbg_uidn_show(s, unused, uid: 0); |
101 | } |
102 | |
103 | DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid0); |
104 | |
105 | static int prev_dbg_uid1_show(struct seq_file *s, void *unused) |
106 | { |
107 | return prev_dbg_uidn_show(s, unused, uid: 1); |
108 | } |
109 | |
110 | DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid1); |
111 | |
112 | static int prev_dbg_uid2_show(struct seq_file *s, void *unused) |
113 | { |
114 | return prev_dbg_uidn_show(s, unused, uid: 2); |
115 | } |
116 | |
117 | DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid2); |
118 | |
119 | static int curr_regs_show(struct seq_file *s, void *unused) |
120 | { |
121 | struct genwqe_dev *cd = s->private; |
122 | unsigned int i; |
123 | struct genwqe_reg *regs; |
124 | |
125 | regs = kcalloc(GENWQE_FFDC_REGS, size: sizeof(*regs), GFP_KERNEL); |
126 | if (regs == NULL) |
127 | return -ENOMEM; |
128 | |
129 | genwqe_stop_traps(cd); |
130 | genwqe_read_ffdc_regs(cd, regs, GENWQE_FFDC_REGS, all: 1); |
131 | genwqe_start_traps(cd); |
132 | |
133 | for (i = 0; i < GENWQE_FFDC_REGS; i++) { |
134 | if (regs[i].addr == 0xffffffff) |
135 | break; /* invalid entries */ |
136 | |
137 | if (regs[i].val == 0x0ull) |
138 | continue; /* do not print 0x0 FIRs */ |
139 | |
140 | seq_printf(m: s, fmt: " 0x%08x 0x%016llx\n" , |
141 | regs[i].addr, regs[i].val); |
142 | } |
143 | return 0; |
144 | } |
145 | |
146 | DEFINE_SHOW_ATTRIBUTE(curr_regs); |
147 | |
148 | static int prev_regs_show(struct seq_file *s, void *unused) |
149 | { |
150 | struct genwqe_dev *cd = s->private; |
151 | unsigned int i; |
152 | struct genwqe_reg *regs = cd->ffdc[GENWQE_DBG_REGS].regs; |
153 | |
154 | if (regs == NULL) |
155 | return -EINVAL; |
156 | |
157 | for (i = 0; i < GENWQE_FFDC_REGS; i++) { |
158 | if (regs[i].addr == 0xffffffff) |
159 | break; /* invalid entries */ |
160 | |
161 | if (regs[i].val == 0x0ull) |
162 | continue; /* do not print 0x0 FIRs */ |
163 | |
164 | seq_printf(m: s, fmt: " 0x%08x 0x%016llx\n" , |
165 | regs[i].addr, regs[i].val); |
166 | } |
167 | return 0; |
168 | } |
169 | |
170 | DEFINE_SHOW_ATTRIBUTE(prev_regs); |
171 | |
172 | static int jtimer_show(struct seq_file *s, void *unused) |
173 | { |
174 | struct genwqe_dev *cd = s->private; |
175 | unsigned int vf_num; |
176 | u64 jtimer; |
177 | |
178 | jtimer = genwqe_read_vreg(cd, IO_SLC_VF_APPJOB_TIMEOUT, func: 0); |
179 | seq_printf(m: s, fmt: " PF 0x%016llx %d msec\n" , jtimer, |
180 | GENWQE_PF_JOBTIMEOUT_MSEC); |
181 | |
182 | for (vf_num = 0; vf_num < cd->num_vfs; vf_num++) { |
183 | jtimer = genwqe_read_vreg(cd, IO_SLC_VF_APPJOB_TIMEOUT, |
184 | func: vf_num + 1); |
185 | seq_printf(m: s, fmt: " VF%-2d 0x%016llx %d msec\n" , vf_num, jtimer, |
186 | cd->vf_jobtimeout_msec[vf_num]); |
187 | } |
188 | return 0; |
189 | } |
190 | |
191 | DEFINE_SHOW_ATTRIBUTE(jtimer); |
192 | |
193 | static int queue_working_time_show(struct seq_file *s, void *unused) |
194 | { |
195 | struct genwqe_dev *cd = s->private; |
196 | unsigned int vf_num; |
197 | u64 t; |
198 | |
199 | t = genwqe_read_vreg(cd, IO_SLC_VF_QUEUE_WTIME, func: 0); |
200 | seq_printf(m: s, fmt: " PF 0x%016llx\n" , t); |
201 | |
202 | for (vf_num = 0; vf_num < cd->num_vfs; vf_num++) { |
203 | t = genwqe_read_vreg(cd, IO_SLC_VF_QUEUE_WTIME, func: vf_num + 1); |
204 | seq_printf(m: s, fmt: " VF%-2d 0x%016llx\n" , vf_num, t); |
205 | } |
206 | return 0; |
207 | } |
208 | |
209 | DEFINE_SHOW_ATTRIBUTE(queue_working_time); |
210 | |
211 | static int ddcb_info_show(struct seq_file *s, void *unused) |
212 | { |
213 | struct genwqe_dev *cd = s->private; |
214 | unsigned int i; |
215 | struct ddcb_queue *queue; |
216 | struct ddcb *pddcb; |
217 | |
218 | queue = &cd->queue; |
219 | seq_puts(m: s, s: "DDCB QUEUE:\n" ); |
220 | seq_printf(m: s, fmt: " ddcb_max: %d\n" |
221 | " ddcb_daddr: %016llx - %016llx\n" |
222 | " ddcb_vaddr: %p\n" |
223 | " ddcbs_in_flight: %u\n" |
224 | " ddcbs_max_in_flight: %u\n" |
225 | " ddcbs_completed: %u\n" |
226 | " return_on_busy: %u\n" |
227 | " wait_on_busy: %u\n" |
228 | " irqs_processed: %u\n" , |
229 | queue->ddcb_max, (long long)queue->ddcb_daddr, |
230 | (long long)queue->ddcb_daddr + |
231 | (queue->ddcb_max * DDCB_LENGTH), |
232 | queue->ddcb_vaddr, queue->ddcbs_in_flight, |
233 | queue->ddcbs_max_in_flight, queue->ddcbs_completed, |
234 | queue->return_on_busy, queue->wait_on_busy, |
235 | cd->irqs_processed); |
236 | |
237 | /* Hardware State */ |
238 | seq_printf(m: s, fmt: " 0x%08x 0x%016llx IO_QUEUE_CONFIG\n" |
239 | " 0x%08x 0x%016llx IO_QUEUE_STATUS\n" |
240 | " 0x%08x 0x%016llx IO_QUEUE_SEGMENT\n" |
241 | " 0x%08x 0x%016llx IO_QUEUE_INITSQN\n" |
242 | " 0x%08x 0x%016llx IO_QUEUE_WRAP\n" |
243 | " 0x%08x 0x%016llx IO_QUEUE_OFFSET\n" |
244 | " 0x%08x 0x%016llx IO_QUEUE_WTIME\n" |
245 | " 0x%08x 0x%016llx IO_QUEUE_ERRCNTS\n" |
246 | " 0x%08x 0x%016llx IO_QUEUE_LRW\n" , |
247 | queue->IO_QUEUE_CONFIG, |
248 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_CONFIG), |
249 | queue->IO_QUEUE_STATUS, |
250 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_STATUS), |
251 | queue->IO_QUEUE_SEGMENT, |
252 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_SEGMENT), |
253 | queue->IO_QUEUE_INITSQN, |
254 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_INITSQN), |
255 | queue->IO_QUEUE_WRAP, |
256 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_WRAP), |
257 | queue->IO_QUEUE_OFFSET, |
258 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_OFFSET), |
259 | queue->IO_QUEUE_WTIME, |
260 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_WTIME), |
261 | queue->IO_QUEUE_ERRCNTS, |
262 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_ERRCNTS), |
263 | queue->IO_QUEUE_LRW, |
264 | __genwqe_readq(cd, byte_offs: queue->IO_QUEUE_LRW)); |
265 | |
266 | seq_printf(m: s, fmt: "DDCB list (ddcb_act=%d/ddcb_next=%d):\n" , |
267 | queue->ddcb_act, queue->ddcb_next); |
268 | |
269 | pddcb = queue->ddcb_vaddr; |
270 | for (i = 0; i < queue->ddcb_max; i++) { |
271 | seq_printf(m: s, fmt: " %-3d: RETC=%03x SEQ=%04x HSI/SHI=%02x/%02x " , |
272 | i, be16_to_cpu(pddcb->retc_16), |
273 | be16_to_cpu(pddcb->seqnum_16), |
274 | pddcb->hsi, pddcb->shi); |
275 | seq_printf(m: s, fmt: "PRIV=%06llx CMD=%02x\n" , |
276 | be64_to_cpu(pddcb->priv_64), pddcb->cmd); |
277 | pddcb++; |
278 | } |
279 | return 0; |
280 | } |
281 | |
282 | DEFINE_SHOW_ATTRIBUTE(ddcb_info); |
283 | |
284 | static int info_show(struct seq_file *s, void *unused) |
285 | { |
286 | struct genwqe_dev *cd = s->private; |
287 | u64 app_id, slu_id, bitstream = -1; |
288 | struct pci_dev *pci_dev = cd->pci_dev; |
289 | |
290 | slu_id = __genwqe_readq(cd, IO_SLU_UNITCFG); |
291 | app_id = __genwqe_readq(cd, IO_APP_UNITCFG); |
292 | |
293 | if (genwqe_is_privileged(cd)) |
294 | bitstream = __genwqe_readq(cd, IO_SLU_BITSTREAM); |
295 | |
296 | seq_printf(m: s, fmt: "%s driver version: %s\n" |
297 | " Device Name/Type: %s %s CardIdx: %d\n" |
298 | " SLU/APP Config : 0x%016llx/0x%016llx\n" |
299 | " Build Date : %u/%x/%u\n" |
300 | " Base Clock : %u MHz\n" |
301 | " Arch/SVN Release: %u/%llx\n" |
302 | " Bitstream : %llx\n" , |
303 | GENWQE_DEVNAME, DRV_VERSION, dev_name(dev: &pci_dev->dev), |
304 | genwqe_is_privileged(cd) ? |
305 | "Physical" : "Virtual or no SR-IOV" , |
306 | cd->card_idx, slu_id, app_id, |
307 | (u16)((slu_id >> 12) & 0x0fLLU), /* month */ |
308 | (u16)((slu_id >> 4) & 0xffLLU), /* day */ |
309 | (u16)((slu_id >> 16) & 0x0fLLU) + 2010, /* year */ |
310 | genwqe_base_clock_frequency(cd), |
311 | (u16)((slu_id >> 32) & 0xffLLU), slu_id >> 40, |
312 | bitstream); |
313 | |
314 | return 0; |
315 | } |
316 | |
317 | DEFINE_SHOW_ATTRIBUTE(info); |
318 | |
319 | void genwqe_init_debugfs(struct genwqe_dev *cd) |
320 | { |
321 | struct dentry *root; |
322 | char card_name[64]; |
323 | char name[64]; |
324 | unsigned int i; |
325 | |
326 | sprintf(buf: card_name, fmt: "%s%d_card" , GENWQE_DEVNAME, cd->card_idx); |
327 | |
328 | root = debugfs_create_dir(name: card_name, parent: cd->debugfs_genwqe); |
329 | |
330 | /* non privileged interfaces are done here */ |
331 | debugfs_create_file(name: "ddcb_info" , S_IRUGO, parent: root, data: cd, fops: &ddcb_info_fops); |
332 | debugfs_create_file(name: "info" , S_IRUGO, parent: root, data: cd, fops: &info_fops); |
333 | debugfs_create_x64(name: "err_inject" , mode: 0666, parent: root, value: &cd->err_inject); |
334 | debugfs_create_u32(name: "ddcb_software_timeout" , mode: 0666, parent: root, |
335 | value: &cd->ddcb_software_timeout); |
336 | debugfs_create_u32(name: "kill_timeout" , mode: 0666, parent: root, value: &cd->kill_timeout); |
337 | |
338 | /* privileged interfaces follow here */ |
339 | if (!genwqe_is_privileged(cd)) { |
340 | cd->debugfs_root = root; |
341 | return; |
342 | } |
343 | |
344 | debugfs_create_file(name: "curr_regs" , S_IRUGO, parent: root, data: cd, fops: &curr_regs_fops); |
345 | debugfs_create_file(name: "curr_dbg_uid0" , S_IRUGO, parent: root, data: cd, |
346 | fops: &curr_dbg_uid0_fops); |
347 | debugfs_create_file(name: "curr_dbg_uid1" , S_IRUGO, parent: root, data: cd, |
348 | fops: &curr_dbg_uid1_fops); |
349 | debugfs_create_file(name: "curr_dbg_uid2" , S_IRUGO, parent: root, data: cd, |
350 | fops: &curr_dbg_uid2_fops); |
351 | debugfs_create_file(name: "prev_regs" , S_IRUGO, parent: root, data: cd, fops: &prev_regs_fops); |
352 | debugfs_create_file(name: "prev_dbg_uid0" , S_IRUGO, parent: root, data: cd, |
353 | fops: &prev_dbg_uid0_fops); |
354 | debugfs_create_file(name: "prev_dbg_uid1" , S_IRUGO, parent: root, data: cd, |
355 | fops: &prev_dbg_uid1_fops); |
356 | debugfs_create_file(name: "prev_dbg_uid2" , S_IRUGO, parent: root, data: cd, |
357 | fops: &prev_dbg_uid2_fops); |
358 | |
359 | for (i = 0; i < GENWQE_MAX_VFS; i++) { |
360 | sprintf(buf: name, fmt: "vf%u_jobtimeout_msec" , i); |
361 | debugfs_create_u32(name, mode: 0666, parent: root, |
362 | value: &cd->vf_jobtimeout_msec[i]); |
363 | } |
364 | |
365 | debugfs_create_file(name: "jobtimer" , S_IRUGO, parent: root, data: cd, fops: &jtimer_fops); |
366 | debugfs_create_file(name: "queue_working_time" , S_IRUGO, parent: root, data: cd, |
367 | fops: &queue_working_time_fops); |
368 | debugfs_create_u32(name: "skip_recovery" , mode: 0666, parent: root, value: &cd->skip_recovery); |
369 | debugfs_create_u32(name: "use_platform_recovery" , mode: 0666, parent: root, |
370 | value: &cd->use_platform_recovery); |
371 | |
372 | cd->debugfs_root = root; |
373 | } |
374 | |
375 | void genqwe_exit_debugfs(struct genwqe_dev *cd) |
376 | { |
377 | debugfs_remove_recursive(dentry: cd->debugfs_root); |
378 | } |
379 | |