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
28static const char * const qm_debug_file_name[] = {
29	[CURRENT_QM]   = "current_qm",
30	[CURRENT_Q]    = "current_q",
31	[CLEAR_ENABLE] = "clear_enable",
32};
33
34struct qm_dfx_item {
35	const char *name;
36	u32 offset;
37};
38
39struct 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
45static 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
54static 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
82static 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 */
87static 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
103static 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
110static 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
122static 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
137static 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
173free_ctx:
174	hisi_qm_ctx_free(qm, sizeof(*sqc), sqc, &sqc_dma);
175	return 0;
176}
177
178static 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
214free_ctx:
215	hisi_qm_ctx_free(qm, sizeof(*cqc), cqc, &cqc_dma);
216	return 0;
217}
218
219static 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
251err_free_ctx:
252	hisi_qm_ctx_free(qm, size, xeqc, &xeqc_dma);
253	return ret;
254}
255
256static 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
296static 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
325static 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
343static 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
385err_unlock:
386	up_read(&qm->qps_lock);
387	return ret;
388}
389
390static 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
412static 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
448static 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
484err_buffer_free:
485	kfree(s_tmp);
486
487	return ret;
488}
489
490static 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
537put_dfx_access:
538	hisi_qm_put_dfx_access(qm);
539	return ret;
540}
541
542static 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 */
556void 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}
576EXPORT_SYMBOL_GPL(hisi_qm_regs_dump);
577
578static 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
599DEFINE_SHOW_ATTRIBUTE(qm_regs);
600
601static 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
606static 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
624static 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 */
630static 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
640static u32 current_qm_read(struct hisi_qm *qm)
641{
642	return readl(qm->io_base + QM_DFX_MB_CNT_VF);
643}
644
645static 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
665static 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
692static 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
726err_input:
727	mutex_unlock(&file->lock);
728	hisi_qm_put_dfx_access(qm);
729	return -EINVAL;
730}
731
732static 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
785static 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
792static 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
805static 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
836alloc_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
845static 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
861static 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
872static 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
893static 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 */
905int 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}
930EXPORT_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 */
937void 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}
945EXPORT_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 */
954void 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}
984EXPORT_SYMBOL_GPL(hisi_qm_acc_diff_regs_dump);
985
986void 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
1004static 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}
1013DEFINE_SHOW_ATTRIBUTE(qm_diff_regs);
1014
1015static 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
1028static const struct file_operations qm_status_fops = {
1029	.owner = THIS_MODULE,
1030	.open = simple_open,
1031	.read = qm_status_read,
1032};
1033
1034static 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
1047static 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
1057static int qm_debugfs_atomic64_get(void *data, u64 *val)
1058{
1059	*val = atomic64_read((atomic64_t *)data);
1060
1061	return 0;
1062}
1063
1064DEFINE_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 */
1073void 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}
1113EXPORT_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 */
1119void 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}
1147EXPORT_SYMBOL_GPL(hisi_qm_debug_regs_clear);
1148