Linux Audio

Check our new training course

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