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