1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 HiSilicon Limited. */
3 #include <linux/hisi_acc_qm.h>
4 #include "qm_common.h"
5
6 #define QM_DFX_BASE 0x0100000
7 #define QM_DFX_STATE1 0x0104000
8 #define QM_DFX_STATE2 0x01040C8
9 #define QM_DFX_COMMON 0x0000
10 #define QM_DFX_BASE_LEN 0x5A
11 #define QM_DFX_STATE1_LEN 0x2E
12 #define QM_DFX_STATE2_LEN 0x11
13 #define QM_DFX_COMMON_LEN 0xC3
14 #define QM_DFX_REGS_LEN 4UL
15 #define QM_DBG_TMP_BUF_LEN 22
16 #define CURRENT_FUN_MASK GENMASK(5, 0)
17 #define CURRENT_Q_MASK GENMASK(31, 16)
18 #define QM_SQE_ADDR_MASK GENMASK(7, 0)
19
20 #define QM_DFX_MB_CNT_VF 0x104010
21 #define QM_DFX_DB_CNT_VF 0x104020
22 #define QM_DFX_SQE_CNT_VF_SQN 0x104030
23 #define QM_DFX_CQE_CNT_VF_CQN 0x104040
24 #define QM_DFX_QN_SHIFT 16
25 #define QM_DFX_CNT_CLR_CE 0x100118
26 #define QM_DBG_WRITE_LEN 1024
27
28 static const char * const qm_debug_file_name[] = {
29 [CURRENT_QM] = "current_qm",
30 [CURRENT_Q] = "current_q",
31 [CLEAR_ENABLE] = "clear_enable",
32 };
33
34 struct qm_dfx_item {
35 const char *name;
36 u32 offset;
37 };
38
39 struct qm_cmd_dump_item {
40 const char *cmd;
41 char *info_name;
42 int (*dump_fn)(struct hisi_qm *qm, char *cmd, char *info_name);
43 };
44
45 static struct qm_dfx_item qm_dfx_files[] = {
46 {"err_irq", offsetof(struct qm_dfx, err_irq_cnt)},
47 {"aeq_irq", offsetof(struct qm_dfx, aeq_irq_cnt)},
48 {"abnormal_irq", offsetof(struct qm_dfx, abnormal_irq_cnt)},
49 {"create_qp_err", offsetof(struct qm_dfx, create_qp_err_cnt)},
50 {"mb_err", offsetof(struct qm_dfx, mb_err_cnt)},
51 };
52
53 #define CNT_CYC_REGS_NUM 10
54 static const struct debugfs_reg32 qm_dfx_regs[] = {
55 /* XXX_CNT are reading clear register */
56 {"QM_ECC_1BIT_CNT ", 0x104000ull},
57 {"QM_ECC_MBIT_CNT ", 0x104008ull},
58 {"QM_DFX_MB_CNT ", 0x104018ull},
59 {"QM_DFX_DB_CNT ", 0x104028ull},
60 {"QM_DFX_SQE_CNT ", 0x104038ull},
61 {"QM_DFX_CQE_CNT ", 0x104048ull},
62 {"QM_DFX_SEND_SQE_TO_ACC_CNT ", 0x104050ull},
63 {"QM_DFX_WB_SQE_FROM_ACC_CNT ", 0x104058ull},
64 {"QM_DFX_ACC_FINISH_CNT ", 0x104060ull},
65 {"QM_DFX_CQE_ERR_CNT ", 0x1040b4ull},
66 {"QM_DFX_FUNS_ACTIVE_ST ", 0x200ull},
67 {"QM_ECC_1BIT_INF ", 0x104004ull},
68 {"QM_ECC_MBIT_INF ", 0x10400cull},
69 {"QM_DFX_ACC_RDY_VLD0 ", 0x1040a0ull},
70 {"QM_DFX_ACC_RDY_VLD1 ", 0x1040a4ull},
71 {"QM_DFX_AXI_RDY_VLD ", 0x1040a8ull},
72 {"QM_DFX_FF_ST0 ", 0x1040c8ull},
73 {"QM_DFX_FF_ST1 ", 0x1040ccull},
74 {"QM_DFX_FF_ST2 ", 0x1040d0ull},
75 {"QM_DFX_FF_ST3 ", 0x1040d4ull},
76 {"QM_DFX_FF_ST4 ", 0x1040d8ull},
77 {"QM_DFX_FF_ST5 ", 0x1040dcull},
78 {"QM_DFX_FF_ST6 ", 0x1040e0ull},
79 {"QM_IN_IDLE_ST ", 0x1040e4ull},
80 };
81
82 static const struct debugfs_reg32 qm_vf_dfx_regs[] = {
83 {"QM_DFX_FUNS_ACTIVE_ST ", 0x200ull},
84 };
85
86 /* define the QM's dfx regs region and region length */
87 static struct dfx_diff_registers qm_diff_regs[] = {
88 {
89 .reg_offset = QM_DFX_BASE,
90 .reg_len = QM_DFX_BASE_LEN,
91 }, {
92 .reg_offset = QM_DFX_STATE1,
93 .reg_len = QM_DFX_STATE1_LEN,
94 }, {
95 .reg_offset = QM_DFX_STATE2,
96 .reg_len = QM_DFX_STATE2_LEN,
97 }, {
98 .reg_offset = QM_DFX_COMMON,
99 .reg_len = QM_DFX_COMMON_LEN,
100 },
101 };
102
file_to_qm(struct debugfs_file * file)103 static struct hisi_qm *file_to_qm(struct debugfs_file *file)
104 {
105 struct qm_debug *debug = file->debug;
106
107 return container_of(debug, struct hisi_qm, debug);
108 }
109
qm_cmd_read(struct file * filp,char __user * buffer,size_t count,loff_t * pos)110 static ssize_t qm_cmd_read(struct file *filp, char __user *buffer,
111 size_t count, loff_t *pos)
112 {
113 char buf[QM_DBG_READ_LEN];
114 int len;
115
116 len = scnprintf(buf, QM_DBG_READ_LEN, "%s\n",
117 "Please echo help to cmd to get help information");
118
119 return simple_read_from_buffer(buffer, count, pos, buf, len);
120 }
121
dump_show(struct hisi_qm * qm,void * info,unsigned int info_size,char * info_name)122 static void dump_show(struct hisi_qm *qm, void *info,
123 unsigned int info_size, char *info_name)
124 {
125 struct device *dev = &qm->pdev->dev;
126 u8 *info_curr = info;
127 u32 i;
128 #define BYTE_PER_DW 4
129
130 dev_info(dev, "%s DUMP\n", info_name);
131 for (i = 0; i < info_size; i += BYTE_PER_DW, info_curr += BYTE_PER_DW) {
132 pr_info("DW%u: %02X%02X %02X%02X\n", i / BYTE_PER_DW,
133 *(info_curr + 3), *(info_curr + 2), *(info_curr + 1), *(info_curr));
134 }
135 }
136
qm_sqc_dump(struct hisi_qm * qm,char * s,char * name)137 static int qm_sqc_dump(struct hisi_qm *qm, char *s, char *name)
138 {
139 struct device *dev = &qm->pdev->dev;
140 struct qm_sqc *sqc, *sqc_curr;
141 dma_addr_t sqc_dma;
142 u32 qp_id;
143 int ret;
144
145 if (!s)
146 return -EINVAL;
147
148 ret = kstrtou32(s, 0, &qp_id);
149 if (ret || qp_id >= qm->qp_num) {
150 dev_err(dev, "Please input qp num (0-%u)", qm->qp_num - 1);
151 return -EINVAL;
152 }
153
154 sqc = hisi_qm_ctx_alloc(qm, sizeof(*sqc), &sqc_dma);
155 if (IS_ERR(sqc))
156 return PTR_ERR(sqc);
157
158 ret = hisi_qm_mb(qm, QM_MB_CMD_SQC, sqc_dma, qp_id, 1);
159 if (ret) {
160 down_read(&qm->qps_lock);
161 if (qm->sqc) {
162 sqc_curr = qm->sqc + qp_id;
163
164 dump_show(qm, sqc_curr, sizeof(*sqc), "SOFT SQC");
165 }
166 up_read(&qm->qps_lock);
167
168 goto free_ctx;
169 }
170
171 dump_show(qm, sqc, sizeof(*sqc), name);
172
173 free_ctx:
174 hisi_qm_ctx_free(qm, sizeof(*sqc), sqc, &sqc_dma);
175 return 0;
176 }
177
qm_cqc_dump(struct hisi_qm * qm,char * s,char * name)178 static int qm_cqc_dump(struct hisi_qm *qm, char *s, char *name)
179 {
180 struct device *dev = &qm->pdev->dev;
181 struct qm_cqc *cqc, *cqc_curr;
182 dma_addr_t cqc_dma;
183 u32 qp_id;
184 int ret;
185
186 if (!s)
187 return -EINVAL;
188
189 ret = kstrtou32(s, 0, &qp_id);
190 if (ret || qp_id >= qm->qp_num) {
191 dev_err(dev, "Please input qp num (0-%u)", qm->qp_num - 1);
192 return -EINVAL;
193 }
194
195 cqc = hisi_qm_ctx_alloc(qm, sizeof(*cqc), &cqc_dma);
196 if (IS_ERR(cqc))
197 return PTR_ERR(cqc);
198
199 ret = hisi_qm_mb(qm, QM_MB_CMD_CQC, cqc_dma, qp_id, 1);
200 if (ret) {
201 down_read(&qm->qps_lock);
202 if (qm->cqc) {
203 cqc_curr = qm->cqc + qp_id;
204
205 dump_show(qm, cqc_curr, sizeof(*cqc), "SOFT CQC");
206 }
207 up_read(&qm->qps_lock);
208
209 goto free_ctx;
210 }
211
212 dump_show(qm, cqc, sizeof(*cqc), name);
213
214 free_ctx:
215 hisi_qm_ctx_free(qm, sizeof(*cqc), cqc, &cqc_dma);
216 return 0;
217 }
218
qm_eqc_aeqc_dump(struct hisi_qm * qm,char * s,char * name)219 static int qm_eqc_aeqc_dump(struct hisi_qm *qm, char *s, char *name)
220 {
221 struct device *dev = &qm->pdev->dev;
222 dma_addr_t xeqc_dma;
223 size_t size;
224 void *xeqc;
225 int ret;
226 u8 cmd;
227
228 if (strsep(&s, " ")) {
229 dev_err(dev, "Please do not input extra characters!\n");
230 return -EINVAL;
231 }
232
233 if (!strcmp(name, "EQC")) {
234 cmd = QM_MB_CMD_EQC;
235 size = sizeof(struct qm_eqc);
236 } else {
237 cmd = QM_MB_CMD_AEQC;
238 size = sizeof(struct qm_aeqc);
239 }
240
241 xeqc = hisi_qm_ctx_alloc(qm, size, &xeqc_dma);
242 if (IS_ERR(xeqc))
243 return PTR_ERR(xeqc);
244
245 ret = hisi_qm_mb(qm, cmd, xeqc_dma, 0, 1);
246 if (ret)
247 goto err_free_ctx;
248
249 dump_show(qm, xeqc, size, name);
250
251 err_free_ctx:
252 hisi_qm_ctx_free(qm, size, xeqc, &xeqc_dma);
253 return ret;
254 }
255
q_dump_param_parse(struct hisi_qm * qm,char * s,u32 * e_id,u32 * q_id,u16 q_depth)256 static int q_dump_param_parse(struct hisi_qm *qm, char *s,
257 u32 *e_id, u32 *q_id, u16 q_depth)
258 {
259 struct device *dev = &qm->pdev->dev;
260 unsigned int qp_num = qm->qp_num;
261 char *presult;
262 int ret;
263
264 presult = strsep(&s, " ");
265 if (!presult) {
266 dev_err(dev, "Please input qp number!\n");
267 return -EINVAL;
268 }
269
270 ret = kstrtou32(presult, 0, q_id);
271 if (ret || *q_id >= qp_num) {
272 dev_err(dev, "Please input qp num (0-%u)", qp_num - 1);
273 return -EINVAL;
274 }
275
276 presult = strsep(&s, " ");
277 if (!presult) {
278 dev_err(dev, "Please input sqe number!\n");
279 return -EINVAL;
280 }
281
282 ret = kstrtou32(presult, 0, e_id);
283 if (ret || *e_id >= q_depth) {
284 dev_err(dev, "Please input sqe num (0-%u)", q_depth - 1);
285 return -EINVAL;
286 }
287
288 if (strsep(&s, " ")) {
289 dev_err(dev, "Please do not input extra characters!\n");
290 return -EINVAL;
291 }
292
293 return 0;
294 }
295
qm_sq_dump(struct hisi_qm * qm,char * s,char * name)296 static int qm_sq_dump(struct hisi_qm *qm, char *s, char *name)
297 {
298 u16 sq_depth = qm->qp_array->cq_depth;
299 void *sqe, *sqe_curr;
300 struct hisi_qp *qp;
301 u32 qp_id, sqe_id;
302 int ret;
303
304 ret = q_dump_param_parse(qm, s, &sqe_id, &qp_id, sq_depth);
305 if (ret)
306 return ret;
307
308 sqe = kzalloc(qm->sqe_size * sq_depth, GFP_KERNEL);
309 if (!sqe)
310 return -ENOMEM;
311
312 qp = &qm->qp_array[qp_id];
313 memcpy(sqe, qp->sqe, qm->sqe_size * sq_depth);
314 sqe_curr = sqe + (u32)(sqe_id * qm->sqe_size);
315 memset(sqe_curr + qm->debug.sqe_mask_offset, QM_SQE_ADDR_MASK,
316 qm->debug.sqe_mask_len);
317
318 dump_show(qm, sqe_curr, qm->sqe_size, name);
319
320 kfree(sqe);
321
322 return 0;
323 }
324
qm_cq_dump(struct hisi_qm * qm,char * s,char * name)325 static int qm_cq_dump(struct hisi_qm *qm, char *s, char *name)
326 {
327 struct qm_cqe *cqe_curr;
328 struct hisi_qp *qp;
329 u32 qp_id, cqe_id;
330 int ret;
331
332 ret = q_dump_param_parse(qm, s, &cqe_id, &qp_id, qm->qp_array->cq_depth);
333 if (ret)
334 return ret;
335
336 qp = &qm->qp_array[qp_id];
337 cqe_curr = qp->cqe + cqe_id;
338 dump_show(qm, cqe_curr, sizeof(struct qm_cqe), name);
339
340 return 0;
341 }
342
qm_eq_aeq_dump(struct hisi_qm * qm,char * s,char * name)343 static int qm_eq_aeq_dump(struct hisi_qm *qm, char *s, char *name)
344 {
345 struct device *dev = &qm->pdev->dev;
346 u16 xeq_depth;
347 size_t size;
348 void *xeqe;
349 u32 xeqe_id;
350 int ret;
351
352 if (!s)
353 return -EINVAL;
354
355 ret = kstrtou32(s, 0, &xeqe_id);
356 if (ret)
357 return -EINVAL;
358
359 if (!strcmp(name, "EQE")) {
360 xeq_depth = qm->eq_depth;
361 size = sizeof(struct qm_eqe);
362 } else {
363 xeq_depth = qm->aeq_depth;
364 size = sizeof(struct qm_aeqe);
365 }
366
367 if (xeqe_id >= xeq_depth) {
368 dev_err(dev, "Please input eqe or aeqe num (0-%u)", xeq_depth - 1);
369 return -EINVAL;
370 }
371
372 down_read(&qm->qps_lock);
373
374 if (qm->eqe && !strcmp(name, "EQE")) {
375 xeqe = qm->eqe + xeqe_id;
376 } else if (qm->aeqe && !strcmp(name, "AEQE")) {
377 xeqe = qm->aeqe + xeqe_id;
378 } else {
379 ret = -EINVAL;
380 goto err_unlock;
381 }
382
383 dump_show(qm, xeqe, size, name);
384
385 err_unlock:
386 up_read(&qm->qps_lock);
387 return ret;
388 }
389
qm_dbg_help(struct hisi_qm * qm,char * s)390 static int qm_dbg_help(struct hisi_qm *qm, char *s)
391 {
392 struct device *dev = &qm->pdev->dev;
393
394 if (strsep(&s, " ")) {
395 dev_err(dev, "Please do not input extra characters!\n");
396 return -EINVAL;
397 }
398
399 dev_info(dev, "available commands:\n");
400 dev_info(dev, "sqc <num>\n");
401 dev_info(dev, "cqc <num>\n");
402 dev_info(dev, "eqc\n");
403 dev_info(dev, "aeqc\n");
404 dev_info(dev, "sq <num> <e>\n");
405 dev_info(dev, "cq <num> <e>\n");
406 dev_info(dev, "eq <e>\n");
407 dev_info(dev, "aeq <e>\n");
408
409 return 0;
410 }
411
412 static const struct qm_cmd_dump_item qm_cmd_dump_table[] = {
413 {
414 .cmd = "sqc",
415 .info_name = "SQC",
416 .dump_fn = qm_sqc_dump,
417 }, {
418 .cmd = "cqc",
419 .info_name = "CQC",
420 .dump_fn = qm_cqc_dump,
421 }, {
422 .cmd = "eqc",
423 .info_name = "EQC",
424 .dump_fn = qm_eqc_aeqc_dump,
425 }, {
426 .cmd = "aeqc",
427 .info_name = "AEQC",
428 .dump_fn = qm_eqc_aeqc_dump,
429 }, {
430 .cmd = "sq",
431 .info_name = "SQE",
432 .dump_fn = qm_sq_dump,
433 }, {
434 .cmd = "cq",
435 .info_name = "CQE",
436 .dump_fn = qm_cq_dump,
437 }, {
438 .cmd = "eq",
439 .info_name = "EQE",
440 .dump_fn = qm_eq_aeq_dump,
441 }, {
442 .cmd = "aeq",
443 .info_name = "AEQE",
444 .dump_fn = qm_eq_aeq_dump,
445 },
446 };
447
qm_cmd_write_dump(struct hisi_qm * qm,const char * cmd_buf)448 static int qm_cmd_write_dump(struct hisi_qm *qm, const char *cmd_buf)
449 {
450 struct device *dev = &qm->pdev->dev;
451 char *presult, *s, *s_tmp;
452 int table_size, i, ret;
453
454 s = kstrdup(cmd_buf, GFP_KERNEL);
455 if (!s)
456 return -ENOMEM;
457
458 s_tmp = s;
459 presult = strsep(&s, " ");
460 if (!presult) {
461 ret = -EINVAL;
462 goto err_buffer_free;
463 }
464
465 if (!strcmp(presult, "help")) {
466 ret = qm_dbg_help(qm, s);
467 goto err_buffer_free;
468 }
469
470 table_size = ARRAY_SIZE(qm_cmd_dump_table);
471 for (i = 0; i < table_size; i++) {
472 if (!strcmp(presult, qm_cmd_dump_table[i].cmd)) {
473 ret = qm_cmd_dump_table[i].dump_fn(qm, s,
474 qm_cmd_dump_table[i].info_name);
475 break;
476 }
477 }
478
479 if (i == table_size) {
480 dev_info(dev, "Please echo help\n");
481 ret = -EINVAL;
482 }
483
484 err_buffer_free:
485 kfree(s_tmp);
486
487 return ret;
488 }
489
qm_cmd_write(struct file * filp,const char __user * buffer,size_t count,loff_t * pos)490 static ssize_t qm_cmd_write(struct file *filp, const char __user *buffer,
491 size_t count, loff_t *pos)
492 {
493 struct hisi_qm *qm = filp->private_data;
494 char *cmd_buf, *cmd_buf_tmp;
495 int ret;
496
497 if (*pos)
498 return 0;
499
500 ret = hisi_qm_get_dfx_access(qm);
501 if (ret)
502 return ret;
503
504 /* Judge if the instance is being reset. */
505 if (unlikely(atomic_read(&qm->status.flags) == QM_STOP)) {
506 ret = 0;
507 goto put_dfx_access;
508 }
509
510 if (count > QM_DBG_WRITE_LEN) {
511 ret = -ENOSPC;
512 goto put_dfx_access;
513 }
514
515 cmd_buf = memdup_user_nul(buffer, count);
516 if (IS_ERR(cmd_buf)) {
517 ret = PTR_ERR(cmd_buf);
518 goto put_dfx_access;
519 }
520
521 cmd_buf_tmp = strchr(cmd_buf, '\n');
522 if (cmd_buf_tmp) {
523 *cmd_buf_tmp = '\0';
524 count = cmd_buf_tmp - cmd_buf + 1;
525 }
526
527 ret = qm_cmd_write_dump(qm, cmd_buf);
528 if (ret) {
529 kfree(cmd_buf);
530 goto put_dfx_access;
531 }
532
533 kfree(cmd_buf);
534
535 ret = count;
536
537 put_dfx_access:
538 hisi_qm_put_dfx_access(qm);
539 return ret;
540 }
541
542 static const struct file_operations qm_cmd_fops = {
543 .owner = THIS_MODULE,
544 .open = simple_open,
545 .read = qm_cmd_read,
546 .write = qm_cmd_write,
547 };
548
549 /**
550 * hisi_qm_regs_dump() - Dump registers's value.
551 * @s: debugfs file handle.
552 * @regset: accelerator registers information.
553 *
554 * Dump accelerator registers.
555 */
hisi_qm_regs_dump(struct seq_file * s,struct debugfs_regset32 * regset)556 void hisi_qm_regs_dump(struct seq_file *s, struct debugfs_regset32 *regset)
557 {
558 struct pci_dev *pdev = to_pci_dev(regset->dev);
559 struct hisi_qm *qm = pci_get_drvdata(pdev);
560 const struct debugfs_reg32 *regs = regset->regs;
561 int regs_len = regset->nregs;
562 int i, ret;
563 u32 val;
564
565 ret = hisi_qm_get_dfx_access(qm);
566 if (ret)
567 return;
568
569 for (i = 0; i < regs_len; i++) {
570 val = readl(regset->base + regs[i].offset);
571 seq_printf(s, "%s= 0x%08x\n", regs[i].name, val);
572 }
573
574 hisi_qm_put_dfx_access(qm);
575 }
576 EXPORT_SYMBOL_GPL(hisi_qm_regs_dump);
577
qm_regs_show(struct seq_file * s,void * unused)578 static int qm_regs_show(struct seq_file *s, void *unused)
579 {
580 struct hisi_qm *qm = s->private;
581 struct debugfs_regset32 regset;
582
583 if (qm->fun_type == QM_HW_PF) {
584 regset.regs = qm_dfx_regs;
585 regset.nregs = ARRAY_SIZE(qm_dfx_regs);
586 } else {
587 regset.regs = qm_vf_dfx_regs;
588 regset.nregs = ARRAY_SIZE(qm_vf_dfx_regs);
589 }
590
591 regset.base = qm->io_base;
592 regset.dev = &qm->pdev->dev;
593
594 hisi_qm_regs_dump(s, ®set);
595
596 return 0;
597 }
598
599 DEFINE_SHOW_ATTRIBUTE(qm_regs);
600
current_q_read(struct hisi_qm * qm)601 static u32 current_q_read(struct hisi_qm *qm)
602 {
603 return readl(qm->io_base + QM_DFX_SQE_CNT_VF_SQN) >> QM_DFX_QN_SHIFT;
604 }
605
current_q_write(struct hisi_qm * qm,u32 val)606 static int current_q_write(struct hisi_qm *qm, u32 val)
607 {
608 u32 tmp;
609
610 if (val >= qm->debug.curr_qm_qp_num)
611 return -EINVAL;
612
613 tmp = val << QM_DFX_QN_SHIFT |
614 (readl(qm->io_base + QM_DFX_SQE_CNT_VF_SQN) & CURRENT_FUN_MASK);
615 writel(tmp, qm->io_base + QM_DFX_SQE_CNT_VF_SQN);
616
617 tmp = val << QM_DFX_QN_SHIFT |
618 (readl(qm->io_base + QM_DFX_CQE_CNT_VF_CQN) & CURRENT_FUN_MASK);
619 writel(tmp, qm->io_base + QM_DFX_CQE_CNT_VF_CQN);
620
621 return 0;
622 }
623
clear_enable_read(struct hisi_qm * qm)624 static u32 clear_enable_read(struct hisi_qm *qm)
625 {
626 return readl(qm->io_base + QM_DFX_CNT_CLR_CE);
627 }
628
629 /* rd_clr_ctrl 1 enable read clear, otherwise 0 disable it */
clear_enable_write(struct hisi_qm * qm,u32 rd_clr_ctrl)630 static int clear_enable_write(struct hisi_qm *qm, u32 rd_clr_ctrl)
631 {
632 if (rd_clr_ctrl > 1)
633 return -EINVAL;
634
635 writel(rd_clr_ctrl, qm->io_base + QM_DFX_CNT_CLR_CE);
636
637 return 0;
638 }
639
current_qm_read(struct hisi_qm * qm)640 static u32 current_qm_read(struct hisi_qm *qm)
641 {
642 return readl(qm->io_base + QM_DFX_MB_CNT_VF);
643 }
644
qm_get_vf_qp_num(struct hisi_qm * qm,u32 fun_num)645 static int qm_get_vf_qp_num(struct hisi_qm *qm, u32 fun_num)
646 {
647 u32 remain_q_num, vfq_num;
648 u32 num_vfs = qm->vfs_num;
649
650 vfq_num = (qm->ctrl_qp_num - qm->qp_num) / num_vfs;
651 if (vfq_num >= qm->max_qp_num)
652 return qm->max_qp_num;
653
654 remain_q_num = (qm->ctrl_qp_num - qm->qp_num) % num_vfs;
655 if (vfq_num + remain_q_num <= qm->max_qp_num)
656 return fun_num == num_vfs ? vfq_num + remain_q_num : vfq_num;
657
658 /*
659 * if vfq_num + remain_q_num > max_qp_num, the last VFs,
660 * each with one more queue.
661 */
662 return fun_num + remain_q_num > num_vfs ? vfq_num + 1 : vfq_num;
663 }
664
current_qm_write(struct hisi_qm * qm,u32 val)665 static int current_qm_write(struct hisi_qm *qm, u32 val)
666 {
667 u32 tmp;
668
669 if (val > qm->vfs_num)
670 return -EINVAL;
671
672 /* According PF or VF Dev ID to calculation curr_qm_qp_num and store */
673 if (!val)
674 qm->debug.curr_qm_qp_num = qm->qp_num;
675 else
676 qm->debug.curr_qm_qp_num = qm_get_vf_qp_num(qm, val);
677
678 writel(val, qm->io_base + QM_DFX_MB_CNT_VF);
679 writel(val, qm->io_base + QM_DFX_DB_CNT_VF);
680
681 tmp = val |
682 (readl(qm->io_base + QM_DFX_SQE_CNT_VF_SQN) & CURRENT_Q_MASK);
683 writel(tmp, qm->io_base + QM_DFX_SQE_CNT_VF_SQN);
684
685 tmp = val |
686 (readl(qm->io_base + QM_DFX_CQE_CNT_VF_CQN) & CURRENT_Q_MASK);
687 writel(tmp, qm->io_base + QM_DFX_CQE_CNT_VF_CQN);
688
689 return 0;
690 }
691
qm_debug_read(struct file * filp,char __user * buf,size_t count,loff_t * pos)692 static ssize_t qm_debug_read(struct file *filp, char __user *buf,
693 size_t count, loff_t *pos)
694 {
695 struct debugfs_file *file = filp->private_data;
696 enum qm_debug_file index = file->index;
697 struct hisi_qm *qm = file_to_qm(file);
698 char tbuf[QM_DBG_TMP_BUF_LEN];
699 u32 val;
700 int ret;
701
702 ret = hisi_qm_get_dfx_access(qm);
703 if (ret)
704 return ret;
705
706 mutex_lock(&file->lock);
707 switch (index) {
708 case CURRENT_QM:
709 val = current_qm_read(qm);
710 break;
711 case CURRENT_Q:
712 val = current_q_read(qm);
713 break;
714 case CLEAR_ENABLE:
715 val = clear_enable_read(qm);
716 break;
717 default:
718 goto err_input;
719 }
720 mutex_unlock(&file->lock);
721
722 hisi_qm_put_dfx_access(qm);
723 ret = scnprintf(tbuf, QM_DBG_TMP_BUF_LEN, "%u\n", val);
724 return simple_read_from_buffer(buf, count, pos, tbuf, ret);
725
726 err_input:
727 mutex_unlock(&file->lock);
728 hisi_qm_put_dfx_access(qm);
729 return -EINVAL;
730 }
731
qm_debug_write(struct file * filp,const char __user * buf,size_t count,loff_t * pos)732 static ssize_t qm_debug_write(struct file *filp, const char __user *buf,
733 size_t count, loff_t *pos)
734 {
735 struct debugfs_file *file = filp->private_data;
736 enum qm_debug_file index = file->index;
737 struct hisi_qm *qm = file_to_qm(file);
738 unsigned long val;
739 char tbuf[QM_DBG_TMP_BUF_LEN];
740 int len, ret;
741
742 if (*pos != 0)
743 return 0;
744
745 if (count >= QM_DBG_TMP_BUF_LEN)
746 return -ENOSPC;
747
748 len = simple_write_to_buffer(tbuf, QM_DBG_TMP_BUF_LEN - 1, pos, buf,
749 count);
750 if (len < 0)
751 return len;
752
753 tbuf[len] = '\0';
754 if (kstrtoul(tbuf, 0, &val))
755 return -EFAULT;
756
757 ret = hisi_qm_get_dfx_access(qm);
758 if (ret)
759 return ret;
760
761 mutex_lock(&file->lock);
762 switch (index) {
763 case CURRENT_QM:
764 ret = current_qm_write(qm, val);
765 break;
766 case CURRENT_Q:
767 ret = current_q_write(qm, val);
768 break;
769 case CLEAR_ENABLE:
770 ret = clear_enable_write(qm, val);
771 break;
772 default:
773 ret = -EINVAL;
774 }
775 mutex_unlock(&file->lock);
776
777 hisi_qm_put_dfx_access(qm);
778
779 if (ret)
780 return ret;
781
782 return count;
783 }
784
785 static const struct file_operations qm_debug_fops = {
786 .owner = THIS_MODULE,
787 .open = simple_open,
788 .read = qm_debug_read,
789 .write = qm_debug_write,
790 };
791
dfx_regs_uninit(struct hisi_qm * qm,struct dfx_diff_registers * dregs,int reg_len)792 static void dfx_regs_uninit(struct hisi_qm *qm,
793 struct dfx_diff_registers *dregs, int reg_len)
794 {
795 int i;
796
797 if (!dregs)
798 return;
799
800 /* Setting the pointer is NULL to prevent double free */
801 for (i = 0; i < reg_len; i++) {
802 if (!dregs[i].regs)
803 continue;
804
805 kfree(dregs[i].regs);
806 dregs[i].regs = NULL;
807 }
808 kfree(dregs);
809 }
810
dfx_regs_init(struct hisi_qm * qm,const struct dfx_diff_registers * cregs,u32 reg_len)811 static struct dfx_diff_registers *dfx_regs_init(struct hisi_qm *qm,
812 const struct dfx_diff_registers *cregs, u32 reg_len)
813 {
814 struct dfx_diff_registers *diff_regs;
815 u32 j, base_offset;
816 int i;
817
818 diff_regs = kcalloc(reg_len, sizeof(*diff_regs), GFP_KERNEL);
819 if (!diff_regs)
820 return ERR_PTR(-ENOMEM);
821
822 for (i = 0; i < reg_len; i++) {
823 if (!cregs[i].reg_len)
824 continue;
825
826 diff_regs[i].reg_offset = cregs[i].reg_offset;
827 diff_regs[i].reg_len = cregs[i].reg_len;
828 diff_regs[i].regs = kcalloc(QM_DFX_REGS_LEN, cregs[i].reg_len,
829 GFP_KERNEL);
830 if (!diff_regs[i].regs)
831 goto alloc_error;
832
833 for (j = 0; j < diff_regs[i].reg_len; j++) {
834 base_offset = diff_regs[i].reg_offset +
835 j * QM_DFX_REGS_LEN;
836 diff_regs[i].regs[j] = readl(qm->io_base + base_offset);
837 }
838 }
839
840 return diff_regs;
841
842 alloc_error:
843 while (i > 0) {
844 i--;
845 kfree(diff_regs[i].regs);
846 }
847 kfree(diff_regs);
848 return ERR_PTR(-ENOMEM);
849 }
850
qm_diff_regs_init(struct hisi_qm * qm,struct dfx_diff_registers * dregs,u32 reg_len)851 static int qm_diff_regs_init(struct hisi_qm *qm,
852 struct dfx_diff_registers *dregs, u32 reg_len)
853 {
854 int ret;
855
856 qm->debug.qm_diff_regs = dfx_regs_init(qm, qm_diff_regs, ARRAY_SIZE(qm_diff_regs));
857 if (IS_ERR(qm->debug.qm_diff_regs)) {
858 ret = PTR_ERR(qm->debug.qm_diff_regs);
859 qm->debug.qm_diff_regs = NULL;
860 return ret;
861 }
862
863 qm->debug.acc_diff_regs = dfx_regs_init(qm, dregs, reg_len);
864 if (IS_ERR(qm->debug.acc_diff_regs)) {
865 dfx_regs_uninit(qm, qm->debug.qm_diff_regs, ARRAY_SIZE(qm_diff_regs));
866 ret = PTR_ERR(qm->debug.acc_diff_regs);
867 qm->debug.acc_diff_regs = NULL;
868 return ret;
869 }
870
871 return 0;
872 }
873
qm_last_regs_uninit(struct hisi_qm * qm)874 static void qm_last_regs_uninit(struct hisi_qm *qm)
875 {
876 struct qm_debug *debug = &qm->debug;
877
878 if (qm->fun_type == QM_HW_VF || !debug->qm_last_words)
879 return;
880
881 kfree(debug->qm_last_words);
882 debug->qm_last_words = NULL;
883 }
884
qm_last_regs_init(struct hisi_qm * qm)885 static int qm_last_regs_init(struct hisi_qm *qm)
886 {
887 int dfx_regs_num = ARRAY_SIZE(qm_dfx_regs);
888 struct qm_debug *debug = &qm->debug;
889 int i;
890
891 if (qm->fun_type == QM_HW_VF)
892 return 0;
893
894 debug->qm_last_words = kcalloc(dfx_regs_num, sizeof(unsigned int), GFP_KERNEL);
895 if (!debug->qm_last_words)
896 return -ENOMEM;
897
898 for (i = 0; i < dfx_regs_num; i++) {
899 debug->qm_last_words[i] = readl_relaxed(qm->io_base +
900 qm_dfx_regs[i].offset);
901 }
902
903 return 0;
904 }
905
qm_diff_regs_uninit(struct hisi_qm * qm,u32 reg_len)906 static void qm_diff_regs_uninit(struct hisi_qm *qm, u32 reg_len)
907 {
908 dfx_regs_uninit(qm, qm->debug.acc_diff_regs, reg_len);
909 qm->debug.acc_diff_regs = NULL;
910 dfx_regs_uninit(qm, qm->debug.qm_diff_regs, ARRAY_SIZE(qm_diff_regs));
911 qm->debug.qm_diff_regs = NULL;
912 }
913
914 /**
915 * hisi_qm_regs_debugfs_init() - Allocate memory for registers.
916 * @qm: device qm handle.
917 * @dregs: diff registers handle.
918 * @reg_len: diff registers region length.
919 */
hisi_qm_regs_debugfs_init(struct hisi_qm * qm,struct dfx_diff_registers * dregs,u32 reg_len)920 int hisi_qm_regs_debugfs_init(struct hisi_qm *qm,
921 struct dfx_diff_registers *dregs, u32 reg_len)
922 {
923 int ret;
924
925 if (!qm || !dregs)
926 return -EINVAL;
927
928 if (qm->fun_type != QM_HW_PF)
929 return 0;
930
931 ret = qm_last_regs_init(qm);
932 if (ret) {
933 dev_info(&qm->pdev->dev, "failed to init qm words memory!\n");
934 return ret;
935 }
936
937 ret = qm_diff_regs_init(qm, dregs, reg_len);
938 if (ret) {
939 qm_last_regs_uninit(qm);
940 return ret;
941 }
942
943 return 0;
944 }
945 EXPORT_SYMBOL_GPL(hisi_qm_regs_debugfs_init);
946
947 /**
948 * hisi_qm_regs_debugfs_uninit() - Free memory for registers.
949 * @qm: device qm handle.
950 * @reg_len: diff registers region length.
951 */
hisi_qm_regs_debugfs_uninit(struct hisi_qm * qm,u32 reg_len)952 void hisi_qm_regs_debugfs_uninit(struct hisi_qm *qm, u32 reg_len)
953 {
954 if (!qm || qm->fun_type != QM_HW_PF)
955 return;
956
957 qm_diff_regs_uninit(qm, reg_len);
958 qm_last_regs_uninit(qm);
959 }
960 EXPORT_SYMBOL_GPL(hisi_qm_regs_debugfs_uninit);
961
962 /**
963 * hisi_qm_acc_diff_regs_dump() - Dump registers's value.
964 * @qm: device qm handle.
965 * @s: Debugfs file handle.
966 * @dregs: diff registers handle.
967 * @regs_len: diff registers region length.
968 */
hisi_qm_acc_diff_regs_dump(struct hisi_qm * qm,struct seq_file * s,struct dfx_diff_registers * dregs,u32 regs_len)969 void hisi_qm_acc_diff_regs_dump(struct hisi_qm *qm, struct seq_file *s,
970 struct dfx_diff_registers *dregs, u32 regs_len)
971 {
972 u32 j, val, base_offset;
973 int i, ret;
974
975 if (!qm || !s || !dregs)
976 return;
977
978 ret = hisi_qm_get_dfx_access(qm);
979 if (ret)
980 return;
981
982 down_read(&qm->qps_lock);
983 for (i = 0; i < regs_len; i++) {
984 if (!dregs[i].reg_len)
985 continue;
986
987 for (j = 0; j < dregs[i].reg_len; j++) {
988 base_offset = dregs[i].reg_offset + j * QM_DFX_REGS_LEN;
989 val = readl(qm->io_base + base_offset);
990 if (val != dregs[i].regs[j])
991 seq_printf(s, "0x%08x = 0x%08x ---> 0x%08x\n",
992 base_offset, dregs[i].regs[j], val);
993 }
994 }
995 up_read(&qm->qps_lock);
996
997 hisi_qm_put_dfx_access(qm);
998 }
999 EXPORT_SYMBOL_GPL(hisi_qm_acc_diff_regs_dump);
1000
hisi_qm_show_last_dfx_regs(struct hisi_qm * qm)1001 void hisi_qm_show_last_dfx_regs(struct hisi_qm *qm)
1002 {
1003 struct qm_debug *debug = &qm->debug;
1004 struct pci_dev *pdev = qm->pdev;
1005 u32 val;
1006 int i;
1007
1008 if (qm->fun_type == QM_HW_VF || !debug->qm_last_words)
1009 return;
1010
1011 for (i = 0; i < ARRAY_SIZE(qm_dfx_regs); i++) {
1012 val = readl_relaxed(qm->io_base + qm_dfx_regs[i].offset);
1013 if (debug->qm_last_words[i] != val)
1014 pci_info(pdev, "%s \t= 0x%08x => 0x%08x\n",
1015 qm_dfx_regs[i].name, debug->qm_last_words[i], val);
1016 }
1017 }
1018
qm_diff_regs_show(struct seq_file * s,void * unused)1019 static int qm_diff_regs_show(struct seq_file *s, void *unused)
1020 {
1021 struct hisi_qm *qm = s->private;
1022
1023 hisi_qm_acc_diff_regs_dump(qm, s, qm->debug.qm_diff_regs,
1024 ARRAY_SIZE(qm_diff_regs));
1025
1026 return 0;
1027 }
1028 DEFINE_SHOW_ATTRIBUTE(qm_diff_regs);
1029
qm_status_read(struct file * filp,char __user * buffer,size_t count,loff_t * pos)1030 static ssize_t qm_status_read(struct file *filp, char __user *buffer,
1031 size_t count, loff_t *pos)
1032 {
1033 struct hisi_qm *qm = filp->private_data;
1034 char buf[QM_DBG_READ_LEN];
1035 int val, len;
1036
1037 val = atomic_read(&qm->status.flags);
1038 len = scnprintf(buf, QM_DBG_READ_LEN, "%s\n", qm_s[val]);
1039
1040 return simple_read_from_buffer(buffer, count, pos, buf, len);
1041 }
1042
1043 static const struct file_operations qm_status_fops = {
1044 .owner = THIS_MODULE,
1045 .open = simple_open,
1046 .read = qm_status_read,
1047 };
1048
qm_create_debugfs_file(struct hisi_qm * qm,struct dentry * dir,enum qm_debug_file index)1049 static void qm_create_debugfs_file(struct hisi_qm *qm, struct dentry *dir,
1050 enum qm_debug_file index)
1051 {
1052 struct debugfs_file *file = qm->debug.files + index;
1053
1054 debugfs_create_file(qm_debug_file_name[index], 0600, dir, file,
1055 &qm_debug_fops);
1056
1057 file->index = index;
1058 mutex_init(&file->lock);
1059 file->debug = &qm->debug;
1060 }
1061
qm_debugfs_atomic64_set(void * data,u64 val)1062 static int qm_debugfs_atomic64_set(void *data, u64 val)
1063 {
1064 if (val)
1065 return -EINVAL;
1066
1067 atomic64_set((atomic64_t *)data, 0);
1068
1069 return 0;
1070 }
1071
qm_debugfs_atomic64_get(void * data,u64 * val)1072 static int qm_debugfs_atomic64_get(void *data, u64 *val)
1073 {
1074 *val = atomic64_read((atomic64_t *)data);
1075
1076 return 0;
1077 }
1078
1079 DEFINE_DEBUGFS_ATTRIBUTE(qm_atomic64_ops, qm_debugfs_atomic64_get,
1080 qm_debugfs_atomic64_set, "%llu\n");
1081
1082 /**
1083 * hisi_qm_debug_init() - Initialize qm related debugfs files.
1084 * @qm: The qm for which we want to add debugfs files.
1085 *
1086 * Create qm related debugfs files.
1087 */
hisi_qm_debug_init(struct hisi_qm * qm)1088 void hisi_qm_debug_init(struct hisi_qm *qm)
1089 {
1090 struct dfx_diff_registers *qm_regs = qm->debug.qm_diff_regs;
1091 struct qm_dfx *dfx = &qm->debug.dfx;
1092 struct dentry *qm_d;
1093 void *data;
1094 int i;
1095
1096 qm_d = debugfs_create_dir("qm", qm->debug.debug_root);
1097 qm->debug.qm_d = qm_d;
1098
1099 /* only show this in PF */
1100 if (qm->fun_type == QM_HW_PF) {
1101 qm_create_debugfs_file(qm, qm->debug.debug_root, CURRENT_QM);
1102 for (i = CURRENT_Q; i < DEBUG_FILE_NUM; i++)
1103 qm_create_debugfs_file(qm, qm->debug.qm_d, i);
1104 }
1105
1106 if (qm_regs)
1107 debugfs_create_file("diff_regs", 0444, qm->debug.qm_d,
1108 qm, &qm_diff_regs_fops);
1109
1110 debugfs_create_file("regs", 0444, qm->debug.qm_d, qm, &qm_regs_fops);
1111
1112 debugfs_create_file("cmd", 0600, qm->debug.qm_d, qm, &qm_cmd_fops);
1113
1114 debugfs_create_file("status", 0444, qm->debug.qm_d, qm,
1115 &qm_status_fops);
1116 for (i = 0; i < ARRAY_SIZE(qm_dfx_files); i++) {
1117 data = (atomic64_t *)((uintptr_t)dfx + qm_dfx_files[i].offset);
1118 debugfs_create_file(qm_dfx_files[i].name,
1119 0644,
1120 qm_d,
1121 data,
1122 &qm_atomic64_ops);
1123 }
1124
1125 if (test_bit(QM_SUPPORT_FUNC_QOS, &qm->caps))
1126 hisi_qm_set_algqos_init(qm);
1127 }
1128 EXPORT_SYMBOL_GPL(hisi_qm_debug_init);
1129
1130 /**
1131 * hisi_qm_debug_regs_clear() - clear qm debug related registers.
1132 * @qm: The qm for which we want to clear its debug registers.
1133 */
hisi_qm_debug_regs_clear(struct hisi_qm * qm)1134 void hisi_qm_debug_regs_clear(struct hisi_qm *qm)
1135 {
1136 const struct debugfs_reg32 *regs;
1137 int i;
1138
1139 /* clear current_qm */
1140 writel(0x0, qm->io_base + QM_DFX_MB_CNT_VF);
1141 writel(0x0, qm->io_base + QM_DFX_DB_CNT_VF);
1142
1143 /* clear current_q */
1144 writel(0x0, qm->io_base + QM_DFX_SQE_CNT_VF_SQN);
1145 writel(0x0, qm->io_base + QM_DFX_CQE_CNT_VF_CQN);
1146
1147 /*
1148 * these registers are reading and clearing, so clear them after
1149 * reading them.
1150 */
1151 writel(0x1, qm->io_base + QM_DFX_CNT_CLR_CE);
1152
1153 regs = qm_dfx_regs;
1154 for (i = 0; i < CNT_CYC_REGS_NUM; i++) {
1155 readl(qm->io_base + regs->offset);
1156 regs++;
1157 }
1158
1159 /* clear clear_enable */
1160 writel(0x0, qm->io_base + QM_DFX_CNT_CLR_CE);
1161 }
1162 EXPORT_SYMBOL_GPL(hisi_qm_debug_regs_clear);
1163