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