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 /* Setting the pointer is NULL to prevent double free */
798 for (i = 0; i < reg_len; i++) {
799 kfree(dregs[i].regs);
800 dregs[i].regs = NULL;
801 }
802 kfree(dregs);
803 }
804
dfx_regs_init(struct hisi_qm * qm,const struct dfx_diff_registers * cregs,u32 reg_len)805 static struct dfx_diff_registers *dfx_regs_init(struct hisi_qm *qm,
806 const struct dfx_diff_registers *cregs, u32 reg_len)
807 {
808 struct dfx_diff_registers *diff_regs;
809 u32 j, base_offset;
810 int i;
811
812 diff_regs = kcalloc(reg_len, sizeof(*diff_regs), GFP_KERNEL);
813 if (!diff_regs)
814 return ERR_PTR(-ENOMEM);
815
816 for (i = 0; i < reg_len; i++) {
817 if (!cregs[i].reg_len)
818 continue;
819
820 diff_regs[i].reg_offset = cregs[i].reg_offset;
821 diff_regs[i].reg_len = cregs[i].reg_len;
822 diff_regs[i].regs = kcalloc(QM_DFX_REGS_LEN, cregs[i].reg_len,
823 GFP_KERNEL);
824 if (!diff_regs[i].regs)
825 goto alloc_error;
826
827 for (j = 0; j < diff_regs[i].reg_len; j++) {
828 base_offset = diff_regs[i].reg_offset +
829 j * QM_DFX_REGS_LEN;
830 diff_regs[i].regs[j] = readl(qm->io_base + base_offset);
831 }
832 }
833
834 return diff_regs;
835
836 alloc_error:
837 while (i > 0) {
838 i--;
839 kfree(diff_regs[i].regs);
840 }
841 kfree(diff_regs);
842 return ERR_PTR(-ENOMEM);
843 }
844
qm_diff_regs_init(struct hisi_qm * qm,struct dfx_diff_registers * dregs,u32 reg_len)845 static int qm_diff_regs_init(struct hisi_qm *qm,
846 struct dfx_diff_registers *dregs, u32 reg_len)
847 {
848 qm->debug.qm_diff_regs = dfx_regs_init(qm, qm_diff_regs, ARRAY_SIZE(qm_diff_regs));
849 if (IS_ERR(qm->debug.qm_diff_regs))
850 return PTR_ERR(qm->debug.qm_diff_regs);
851
852 qm->debug.acc_diff_regs = dfx_regs_init(qm, dregs, reg_len);
853 if (IS_ERR(qm->debug.acc_diff_regs)) {
854 dfx_regs_uninit(qm, qm->debug.qm_diff_regs, ARRAY_SIZE(qm_diff_regs));
855 return PTR_ERR(qm->debug.acc_diff_regs);
856 }
857
858 return 0;
859 }
860
qm_last_regs_uninit(struct hisi_qm * qm)861 static void qm_last_regs_uninit(struct hisi_qm *qm)
862 {
863 struct qm_debug *debug = &qm->debug;
864
865 if (qm->fun_type == QM_HW_VF || !debug->qm_last_words)
866 return;
867
868 kfree(debug->qm_last_words);
869 debug->qm_last_words = NULL;
870 }
871
qm_last_regs_init(struct hisi_qm * qm)872 static int qm_last_regs_init(struct hisi_qm *qm)
873 {
874 int dfx_regs_num = ARRAY_SIZE(qm_dfx_regs);
875 struct qm_debug *debug = &qm->debug;
876 int i;
877
878 if (qm->fun_type == QM_HW_VF)
879 return 0;
880
881 debug->qm_last_words = kcalloc(dfx_regs_num, sizeof(unsigned int), GFP_KERNEL);
882 if (!debug->qm_last_words)
883 return -ENOMEM;
884
885 for (i = 0; i < dfx_regs_num; i++) {
886 debug->qm_last_words[i] = readl_relaxed(qm->io_base +
887 qm_dfx_regs[i].offset);
888 }
889
890 return 0;
891 }
892
qm_diff_regs_uninit(struct hisi_qm * qm,u32 reg_len)893 static void qm_diff_regs_uninit(struct hisi_qm *qm, u32 reg_len)
894 {
895 dfx_regs_uninit(qm, qm->debug.acc_diff_regs, reg_len);
896 dfx_regs_uninit(qm, qm->debug.qm_diff_regs, ARRAY_SIZE(qm_diff_regs));
897 }
898
899 /**
900 * hisi_qm_regs_debugfs_init() - Allocate memory for registers.
901 * @qm: device qm handle.
902 * @dregs: diff registers handle.
903 * @reg_len: diff registers region length.
904 */
hisi_qm_regs_debugfs_init(struct hisi_qm * qm,struct dfx_diff_registers * dregs,u32 reg_len)905 int hisi_qm_regs_debugfs_init(struct hisi_qm *qm,
906 struct dfx_diff_registers *dregs, u32 reg_len)
907 {
908 int ret;
909
910 if (!qm || !dregs)
911 return -EINVAL;
912
913 if (qm->fun_type != QM_HW_PF)
914 return 0;
915
916 ret = qm_last_regs_init(qm);
917 if (ret) {
918 dev_info(&qm->pdev->dev, "failed to init qm words memory!\n");
919 return ret;
920 }
921
922 ret = qm_diff_regs_init(qm, dregs, reg_len);
923 if (ret) {
924 qm_last_regs_uninit(qm);
925 return ret;
926 }
927
928 return 0;
929 }
930 EXPORT_SYMBOL_GPL(hisi_qm_regs_debugfs_init);
931
932 /**
933 * hisi_qm_regs_debugfs_uninit() - Free memory for registers.
934 * @qm: device qm handle.
935 * @reg_len: diff registers region length.
936 */
hisi_qm_regs_debugfs_uninit(struct hisi_qm * qm,u32 reg_len)937 void hisi_qm_regs_debugfs_uninit(struct hisi_qm *qm, u32 reg_len)
938 {
939 if (!qm || qm->fun_type != QM_HW_PF)
940 return;
941
942 qm_diff_regs_uninit(qm, reg_len);
943 qm_last_regs_uninit(qm);
944 }
945 EXPORT_SYMBOL_GPL(hisi_qm_regs_debugfs_uninit);
946
947 /**
948 * hisi_qm_acc_diff_regs_dump() - Dump registers's value.
949 * @qm: device qm handle.
950 * @s: Debugfs file handle.
951 * @dregs: diff registers handle.
952 * @regs_len: diff registers region length.
953 */
hisi_qm_acc_diff_regs_dump(struct hisi_qm * qm,struct seq_file * s,struct dfx_diff_registers * dregs,u32 regs_len)954 void hisi_qm_acc_diff_regs_dump(struct hisi_qm *qm, struct seq_file *s,
955 struct dfx_diff_registers *dregs, u32 regs_len)
956 {
957 u32 j, val, base_offset;
958 int i, ret;
959
960 if (!qm || !s || !dregs)
961 return;
962
963 ret = hisi_qm_get_dfx_access(qm);
964 if (ret)
965 return;
966
967 down_read(&qm->qps_lock);
968 for (i = 0; i < regs_len; i++) {
969 if (!dregs[i].reg_len)
970 continue;
971
972 for (j = 0; j < dregs[i].reg_len; j++) {
973 base_offset = dregs[i].reg_offset + j * QM_DFX_REGS_LEN;
974 val = readl(qm->io_base + base_offset);
975 if (val != dregs[i].regs[j])
976 seq_printf(s, "0x%08x = 0x%08x ---> 0x%08x\n",
977 base_offset, dregs[i].regs[j], val);
978 }
979 }
980 up_read(&qm->qps_lock);
981
982 hisi_qm_put_dfx_access(qm);
983 }
984 EXPORT_SYMBOL_GPL(hisi_qm_acc_diff_regs_dump);
985
hisi_qm_show_last_dfx_regs(struct hisi_qm * qm)986 void hisi_qm_show_last_dfx_regs(struct hisi_qm *qm)
987 {
988 struct qm_debug *debug = &qm->debug;
989 struct pci_dev *pdev = qm->pdev;
990 u32 val;
991 int i;
992
993 if (qm->fun_type == QM_HW_VF || !debug->qm_last_words)
994 return;
995
996 for (i = 0; i < ARRAY_SIZE(qm_dfx_regs); i++) {
997 val = readl_relaxed(qm->io_base + qm_dfx_regs[i].offset);
998 if (debug->qm_last_words[i] != val)
999 pci_info(pdev, "%s \t= 0x%08x => 0x%08x\n",
1000 qm_dfx_regs[i].name, debug->qm_last_words[i], val);
1001 }
1002 }
1003
qm_diff_regs_show(struct seq_file * s,void * unused)1004 static int qm_diff_regs_show(struct seq_file *s, void *unused)
1005 {
1006 struct hisi_qm *qm = s->private;
1007
1008 hisi_qm_acc_diff_regs_dump(qm, s, qm->debug.qm_diff_regs,
1009 ARRAY_SIZE(qm_diff_regs));
1010
1011 return 0;
1012 }
1013 DEFINE_SHOW_ATTRIBUTE(qm_diff_regs);
1014
qm_status_read(struct file * filp,char __user * buffer,size_t count,loff_t * pos)1015 static ssize_t qm_status_read(struct file *filp, char __user *buffer,
1016 size_t count, loff_t *pos)
1017 {
1018 struct hisi_qm *qm = filp->private_data;
1019 char buf[QM_DBG_READ_LEN];
1020 int val, len;
1021
1022 val = atomic_read(&qm->status.flags);
1023 len = scnprintf(buf, QM_DBG_READ_LEN, "%s\n", qm_s[val]);
1024
1025 return simple_read_from_buffer(buffer, count, pos, buf, len);
1026 }
1027
1028 static const struct file_operations qm_status_fops = {
1029 .owner = THIS_MODULE,
1030 .open = simple_open,
1031 .read = qm_status_read,
1032 };
1033
qm_create_debugfs_file(struct hisi_qm * qm,struct dentry * dir,enum qm_debug_file index)1034 static void qm_create_debugfs_file(struct hisi_qm *qm, struct dentry *dir,
1035 enum qm_debug_file index)
1036 {
1037 struct debugfs_file *file = qm->debug.files + index;
1038
1039 debugfs_create_file(qm_debug_file_name[index], 0600, dir, file,
1040 &qm_debug_fops);
1041
1042 file->index = index;
1043 mutex_init(&file->lock);
1044 file->debug = &qm->debug;
1045 }
1046
qm_debugfs_atomic64_set(void * data,u64 val)1047 static int qm_debugfs_atomic64_set(void *data, u64 val)
1048 {
1049 if (val)
1050 return -EINVAL;
1051
1052 atomic64_set((atomic64_t *)data, 0);
1053
1054 return 0;
1055 }
1056
qm_debugfs_atomic64_get(void * data,u64 * val)1057 static int qm_debugfs_atomic64_get(void *data, u64 *val)
1058 {
1059 *val = atomic64_read((atomic64_t *)data);
1060
1061 return 0;
1062 }
1063
1064 DEFINE_DEBUGFS_ATTRIBUTE(qm_atomic64_ops, qm_debugfs_atomic64_get,
1065 qm_debugfs_atomic64_set, "%llu\n");
1066
1067 /**
1068 * hisi_qm_debug_init() - Initialize qm related debugfs files.
1069 * @qm: The qm for which we want to add debugfs files.
1070 *
1071 * Create qm related debugfs files.
1072 */
hisi_qm_debug_init(struct hisi_qm * qm)1073 void hisi_qm_debug_init(struct hisi_qm *qm)
1074 {
1075 struct dfx_diff_registers *qm_regs = qm->debug.qm_diff_regs;
1076 struct qm_dfx *dfx = &qm->debug.dfx;
1077 struct dentry *qm_d;
1078 void *data;
1079 int i;
1080
1081 qm_d = debugfs_create_dir("qm", qm->debug.debug_root);
1082 qm->debug.qm_d = qm_d;
1083
1084 /* only show this in PF */
1085 if (qm->fun_type == QM_HW_PF) {
1086 qm_create_debugfs_file(qm, qm->debug.debug_root, CURRENT_QM);
1087 for (i = CURRENT_Q; i < DEBUG_FILE_NUM; i++)
1088 qm_create_debugfs_file(qm, qm->debug.qm_d, i);
1089 }
1090
1091 if (qm_regs)
1092 debugfs_create_file("diff_regs", 0444, qm->debug.qm_d,
1093 qm, &qm_diff_regs_fops);
1094
1095 debugfs_create_file("regs", 0444, qm->debug.qm_d, qm, &qm_regs_fops);
1096
1097 debugfs_create_file("cmd", 0600, qm->debug.qm_d, qm, &qm_cmd_fops);
1098
1099 debugfs_create_file("status", 0444, qm->debug.qm_d, qm,
1100 &qm_status_fops);
1101 for (i = 0; i < ARRAY_SIZE(qm_dfx_files); i++) {
1102 data = (atomic64_t *)((uintptr_t)dfx + qm_dfx_files[i].offset);
1103 debugfs_create_file(qm_dfx_files[i].name,
1104 0644,
1105 qm_d,
1106 data,
1107 &qm_atomic64_ops);
1108 }
1109
1110 if (test_bit(QM_SUPPORT_FUNC_QOS, &qm->caps))
1111 hisi_qm_set_algqos_init(qm);
1112 }
1113 EXPORT_SYMBOL_GPL(hisi_qm_debug_init);
1114
1115 /**
1116 * hisi_qm_debug_regs_clear() - clear qm debug related registers.
1117 * @qm: The qm for which we want to clear its debug registers.
1118 */
hisi_qm_debug_regs_clear(struct hisi_qm * qm)1119 void hisi_qm_debug_regs_clear(struct hisi_qm *qm)
1120 {
1121 const struct debugfs_reg32 *regs;
1122 int i;
1123
1124 /* clear current_qm */
1125 writel(0x0, qm->io_base + QM_DFX_MB_CNT_VF);
1126 writel(0x0, qm->io_base + QM_DFX_DB_CNT_VF);
1127
1128 /* clear current_q */
1129 writel(0x0, qm->io_base + QM_DFX_SQE_CNT_VF_SQN);
1130 writel(0x0, qm->io_base + QM_DFX_CQE_CNT_VF_CQN);
1131
1132 /*
1133 * these registers are reading and clearing, so clear them after
1134 * reading them.
1135 */
1136 writel(0x1, qm->io_base + QM_DFX_CNT_CLR_CE);
1137
1138 regs = qm_dfx_regs;
1139 for (i = 0; i < CNT_CYC_REGS_NUM; i++) {
1140 readl(qm->io_base + regs->offset);
1141 regs++;
1142 }
1143
1144 /* clear clear_enable */
1145 writel(0x0, qm->io_base + QM_DFX_CNT_CLR_CE);
1146 }
1147 EXPORT_SYMBOL_GPL(hisi_qm_debug_regs_clear);
1148