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, fmt: "%s\n" , |
147 | "Please echo help to cmd to get help information" ); |
148 | |
149 | return simple_read_from_buffer(to: buffer, count, ppos: pos, from: buf, available: 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, base: 0, res: &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, xqc: &sqc, qp_id, op: 1); |
185 | if (!ret) { |
186 | dump_show(qm, info: &sqc, info_size: sizeof(struct qm_sqc), info_name: name); |
187 | |
188 | return 0; |
189 | } |
190 | |
191 | down_read(sem: &qm->qps_lock); |
192 | if (qm->sqc) { |
193 | sqc_curr = qm->sqc + qp_id; |
194 | |
195 | dump_show(qm, info: sqc_curr, info_size: sizeof(*sqc_curr), info_name: "SOFT SQC" ); |
196 | } |
197 | up_read(sem: &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, base: 0, res: &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, xqc: &cqc, qp_id, op: 1); |
220 | if (!ret) { |
221 | dump_show(qm, info: &cqc, info_size: sizeof(struct qm_cqc), info_name: name); |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | down_read(sem: &qm->qps_lock); |
227 | if (qm->cqc) { |
228 | cqc_curr = qm->cqc + qp_id; |
229 | |
230 | dump_show(qm, info: cqc_curr, info_size: sizeof(*cqc_curr), info_name: "SOFT CQC" ); |
231 | } |
232 | up_read(sem: &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, xqc: xeqc, qp_id: 0, op: 1); |
263 | if (ret) |
264 | return ret; |
265 | |
266 | dump_show(qm, info: xeqc, info_size: size, info_name: 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(s: presult, base: 0, res: 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(s: presult, base: 0, res: 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, *sqe_curr; |
315 | struct hisi_qp *qp; |
316 | u32 qp_id, sqe_id; |
317 | int ret; |
318 | |
319 | ret = q_dump_param_parse(qm, s, e_id: &sqe_id, q_id: &qp_id, q_depth: sq_depth); |
320 | if (ret) |
321 | return ret; |
322 | |
323 | sqe = kzalloc(size: qm->sqe_size * sq_depth, GFP_KERNEL); |
324 | if (!sqe) |
325 | return -ENOMEM; |
326 | |
327 | qp = &qm->qp_array[qp_id]; |
328 | memcpy(sqe, qp->sqe, qm->sqe_size * sq_depth); |
329 | sqe_curr = sqe + (u32)(sqe_id * qm->sqe_size); |
330 | memset(sqe_curr + qm->debug.sqe_mask_offset, QM_SQE_ADDR_MASK, |
331 | qm->debug.sqe_mask_len); |
332 | |
333 | dump_show(qm, info: sqe_curr, info_size: qm->sqe_size, info_name: name); |
334 | |
335 | kfree(objp: sqe); |
336 | |
337 | return 0; |
338 | } |
339 | |
340 | static int qm_cq_dump(struct hisi_qm *qm, char *s, char *name) |
341 | { |
342 | struct qm_cqe *cqe_curr; |
343 | struct hisi_qp *qp; |
344 | u32 qp_id, cqe_id; |
345 | int ret; |
346 | |
347 | ret = q_dump_param_parse(qm, s, e_id: &cqe_id, q_id: &qp_id, q_depth: qm->qp_array->cq_depth); |
348 | if (ret) |
349 | return ret; |
350 | |
351 | qp = &qm->qp_array[qp_id]; |
352 | cqe_curr = qp->cqe + cqe_id; |
353 | dump_show(qm, info: cqe_curr, info_size: sizeof(struct qm_cqe), info_name: name); |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | static int qm_eq_aeq_dump(struct hisi_qm *qm, char *s, char *name) |
359 | { |
360 | struct device *dev = &qm->pdev->dev; |
361 | u16 xeq_depth; |
362 | size_t size; |
363 | void *xeqe; |
364 | u32 xeqe_id; |
365 | int ret; |
366 | |
367 | if (!s) |
368 | return -EINVAL; |
369 | |
370 | ret = kstrtou32(s, base: 0, res: &xeqe_id); |
371 | if (ret) |
372 | return -EINVAL; |
373 | |
374 | if (!strcmp(name, "EQE" )) { |
375 | xeq_depth = qm->eq_depth; |
376 | size = sizeof(struct qm_eqe); |
377 | } else { |
378 | xeq_depth = qm->aeq_depth; |
379 | size = sizeof(struct qm_aeqe); |
380 | } |
381 | |
382 | if (xeqe_id >= xeq_depth) { |
383 | dev_err(dev, "Please input eqe or aeqe num (0-%u)" , xeq_depth - 1); |
384 | return -EINVAL; |
385 | } |
386 | |
387 | down_read(sem: &qm->qps_lock); |
388 | |
389 | if (qm->eqe && !strcmp(name, "EQE" )) { |
390 | xeqe = qm->eqe + xeqe_id; |
391 | } else if (qm->aeqe && !strcmp(name, "AEQE" )) { |
392 | xeqe = qm->aeqe + xeqe_id; |
393 | } else { |
394 | ret = -EINVAL; |
395 | goto err_unlock; |
396 | } |
397 | |
398 | dump_show(qm, info: xeqe, info_size: size, info_name: name); |
399 | |
400 | err_unlock: |
401 | up_read(sem: &qm->qps_lock); |
402 | return ret; |
403 | } |
404 | |
405 | static int qm_dbg_help(struct hisi_qm *qm, char *s) |
406 | { |
407 | struct device *dev = &qm->pdev->dev; |
408 | |
409 | if (strsep(&s, " " )) { |
410 | dev_err(dev, "Please do not input extra characters!\n" ); |
411 | return -EINVAL; |
412 | } |
413 | |
414 | dev_info(dev, "available commands:\n" ); |
415 | dev_info(dev, "sqc <num>\n" ); |
416 | dev_info(dev, "cqc <num>\n" ); |
417 | dev_info(dev, "eqc\n" ); |
418 | dev_info(dev, "aeqc\n" ); |
419 | dev_info(dev, "sq <num> <e>\n" ); |
420 | dev_info(dev, "cq <num> <e>\n" ); |
421 | dev_info(dev, "eq <e>\n" ); |
422 | dev_info(dev, "aeq <e>\n" ); |
423 | |
424 | return 0; |
425 | } |
426 | |
427 | static const struct qm_cmd_dump_item qm_cmd_dump_table[] = { |
428 | { |
429 | .cmd = "sqc" , |
430 | .info_name = "SQC" , |
431 | .dump_fn = qm_sqc_dump, |
432 | }, { |
433 | .cmd = "cqc" , |
434 | .info_name = "CQC" , |
435 | .dump_fn = qm_cqc_dump, |
436 | }, { |
437 | .cmd = "eqc" , |
438 | .info_name = "EQC" , |
439 | .dump_fn = qm_eqc_aeqc_dump, |
440 | }, { |
441 | .cmd = "aeqc" , |
442 | .info_name = "AEQC" , |
443 | .dump_fn = qm_eqc_aeqc_dump, |
444 | }, { |
445 | .cmd = "sq" , |
446 | .info_name = "SQE" , |
447 | .dump_fn = qm_sq_dump, |
448 | }, { |
449 | .cmd = "cq" , |
450 | .info_name = "CQE" , |
451 | .dump_fn = qm_cq_dump, |
452 | }, { |
453 | .cmd = "eq" , |
454 | .info_name = "EQE" , |
455 | .dump_fn = qm_eq_aeq_dump, |
456 | }, { |
457 | .cmd = "aeq" , |
458 | .info_name = "AEQE" , |
459 | .dump_fn = qm_eq_aeq_dump, |
460 | }, |
461 | }; |
462 | |
463 | static int qm_cmd_write_dump(struct hisi_qm *qm, const char *cmd_buf) |
464 | { |
465 | struct device *dev = &qm->pdev->dev; |
466 | char *presult, *s, *s_tmp; |
467 | int table_size, i, ret; |
468 | |
469 | s = kstrdup(s: cmd_buf, GFP_KERNEL); |
470 | if (!s) |
471 | return -ENOMEM; |
472 | |
473 | s_tmp = s; |
474 | presult = strsep(&s, " " ); |
475 | if (!presult) { |
476 | ret = -EINVAL; |
477 | goto err_buffer_free; |
478 | } |
479 | |
480 | if (!strcmp(presult, "help" )) { |
481 | ret = qm_dbg_help(qm, s); |
482 | goto err_buffer_free; |
483 | } |
484 | |
485 | table_size = ARRAY_SIZE(qm_cmd_dump_table); |
486 | for (i = 0; i < table_size; i++) { |
487 | if (!strcmp(presult, qm_cmd_dump_table[i].cmd)) { |
488 | ret = qm_cmd_dump_table[i].dump_fn(qm, s, |
489 | qm_cmd_dump_table[i].info_name); |
490 | break; |
491 | } |
492 | } |
493 | |
494 | if (i == table_size) { |
495 | dev_info(dev, "Please echo help\n" ); |
496 | ret = -EINVAL; |
497 | } |
498 | |
499 | err_buffer_free: |
500 | kfree(objp: s_tmp); |
501 | |
502 | return ret; |
503 | } |
504 | |
505 | static ssize_t qm_cmd_write(struct file *filp, const char __user *buffer, |
506 | size_t count, loff_t *pos) |
507 | { |
508 | struct hisi_qm *qm = filp->private_data; |
509 | char *cmd_buf, *cmd_buf_tmp; |
510 | int ret; |
511 | |
512 | if (*pos) |
513 | return 0; |
514 | |
515 | ret = hisi_qm_get_dfx_access(qm); |
516 | if (ret) |
517 | return ret; |
518 | |
519 | /* Judge if the instance is being reset. */ |
520 | if (unlikely(atomic_read(&qm->status.flags) == QM_STOP)) { |
521 | ret = 0; |
522 | goto put_dfx_access; |
523 | } |
524 | |
525 | if (count > QM_DBG_WRITE_LEN) { |
526 | ret = -ENOSPC; |
527 | goto put_dfx_access; |
528 | } |
529 | |
530 | cmd_buf = memdup_user_nul(buffer, count); |
531 | if (IS_ERR(ptr: cmd_buf)) { |
532 | ret = PTR_ERR(ptr: cmd_buf); |
533 | goto put_dfx_access; |
534 | } |
535 | |
536 | cmd_buf_tmp = strchr(cmd_buf, '\n'); |
537 | if (cmd_buf_tmp) { |
538 | *cmd_buf_tmp = '\0'; |
539 | count = cmd_buf_tmp - cmd_buf + 1; |
540 | } |
541 | |
542 | ret = qm_cmd_write_dump(qm, cmd_buf); |
543 | if (ret) { |
544 | kfree(objp: cmd_buf); |
545 | goto put_dfx_access; |
546 | } |
547 | |
548 | kfree(objp: cmd_buf); |
549 | |
550 | ret = count; |
551 | |
552 | put_dfx_access: |
553 | hisi_qm_put_dfx_access(qm); |
554 | return ret; |
555 | } |
556 | |
557 | static const struct file_operations qm_cmd_fops = { |
558 | .owner = THIS_MODULE, |
559 | .open = simple_open, |
560 | .read = qm_cmd_read, |
561 | .write = qm_cmd_write, |
562 | }; |
563 | |
564 | /** |
565 | * hisi_qm_regs_dump() - Dump registers's value. |
566 | * @s: debugfs file handle. |
567 | * @regset: accelerator registers information. |
568 | * |
569 | * Dump accelerator registers. |
570 | */ |
571 | void hisi_qm_regs_dump(struct seq_file *s, struct debugfs_regset32 *regset) |
572 | { |
573 | struct pci_dev *pdev = to_pci_dev(regset->dev); |
574 | struct hisi_qm *qm = pci_get_drvdata(pdev); |
575 | const struct debugfs_reg32 *regs = regset->regs; |
576 | int regs_len = regset->nregs; |
577 | int i, ret; |
578 | u32 val; |
579 | |
580 | ret = hisi_qm_get_dfx_access(qm); |
581 | if (ret) |
582 | return; |
583 | |
584 | for (i = 0; i < regs_len; i++) { |
585 | val = readl(addr: regset->base + regs[i].offset); |
586 | seq_printf(m: s, fmt: "%s= 0x%08x\n" , regs[i].name, val); |
587 | } |
588 | |
589 | hisi_qm_put_dfx_access(qm); |
590 | } |
591 | EXPORT_SYMBOL_GPL(hisi_qm_regs_dump); |
592 | |
593 | static int qm_regs_show(struct seq_file *s, void *unused) |
594 | { |
595 | struct hisi_qm *qm = s->private; |
596 | struct debugfs_regset32 regset; |
597 | |
598 | if (qm->fun_type == QM_HW_PF) { |
599 | regset.regs = qm_dfx_regs; |
600 | regset.nregs = ARRAY_SIZE(qm_dfx_regs); |
601 | } else { |
602 | regset.regs = qm_vf_dfx_regs; |
603 | regset.nregs = ARRAY_SIZE(qm_vf_dfx_regs); |
604 | } |
605 | |
606 | regset.base = qm->io_base; |
607 | regset.dev = &qm->pdev->dev; |
608 | |
609 | hisi_qm_regs_dump(s, ®set); |
610 | |
611 | return 0; |
612 | } |
613 | |
614 | DEFINE_SHOW_ATTRIBUTE(qm_regs); |
615 | |
616 | static u32 current_q_read(struct hisi_qm *qm) |
617 | { |
618 | return readl(addr: qm->io_base + QM_DFX_SQE_CNT_VF_SQN) >> QM_DFX_QN_SHIFT; |
619 | } |
620 | |
621 | static int current_q_write(struct hisi_qm *qm, u32 val) |
622 | { |
623 | u32 tmp; |
624 | |
625 | if (val >= qm->debug.curr_qm_qp_num) |
626 | return -EINVAL; |
627 | |
628 | tmp = val << QM_DFX_QN_SHIFT | |
629 | (readl(addr: qm->io_base + QM_DFX_SQE_CNT_VF_SQN) & CURRENT_FUN_MASK); |
630 | writel(val: tmp, addr: qm->io_base + QM_DFX_SQE_CNT_VF_SQN); |
631 | |
632 | tmp = val << QM_DFX_QN_SHIFT | |
633 | (readl(addr: qm->io_base + QM_DFX_CQE_CNT_VF_CQN) & CURRENT_FUN_MASK); |
634 | writel(val: tmp, addr: qm->io_base + QM_DFX_CQE_CNT_VF_CQN); |
635 | |
636 | return 0; |
637 | } |
638 | |
639 | static u32 clear_enable_read(struct hisi_qm *qm) |
640 | { |
641 | return readl(addr: qm->io_base + QM_DFX_CNT_CLR_CE); |
642 | } |
643 | |
644 | /* rd_clr_ctrl 1 enable read clear, otherwise 0 disable it */ |
645 | static int clear_enable_write(struct hisi_qm *qm, u32 rd_clr_ctrl) |
646 | { |
647 | if (rd_clr_ctrl > 1) |
648 | return -EINVAL; |
649 | |
650 | writel(val: rd_clr_ctrl, addr: qm->io_base + QM_DFX_CNT_CLR_CE); |
651 | |
652 | return 0; |
653 | } |
654 | |
655 | static u32 current_qm_read(struct hisi_qm *qm) |
656 | { |
657 | return readl(addr: qm->io_base + QM_DFX_MB_CNT_VF); |
658 | } |
659 | |
660 | static int qm_get_vf_qp_num(struct hisi_qm *qm, u32 fun_num) |
661 | { |
662 | u32 remain_q_num, vfq_num; |
663 | u32 num_vfs = qm->vfs_num; |
664 | |
665 | vfq_num = (qm->ctrl_qp_num - qm->qp_num) / num_vfs; |
666 | if (vfq_num >= qm->max_qp_num) |
667 | return qm->max_qp_num; |
668 | |
669 | remain_q_num = (qm->ctrl_qp_num - qm->qp_num) % num_vfs; |
670 | if (vfq_num + remain_q_num <= qm->max_qp_num) |
671 | return fun_num == num_vfs ? vfq_num + remain_q_num : vfq_num; |
672 | |
673 | /* |
674 | * if vfq_num + remain_q_num > max_qp_num, the last VFs, |
675 | * each with one more queue. |
676 | */ |
677 | return fun_num + remain_q_num > num_vfs ? vfq_num + 1 : vfq_num; |
678 | } |
679 | |
680 | static int current_qm_write(struct hisi_qm *qm, u32 val) |
681 | { |
682 | u32 tmp; |
683 | |
684 | if (val > qm->vfs_num) |
685 | return -EINVAL; |
686 | |
687 | /* According PF or VF Dev ID to calculation curr_qm_qp_num and store */ |
688 | if (!val) |
689 | qm->debug.curr_qm_qp_num = qm->qp_num; |
690 | else |
691 | qm->debug.curr_qm_qp_num = qm_get_vf_qp_num(qm, fun_num: val); |
692 | |
693 | writel(val, addr: qm->io_base + QM_DFX_MB_CNT_VF); |
694 | writel(val, addr: qm->io_base + QM_DFX_DB_CNT_VF); |
695 | |
696 | tmp = val | |
697 | (readl(addr: qm->io_base + QM_DFX_SQE_CNT_VF_SQN) & CURRENT_Q_MASK); |
698 | writel(val: tmp, addr: qm->io_base + QM_DFX_SQE_CNT_VF_SQN); |
699 | |
700 | tmp = val | |
701 | (readl(addr: qm->io_base + QM_DFX_CQE_CNT_VF_CQN) & CURRENT_Q_MASK); |
702 | writel(val: tmp, addr: qm->io_base + QM_DFX_CQE_CNT_VF_CQN); |
703 | |
704 | return 0; |
705 | } |
706 | |
707 | static ssize_t qm_debug_read(struct file *filp, char __user *buf, |
708 | size_t count, loff_t *pos) |
709 | { |
710 | struct debugfs_file *file = filp->private_data; |
711 | enum qm_debug_file index = file->index; |
712 | struct hisi_qm *qm = file_to_qm(file); |
713 | char tbuf[QM_DBG_TMP_BUF_LEN]; |
714 | u32 val; |
715 | int ret; |
716 | |
717 | ret = hisi_qm_get_dfx_access(qm); |
718 | if (ret) |
719 | return ret; |
720 | |
721 | mutex_lock(&file->lock); |
722 | switch (index) { |
723 | case CURRENT_QM: |
724 | val = current_qm_read(qm); |
725 | break; |
726 | case CURRENT_Q: |
727 | val = current_q_read(qm); |
728 | break; |
729 | case CLEAR_ENABLE: |
730 | val = clear_enable_read(qm); |
731 | break; |
732 | default: |
733 | goto err_input; |
734 | } |
735 | mutex_unlock(lock: &file->lock); |
736 | |
737 | hisi_qm_put_dfx_access(qm); |
738 | ret = scnprintf(buf: tbuf, QM_DBG_TMP_BUF_LEN, fmt: "%u\n" , val); |
739 | return simple_read_from_buffer(to: buf, count, ppos: pos, from: tbuf, available: ret); |
740 | |
741 | err_input: |
742 | mutex_unlock(lock: &file->lock); |
743 | hisi_qm_put_dfx_access(qm); |
744 | return -EINVAL; |
745 | } |
746 | |
747 | static ssize_t qm_debug_write(struct file *filp, const char __user *buf, |
748 | size_t count, loff_t *pos) |
749 | { |
750 | struct debugfs_file *file = filp->private_data; |
751 | enum qm_debug_file index = file->index; |
752 | struct hisi_qm *qm = file_to_qm(file); |
753 | unsigned long val; |
754 | char tbuf[QM_DBG_TMP_BUF_LEN]; |
755 | int len, ret; |
756 | |
757 | if (*pos != 0) |
758 | return 0; |
759 | |
760 | if (count >= QM_DBG_TMP_BUF_LEN) |
761 | return -ENOSPC; |
762 | |
763 | len = simple_write_to_buffer(to: tbuf, QM_DBG_TMP_BUF_LEN - 1, ppos: pos, from: buf, |
764 | count); |
765 | if (len < 0) |
766 | return len; |
767 | |
768 | tbuf[len] = '\0'; |
769 | if (kstrtoul(s: tbuf, base: 0, res: &val)) |
770 | return -EFAULT; |
771 | |
772 | ret = hisi_qm_get_dfx_access(qm); |
773 | if (ret) |
774 | return ret; |
775 | |
776 | mutex_lock(&file->lock); |
777 | switch (index) { |
778 | case CURRENT_QM: |
779 | ret = current_qm_write(qm, val); |
780 | break; |
781 | case CURRENT_Q: |
782 | ret = current_q_write(qm, val); |
783 | break; |
784 | case CLEAR_ENABLE: |
785 | ret = clear_enable_write(qm, rd_clr_ctrl: val); |
786 | break; |
787 | default: |
788 | ret = -EINVAL; |
789 | } |
790 | mutex_unlock(lock: &file->lock); |
791 | |
792 | hisi_qm_put_dfx_access(qm); |
793 | |
794 | if (ret) |
795 | return ret; |
796 | |
797 | return count; |
798 | } |
799 | |
800 | static const struct file_operations qm_debug_fops = { |
801 | .owner = THIS_MODULE, |
802 | .open = simple_open, |
803 | .read = qm_debug_read, |
804 | .write = qm_debug_write, |
805 | }; |
806 | |
807 | static void dfx_regs_uninit(struct hisi_qm *qm, |
808 | struct dfx_diff_registers *dregs, int reg_len) |
809 | { |
810 | int i; |
811 | |
812 | /* Setting the pointer is NULL to prevent double free */ |
813 | for (i = 0; i < reg_len; i++) { |
814 | kfree(objp: dregs[i].regs); |
815 | dregs[i].regs = NULL; |
816 | } |
817 | kfree(objp: dregs); |
818 | } |
819 | |
820 | static struct dfx_diff_registers *dfx_regs_init(struct hisi_qm *qm, |
821 | const struct dfx_diff_registers *cregs, u32 reg_len) |
822 | { |
823 | struct dfx_diff_registers *diff_regs; |
824 | u32 j, base_offset; |
825 | int i; |
826 | |
827 | diff_regs = kcalloc(n: reg_len, size: sizeof(*diff_regs), GFP_KERNEL); |
828 | if (!diff_regs) |
829 | return ERR_PTR(error: -ENOMEM); |
830 | |
831 | for (i = 0; i < reg_len; i++) { |
832 | if (!cregs[i].reg_len) |
833 | continue; |
834 | |
835 | diff_regs[i].reg_offset = cregs[i].reg_offset; |
836 | diff_regs[i].reg_len = cregs[i].reg_len; |
837 | diff_regs[i].regs = kcalloc(QM_DFX_REGS_LEN, size: cregs[i].reg_len, |
838 | GFP_KERNEL); |
839 | if (!diff_regs[i].regs) |
840 | goto alloc_error; |
841 | |
842 | for (j = 0; j < diff_regs[i].reg_len; j++) { |
843 | base_offset = diff_regs[i].reg_offset + |
844 | j * QM_DFX_REGS_LEN; |
845 | diff_regs[i].regs[j] = readl(addr: qm->io_base + base_offset); |
846 | } |
847 | } |
848 | |
849 | return diff_regs; |
850 | |
851 | alloc_error: |
852 | while (i > 0) { |
853 | i--; |
854 | kfree(objp: diff_regs[i].regs); |
855 | } |
856 | kfree(objp: diff_regs); |
857 | return ERR_PTR(error: -ENOMEM); |
858 | } |
859 | |
860 | static int qm_diff_regs_init(struct hisi_qm *qm, |
861 | struct dfx_diff_registers *dregs, u32 reg_len) |
862 | { |
863 | qm->debug.qm_diff_regs = dfx_regs_init(qm, cregs: qm_diff_regs, ARRAY_SIZE(qm_diff_regs)); |
864 | if (IS_ERR(ptr: qm->debug.qm_diff_regs)) |
865 | return PTR_ERR(ptr: qm->debug.qm_diff_regs); |
866 | |
867 | qm->debug.acc_diff_regs = dfx_regs_init(qm, cregs: dregs, reg_len); |
868 | if (IS_ERR(ptr: qm->debug.acc_diff_regs)) { |
869 | dfx_regs_uninit(qm, dregs: qm->debug.qm_diff_regs, ARRAY_SIZE(qm_diff_regs)); |
870 | return PTR_ERR(ptr: qm->debug.acc_diff_regs); |
871 | } |
872 | |
873 | return 0; |
874 | } |
875 | |
876 | static void qm_last_regs_uninit(struct hisi_qm *qm) |
877 | { |
878 | struct qm_debug *debug = &qm->debug; |
879 | |
880 | if (qm->fun_type == QM_HW_VF || !debug->qm_last_words) |
881 | return; |
882 | |
883 | kfree(objp: debug->qm_last_words); |
884 | debug->qm_last_words = NULL; |
885 | } |
886 | |
887 | static int qm_last_regs_init(struct hisi_qm *qm) |
888 | { |
889 | int dfx_regs_num = ARRAY_SIZE(qm_dfx_regs); |
890 | struct qm_debug *debug = &qm->debug; |
891 | int i; |
892 | |
893 | if (qm->fun_type == QM_HW_VF) |
894 | return 0; |
895 | |
896 | debug->qm_last_words = kcalloc(n: dfx_regs_num, size: sizeof(unsigned int), GFP_KERNEL); |
897 | if (!debug->qm_last_words) |
898 | return -ENOMEM; |
899 | |
900 | for (i = 0; i < dfx_regs_num; i++) { |
901 | debug->qm_last_words[i] = readl_relaxed(qm->io_base + |
902 | qm_dfx_regs[i].offset); |
903 | } |
904 | |
905 | return 0; |
906 | } |
907 | |
908 | static void qm_diff_regs_uninit(struct hisi_qm *qm, u32 reg_len) |
909 | { |
910 | dfx_regs_uninit(qm, dregs: qm->debug.acc_diff_regs, reg_len); |
911 | dfx_regs_uninit(qm, dregs: qm->debug.qm_diff_regs, ARRAY_SIZE(qm_diff_regs)); |
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 | */ |
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 | */ |
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 | */ |
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(sem: &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(addr: qm->io_base + base_offset); |
990 | if (val != dregs[i].regs[j]) |
991 | seq_printf(m: s, fmt: "0x%08x = 0x%08x ---> 0x%08x\n" , |
992 | base_offset, dregs[i].regs[j], val); |
993 | } |
994 | } |
995 | up_read(sem: &qm->qps_lock); |
996 | |
997 | hisi_qm_put_dfx_access(qm); |
998 | } |
999 | EXPORT_SYMBOL_GPL(hisi_qm_acc_diff_regs_dump); |
1000 | |
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 | |
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 | |
1030 | static int qm_state_show(struct seq_file *s, void *unused) |
1031 | { |
1032 | struct hisi_qm *qm = s->private; |
1033 | u32 val; |
1034 | int ret; |
1035 | |
1036 | /* If device is in suspended, directly return the idle state. */ |
1037 | ret = hisi_qm_get_dfx_access(qm); |
1038 | if (!ret) { |
1039 | val = readl(addr: qm->io_base + QM_IN_IDLE_ST_REG); |
1040 | hisi_qm_put_dfx_access(qm); |
1041 | } else if (ret == -EAGAIN) { |
1042 | val = QM_IN_IDLE_STATE; |
1043 | } else { |
1044 | return ret; |
1045 | } |
1046 | |
1047 | seq_printf(m: s, fmt: "%u\n" , val); |
1048 | |
1049 | return 0; |
1050 | } |
1051 | |
1052 | DEFINE_SHOW_ATTRIBUTE(qm_state); |
1053 | |
1054 | static ssize_t qm_status_read(struct file *filp, char __user *buffer, |
1055 | size_t count, loff_t *pos) |
1056 | { |
1057 | struct hisi_qm *qm = filp->private_data; |
1058 | char buf[QM_DBG_READ_LEN]; |
1059 | int val, len; |
1060 | |
1061 | val = atomic_read(v: &qm->status.flags); |
1062 | len = scnprintf(buf, QM_DBG_READ_LEN, fmt: "%s\n" , qm_s[val]); |
1063 | |
1064 | return simple_read_from_buffer(to: buffer, count, ppos: pos, from: buf, available: len); |
1065 | } |
1066 | |
1067 | static const struct file_operations qm_status_fops = { |
1068 | .owner = THIS_MODULE, |
1069 | .open = simple_open, |
1070 | .read = qm_status_read, |
1071 | }; |
1072 | |
1073 | static void qm_create_debugfs_file(struct hisi_qm *qm, struct dentry *dir, |
1074 | enum qm_debug_file index) |
1075 | { |
1076 | struct debugfs_file *file = qm->debug.files + index; |
1077 | |
1078 | debugfs_create_file(name: qm_debug_file_name[index], mode: 0600, parent: dir, data: file, |
1079 | fops: &qm_debug_fops); |
1080 | |
1081 | file->index = index; |
1082 | mutex_init(&file->lock); |
1083 | file->debug = &qm->debug; |
1084 | } |
1085 | |
1086 | static int qm_debugfs_atomic64_set(void *data, u64 val) |
1087 | { |
1088 | if (val) |
1089 | return -EINVAL; |
1090 | |
1091 | atomic64_set(v: (atomic64_t *)data, i: 0); |
1092 | |
1093 | return 0; |
1094 | } |
1095 | |
1096 | static int qm_debugfs_atomic64_get(void *data, u64 *val) |
1097 | { |
1098 | *val = atomic64_read(v: (atomic64_t *)data); |
1099 | |
1100 | return 0; |
1101 | } |
1102 | |
1103 | DEFINE_DEBUGFS_ATTRIBUTE(qm_atomic64_ops, qm_debugfs_atomic64_get, |
1104 | qm_debugfs_atomic64_set, "%llu\n" ); |
1105 | |
1106 | /** |
1107 | * hisi_qm_debug_init() - Initialize qm related debugfs files. |
1108 | * @qm: The qm for which we want to add debugfs files. |
1109 | * |
1110 | * Create qm related debugfs files. |
1111 | */ |
1112 | void hisi_qm_debug_init(struct hisi_qm *qm) |
1113 | { |
1114 | struct dfx_diff_registers *qm_regs = qm->debug.qm_diff_regs; |
1115 | struct qm_dev_dfx *dev_dfx = &qm->debug.dev_dfx; |
1116 | struct qm_dfx *dfx = &qm->debug.dfx; |
1117 | struct dentry *qm_d; |
1118 | void *data; |
1119 | int i; |
1120 | |
1121 | qm_d = debugfs_create_dir(name: "qm" , parent: qm->debug.debug_root); |
1122 | qm->debug.qm_d = qm_d; |
1123 | |
1124 | /* only show this in PF */ |
1125 | if (qm->fun_type == QM_HW_PF) { |
1126 | debugfs_create_file(name: "qm_state" , mode: 0444, parent: qm->debug.qm_d, |
1127 | data: qm, fops: &qm_state_fops); |
1128 | |
1129 | qm_create_debugfs_file(qm, dir: qm->debug.debug_root, index: CURRENT_QM); |
1130 | for (i = CURRENT_Q; i < DEBUG_FILE_NUM; i++) |
1131 | qm_create_debugfs_file(qm, dir: qm->debug.qm_d, index: i); |
1132 | } |
1133 | |
1134 | if (qm_regs) |
1135 | debugfs_create_file(name: "diff_regs" , mode: 0444, parent: qm->debug.qm_d, |
1136 | data: qm, fops: &qm_diff_regs_fops); |
1137 | |
1138 | debugfs_create_file(name: "regs" , mode: 0444, parent: qm->debug.qm_d, data: qm, fops: &qm_regs_fops); |
1139 | |
1140 | debugfs_create_file(name: "cmd" , mode: 0600, parent: qm->debug.qm_d, data: qm, fops: &qm_cmd_fops); |
1141 | |
1142 | debugfs_create_file(name: "status" , mode: 0444, parent: qm->debug.qm_d, data: qm, |
1143 | fops: &qm_status_fops); |
1144 | |
1145 | debugfs_create_u32(name: "dev_state" , mode: 0444, parent: qm->debug.qm_d, value: &dev_dfx->dev_state); |
1146 | debugfs_create_u32(name: "dev_timeout" , mode: 0644, parent: qm->debug.qm_d, value: &dev_dfx->dev_timeout); |
1147 | |
1148 | for (i = 0; i < ARRAY_SIZE(qm_dfx_files); i++) { |
1149 | data = (atomic64_t *)((uintptr_t)dfx + qm_dfx_files[i].offset); |
1150 | debugfs_create_file(name: qm_dfx_files[i].name, |
1151 | mode: 0644, |
1152 | parent: qm_d, |
1153 | data, |
1154 | fops: &qm_atomic64_ops); |
1155 | } |
1156 | |
1157 | if (test_bit(QM_SUPPORT_FUNC_QOS, &qm->caps)) |
1158 | hisi_qm_set_algqos_init(qm); |
1159 | } |
1160 | EXPORT_SYMBOL_GPL(hisi_qm_debug_init); |
1161 | |
1162 | /** |
1163 | * hisi_qm_debug_regs_clear() - clear qm debug related registers. |
1164 | * @qm: The qm for which we want to clear its debug registers. |
1165 | */ |
1166 | void hisi_qm_debug_regs_clear(struct hisi_qm *qm) |
1167 | { |
1168 | const struct debugfs_reg32 *regs; |
1169 | int i; |
1170 | |
1171 | /* clear current_qm */ |
1172 | writel(val: 0x0, addr: qm->io_base + QM_DFX_MB_CNT_VF); |
1173 | writel(val: 0x0, addr: qm->io_base + QM_DFX_DB_CNT_VF); |
1174 | |
1175 | /* clear current_q */ |
1176 | writel(val: 0x0, addr: qm->io_base + QM_DFX_SQE_CNT_VF_SQN); |
1177 | writel(val: 0x0, addr: qm->io_base + QM_DFX_CQE_CNT_VF_CQN); |
1178 | |
1179 | /* |
1180 | * these registers are reading and clearing, so clear them after |
1181 | * reading them. |
1182 | */ |
1183 | writel(val: 0x1, addr: qm->io_base + QM_DFX_CNT_CLR_CE); |
1184 | |
1185 | regs = qm_dfx_regs; |
1186 | for (i = 0; i < CNT_CYC_REGS_NUM; i++) { |
1187 | readl(addr: qm->io_base + regs->offset); |
1188 | regs++; |
1189 | } |
1190 | |
1191 | /* clear clear_enable */ |
1192 | writel(val: 0x0, addr: qm->io_base + QM_DFX_CNT_CLR_CE); |
1193 | } |
1194 | EXPORT_SYMBOL_GPL(hisi_qm_debug_regs_clear); |
1195 | |