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, &regset);
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