1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
3
4 #include <linux/delay.h>
5
6 #include "hinic3_cmdq.h"
7 #include "hinic3_hw_comm.h"
8 #include "hinic3_hwdev.h"
9 #include "hinic3_hwif.h"
10 #include "hinic3_mbox.h"
11
hinic3_get_interrupt_cfg(struct hinic3_hwdev * hwdev,struct hinic3_interrupt_info * info)12 static int hinic3_get_interrupt_cfg(struct hinic3_hwdev *hwdev,
13 struct hinic3_interrupt_info *info)
14 {
15 struct comm_cmd_cfg_msix_ctrl_reg msix_cfg = {};
16 struct mgmt_msg_params msg_params = {};
17 int err;
18
19 msix_cfg.func_id = hinic3_global_func_id(hwdev);
20 msix_cfg.msix_index = info->msix_index;
21 msix_cfg.opcode = MGMT_MSG_CMD_OP_GET;
22
23 mgmt_msg_params_init_default(&msg_params, &msix_cfg, sizeof(msix_cfg));
24
25 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
26 COMM_CMD_CFG_MSIX_CTRL_REG, &msg_params);
27 if (err || msix_cfg.head.status) {
28 dev_err(hwdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x\n",
29 err, msix_cfg.head.status);
30 return -EFAULT;
31 }
32
33 info->lli_credit_limit = msix_cfg.lli_credit_cnt;
34 info->lli_timer_cfg = msix_cfg.lli_timer_cnt;
35 info->pending_limit = msix_cfg.pending_cnt;
36 info->coalesc_timer_cfg = msix_cfg.coalesce_timer_cnt;
37 info->resend_timer_cfg = msix_cfg.resend_timer_cnt;
38
39 return 0;
40 }
41
hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev * hwdev,const struct hinic3_interrupt_info * info)42 int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
43 const struct hinic3_interrupt_info *info)
44 {
45 struct comm_cmd_cfg_msix_ctrl_reg msix_cfg = {};
46 struct mgmt_msg_params msg_params = {};
47 int err;
48
49 msix_cfg.func_id = hinic3_global_func_id(hwdev);
50 msix_cfg.msix_index = info->msix_index;
51 msix_cfg.opcode = MGMT_MSG_CMD_OP_SET;
52
53 msix_cfg.lli_credit_cnt = info->lli_credit_limit;
54 msix_cfg.lli_timer_cnt = info->lli_timer_cfg;
55 msix_cfg.pending_cnt = info->pending_limit;
56 msix_cfg.coalesce_timer_cnt = info->coalesc_timer_cfg;
57 msix_cfg.resend_timer_cnt = info->resend_timer_cfg;
58
59 mgmt_msg_params_init_default(&msg_params, &msix_cfg, sizeof(msix_cfg));
60
61 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
62 COMM_CMD_CFG_MSIX_CTRL_REG, &msg_params);
63 if (err || msix_cfg.head.status) {
64 dev_err(hwdev->dev,
65 "Failed to set interrupt config, err: %d, status: 0x%x\n",
66 err, msix_cfg.head.status);
67 return -EINVAL;
68 }
69
70 return 0;
71 }
72
hinic3_set_interrupt_cfg(struct hinic3_hwdev * hwdev,struct hinic3_interrupt_info info)73 int hinic3_set_interrupt_cfg(struct hinic3_hwdev *hwdev,
74 struct hinic3_interrupt_info info)
75 {
76 struct hinic3_interrupt_info temp_info;
77 int err;
78
79 temp_info.msix_index = info.msix_index;
80
81 err = hinic3_get_interrupt_cfg(hwdev, &temp_info);
82 if (err)
83 return err;
84
85 info.lli_credit_limit = temp_info.lli_credit_limit;
86 info.lli_timer_cfg = temp_info.lli_timer_cfg;
87
88 if (!info.interrupt_coalesc_set) {
89 info.pending_limit = temp_info.pending_limit;
90 info.coalesc_timer_cfg = temp_info.coalesc_timer_cfg;
91 info.resend_timer_cfg = temp_info.resend_timer_cfg;
92 }
93
94 return hinic3_set_interrupt_cfg_direct(hwdev, &info);
95 }
96
hinic3_func_reset(struct hinic3_hwdev * hwdev,u16 func_id,u64 reset_flag)97 int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag)
98 {
99 struct comm_cmd_func_reset func_reset = {};
100 struct mgmt_msg_params msg_params = {};
101 int err;
102
103 func_reset.func_id = func_id;
104 func_reset.reset_flag = reset_flag;
105
106 mgmt_msg_params_init_default(&msg_params, &func_reset,
107 sizeof(func_reset));
108
109 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
110 COMM_CMD_FUNC_RESET, &msg_params);
111 if (err || func_reset.head.status) {
112 dev_err(hwdev->dev, "Failed to reset func resources, reset_flag 0x%llx, err: %d, status: 0x%x\n",
113 reset_flag, err, func_reset.head.status);
114 return -EIO;
115 }
116
117 return 0;
118 }
119
hinic3_comm_features_nego(struct hinic3_hwdev * hwdev,u8 opcode,u64 * s_feature,u16 size)120 static int hinic3_comm_features_nego(struct hinic3_hwdev *hwdev, u8 opcode,
121 u64 *s_feature, u16 size)
122 {
123 struct comm_cmd_feature_nego feature_nego = {};
124 struct mgmt_msg_params msg_params = {};
125 int err;
126
127 feature_nego.func_id = hinic3_global_func_id(hwdev);
128 feature_nego.opcode = opcode;
129 if (opcode == MGMT_MSG_CMD_OP_SET)
130 memcpy(feature_nego.s_feature, s_feature,
131 array_size(size, sizeof(u64)));
132
133 mgmt_msg_params_init_default(&msg_params, &feature_nego,
134 sizeof(feature_nego));
135
136 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
137 COMM_CMD_FEATURE_NEGO, &msg_params);
138 if (err || feature_nego.head.status) {
139 dev_err(hwdev->dev, "Failed to negotiate feature, err: %d, status: 0x%x\n",
140 err, feature_nego.head.status);
141 return -EINVAL;
142 }
143
144 if (opcode == MGMT_MSG_CMD_OP_GET)
145 memcpy(s_feature, feature_nego.s_feature,
146 array_size(size, sizeof(u64)));
147
148 return 0;
149 }
150
hinic3_get_comm_features(struct hinic3_hwdev * hwdev,u64 * s_feature,u16 size)151 int hinic3_get_comm_features(struct hinic3_hwdev *hwdev, u64 *s_feature,
152 u16 size)
153 {
154 return hinic3_comm_features_nego(hwdev, MGMT_MSG_CMD_OP_GET, s_feature,
155 size);
156 }
157
hinic3_set_comm_features(struct hinic3_hwdev * hwdev,u64 * s_feature,u16 size)158 int hinic3_set_comm_features(struct hinic3_hwdev *hwdev, u64 *s_feature,
159 u16 size)
160 {
161 return hinic3_comm_features_nego(hwdev, MGMT_MSG_CMD_OP_SET, s_feature,
162 size);
163 }
164
hinic3_get_global_attr(struct hinic3_hwdev * hwdev,struct comm_global_attr * attr)165 int hinic3_get_global_attr(struct hinic3_hwdev *hwdev,
166 struct comm_global_attr *attr)
167 {
168 struct comm_cmd_get_glb_attr get_attr = {};
169 struct mgmt_msg_params msg_params = {};
170 int err;
171
172 mgmt_msg_params_init_default(&msg_params, &get_attr, sizeof(get_attr));
173
174 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
175 COMM_CMD_GET_GLOBAL_ATTR, &msg_params);
176 if (err || get_attr.head.status) {
177 dev_err(hwdev->dev,
178 "Failed to get global attribute, err: %d, status: 0x%x\n",
179 err, get_attr.head.status);
180 return -EIO;
181 }
182
183 memcpy(attr, &get_attr.attr, sizeof(*attr));
184
185 return 0;
186 }
187
hinic3_set_func_svc_used_state(struct hinic3_hwdev * hwdev,u16 svc_type,u8 state)188 int hinic3_set_func_svc_used_state(struct hinic3_hwdev *hwdev, u16 svc_type,
189 u8 state)
190 {
191 struct comm_cmd_set_func_svc_used_state used_state = {};
192 struct mgmt_msg_params msg_params = {};
193 int err;
194
195 used_state.func_id = hinic3_global_func_id(hwdev);
196 used_state.svc_type = svc_type;
197 used_state.used_state = state;
198
199 mgmt_msg_params_init_default(&msg_params, &used_state,
200 sizeof(used_state));
201
202 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
203 COMM_CMD_SET_FUNC_SVC_USED_STATE,
204 &msg_params);
205 if (err || used_state.head.status) {
206 dev_err(hwdev->dev,
207 "Failed to set func service used state, err: %d, status: 0x%x\n",
208 err, used_state.head.status);
209 return -EIO;
210 }
211
212 return 0;
213 }
214
hinic3_set_dma_attr_tbl(struct hinic3_hwdev * hwdev,u8 entry_idx,u8 st,u8 at,u8 ph,u8 no_snooping,u8 tph_en)215 int hinic3_set_dma_attr_tbl(struct hinic3_hwdev *hwdev, u8 entry_idx, u8 st,
216 u8 at, u8 ph, u8 no_snooping, u8 tph_en)
217 {
218 struct comm_cmd_set_dma_attr dma_attr = {};
219 struct mgmt_msg_params msg_params = {};
220 int err;
221
222 dma_attr.func_id = hinic3_global_func_id(hwdev);
223 dma_attr.entry_idx = entry_idx;
224 dma_attr.st = st;
225 dma_attr.at = at;
226 dma_attr.ph = ph;
227 dma_attr.no_snooping = no_snooping;
228 dma_attr.tph_en = tph_en;
229
230 mgmt_msg_params_init_default(&msg_params, &dma_attr, sizeof(dma_attr));
231
232 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
233 COMM_CMD_SET_DMA_ATTR, &msg_params);
234 if (err || dma_attr.head.status) {
235 dev_err(hwdev->dev, "Failed to set dma attr, err: %d, status: 0x%x\n",
236 err, dma_attr.head.status);
237 return -EIO;
238 }
239
240 return 0;
241 }
242
hinic3_set_wq_page_size(struct hinic3_hwdev * hwdev,u16 func_idx,u32 page_size)243 int hinic3_set_wq_page_size(struct hinic3_hwdev *hwdev, u16 func_idx,
244 u32 page_size)
245 {
246 struct comm_cmd_cfg_wq_page_size page_size_info = {};
247 struct mgmt_msg_params msg_params = {};
248 int err;
249
250 page_size_info.func_id = func_idx;
251 page_size_info.page_size = ilog2(page_size / HINIC3_MIN_PAGE_SIZE);
252 page_size_info.opcode = MGMT_MSG_CMD_OP_SET;
253
254 mgmt_msg_params_init_default(&msg_params, &page_size_info,
255 sizeof(page_size_info));
256
257 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
258 COMM_CMD_CFG_PAGESIZE, &msg_params);
259 if (err || page_size_info.head.status) {
260 dev_err(hwdev->dev,
261 "Failed to set wq page size, err: %d, status: 0x%x\n",
262 err, page_size_info.head.status);
263 return -EFAULT;
264 }
265
266 return 0;
267 }
268
hinic3_set_cmdq_depth(struct hinic3_hwdev * hwdev,u16 cmdq_depth)269 int hinic3_set_cmdq_depth(struct hinic3_hwdev *hwdev, u16 cmdq_depth)
270 {
271 struct comm_cmd_set_root_ctxt root_ctxt = {};
272 struct mgmt_msg_params msg_params = {};
273 int err;
274
275 root_ctxt.func_id = hinic3_global_func_id(hwdev);
276
277 root_ctxt.set_cmdq_depth = 1;
278 root_ctxt.cmdq_depth = ilog2(cmdq_depth);
279
280 mgmt_msg_params_init_default(&msg_params, &root_ctxt,
281 sizeof(root_ctxt));
282
283 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
284 COMM_CMD_SET_VAT, &msg_params);
285 if (err || root_ctxt.head.status) {
286 dev_err(hwdev->dev,
287 "Failed to set cmdq depth, err: %d, status: 0x%x\n",
288 err, root_ctxt.head.status);
289 return -EFAULT;
290 }
291
292 return 0;
293 }
294
295 #define HINIC3_WAIT_CMDQ_IDLE_TIMEOUT 5000
296
check_cmdq_stop_handler(void * priv_data)297 static enum hinic3_wait_return check_cmdq_stop_handler(void *priv_data)
298 {
299 struct hinic3_hwdev *hwdev = priv_data;
300 enum hinic3_cmdq_type cmdq_type;
301 struct hinic3_cmdqs *cmdqs;
302
303 cmdqs = hwdev->cmdqs;
304 for (cmdq_type = 0; cmdq_type < cmdqs->cmdq_num; cmdq_type++) {
305 if (!hinic3_cmdq_idle(&cmdqs->cmdq[cmdq_type]))
306 return HINIC3_WAIT_PROCESS_WAITING;
307 }
308
309 return HINIC3_WAIT_PROCESS_CPL;
310 }
311
wait_cmdq_stop(struct hinic3_hwdev * hwdev)312 static int wait_cmdq_stop(struct hinic3_hwdev *hwdev)
313 {
314 struct hinic3_cmdqs *cmdqs = hwdev->cmdqs;
315 enum hinic3_cmdq_type cmdq_type;
316 int err;
317
318 if (!(cmdqs->status & HINIC3_CMDQ_ENABLE))
319 return 0;
320
321 cmdqs->status &= ~HINIC3_CMDQ_ENABLE;
322 err = hinic3_wait_for_timeout(hwdev, check_cmdq_stop_handler,
323 HINIC3_WAIT_CMDQ_IDLE_TIMEOUT,
324 USEC_PER_MSEC);
325
326 if (err)
327 goto err_reenable_cmdq;
328
329 return 0;
330
331 err_reenable_cmdq:
332 for (cmdq_type = 0; cmdq_type < cmdqs->cmdq_num; cmdq_type++) {
333 if (!hinic3_cmdq_idle(&cmdqs->cmdq[cmdq_type]))
334 dev_err(hwdev->dev, "Cmdq %d is busy\n", cmdq_type);
335 }
336 cmdqs->status |= HINIC3_CMDQ_ENABLE;
337
338 return err;
339 }
340
hinic3_func_rx_tx_flush(struct hinic3_hwdev * hwdev)341 int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev)
342 {
343 struct comm_cmd_clear_resource clear_db = {};
344 struct comm_cmd_clear_resource clr_res = {};
345 struct hinic3_hwif *hwif = hwdev->hwif;
346 struct mgmt_msg_params msg_params = {};
347 int ret = 0;
348 int err;
349
350 err = wait_cmdq_stop(hwdev);
351 if (err) {
352 dev_warn(hwdev->dev, "CMDQ is still working, CMDQ timeout value is unreasonable\n");
353 ret = err;
354 }
355
356 hinic3_toggle_doorbell(hwif, DISABLE_DOORBELL);
357
358 clear_db.func_id = hwif->attr.func_global_idx;
359 mgmt_msg_params_init_default(&msg_params, &clear_db, sizeof(clear_db));
360 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
361 COMM_CMD_FLUSH_DOORBELL, &msg_params);
362 if (err || clear_db.head.status) {
363 dev_warn(hwdev->dev, "Failed to flush doorbell, err: %d, status: 0x%x\n",
364 err, clear_db.head.status);
365 if (err)
366 ret = err;
367 else
368 ret = -EFAULT;
369 }
370
371 hinic3_set_pf_status(hwif, HINIC3_PF_STATUS_FLR_START_FLAG);
372
373 clr_res.func_id = hwif->attr.func_global_idx;
374 msg_params.buf_in = &clr_res;
375 msg_params.in_size = sizeof(clr_res);
376 err = hinic3_send_mbox_to_mgmt_no_ack(hwdev, MGMT_MOD_COMM,
377 COMM_CMD_START_FLUSH,
378 &msg_params);
379 if (err) {
380 dev_warn(hwdev->dev, "Failed to notice flush message, err: %d\n",
381 err);
382 ret = err;
383 }
384
385 hinic3_toggle_doorbell(hwif, ENABLE_DOORBELL);
386
387 err = hinic3_reinit_cmdq_ctxts(hwdev);
388 if (err) {
389 dev_warn(hwdev->dev, "Failed to reinit cmdq\n");
390 ret = err;
391 }
392
393 return ret;
394 }
395
hinic3_set_bdf_ctxt(struct hinic3_hwdev * hwdev,struct comm_cmd_bdf_info * bdf_info)396 int hinic3_set_bdf_ctxt(struct hinic3_hwdev *hwdev,
397 struct comm_cmd_bdf_info *bdf_info)
398 {
399 struct mgmt_msg_params msg_params = {};
400 int err;
401
402 mgmt_msg_params_init_default(&msg_params, bdf_info, sizeof(*bdf_info));
403
404 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
405 COMM_CMD_SEND_BDF_INFO, &msg_params);
406 if (err || bdf_info->head.status) {
407 dev_err(hwdev->dev,
408 "Failed to set bdf info to fw, err: %d, status: 0x%x\n",
409 err, bdf_info->head.status);
410 return -EFAULT;
411 }
412
413 return 0;
414 }
415
hinic3_sync_time(struct hinic3_hwdev * hwdev,u64 time)416 static int hinic3_sync_time(struct hinic3_hwdev *hwdev, u64 time)
417 {
418 struct comm_cmd_sync_time time_info = {};
419 struct mgmt_msg_params msg_params = {};
420 int err;
421
422 time_info.mstime = time;
423
424 mgmt_msg_params_init_default(&msg_params, &time_info,
425 sizeof(time_info));
426
427 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
428 COMM_CMD_SYNC_TIME, &msg_params);
429 if (err || time_info.head.status) {
430 dev_err(hwdev->dev,
431 "Failed to sync time to mgmt, err: %d, status: 0x%x\n",
432 err, time_info.head.status);
433 return -EFAULT;
434 }
435
436 return 0;
437 }
438
hinic3_sync_time_to_fw(struct hinic3_hwdev * hwdev)439 void hinic3_sync_time_to_fw(struct hinic3_hwdev *hwdev)
440 {
441 struct timespec64 ts = {};
442 u64 time;
443 int err;
444
445 ktime_get_real_ts64(&ts);
446 time = (u64)(ts.tv_sec * MSEC_PER_SEC + ts.tv_nsec / NSEC_PER_MSEC);
447
448 err = hinic3_sync_time(hwdev, time);
449 if (err)
450 dev_err(hwdev->dev,
451 "Synchronize UTC time to firmware failed, err=%d\n",
452 err);
453 }
454
get_hw_rx_buf_size_idx(int rx_buf_sz,u16 * buf_sz_idx)455 static int get_hw_rx_buf_size_idx(int rx_buf_sz, u16 *buf_sz_idx)
456 {
457 /* Supported RX buffer sizes in bytes. Configured by array index. */
458 static const int supported_sizes[16] = {
459 [0] = 32, [1] = 64, [2] = 96, [3] = 128,
460 [4] = 192, [5] = 256, [6] = 384, [7] = 512,
461 [8] = 768, [9] = 1024, [10] = 1536, [11] = 2048,
462 [12] = 3072, [13] = 4096, [14] = 8192, [15] = 16384,
463 };
464 u16 idx;
465
466 /* Scan from biggest to smallest. Choose supported size that is equal or
467 * smaller. For smaller value HW will under-utilize posted buffers. For
468 * bigger value HW may overrun posted buffers.
469 */
470 idx = ARRAY_SIZE(supported_sizes);
471 while (idx > 0) {
472 idx--;
473 if (supported_sizes[idx] <= rx_buf_sz) {
474 *buf_sz_idx = idx;
475 return 0;
476 }
477 }
478
479 return -EINVAL;
480 }
481
hinic3_set_root_ctxt(struct hinic3_hwdev * hwdev,u32 rq_depth,u32 sq_depth,int rx_buf_sz)482 int hinic3_set_root_ctxt(struct hinic3_hwdev *hwdev, u32 rq_depth, u32 sq_depth,
483 int rx_buf_sz)
484 {
485 struct comm_cmd_set_root_ctxt root_ctxt = {};
486 struct mgmt_msg_params msg_params = {};
487 u16 buf_sz_idx;
488 int err;
489
490 err = get_hw_rx_buf_size_idx(rx_buf_sz, &buf_sz_idx);
491 if (err)
492 return err;
493
494 root_ctxt.func_id = hinic3_global_func_id(hwdev);
495
496 root_ctxt.set_cmdq_depth = 0;
497 root_ctxt.cmdq_depth = 0;
498
499 root_ctxt.lro_en = 1;
500
501 root_ctxt.rq_depth = ilog2(rq_depth);
502 root_ctxt.rx_buf_sz = buf_sz_idx;
503 root_ctxt.sq_depth = ilog2(sq_depth);
504
505 mgmt_msg_params_init_default(&msg_params, &root_ctxt,
506 sizeof(root_ctxt));
507
508 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
509 COMM_CMD_SET_VAT, &msg_params);
510 if (err || root_ctxt.head.status) {
511 dev_err(hwdev->dev,
512 "Failed to set root context, err: %d, status: 0x%x\n",
513 err, root_ctxt.head.status);
514 return -EFAULT;
515 }
516
517 return 0;
518 }
519
hinic3_clean_root_ctxt(struct hinic3_hwdev * hwdev)520 int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev)
521 {
522 struct comm_cmd_set_root_ctxt root_ctxt = {};
523 struct mgmt_msg_params msg_params = {};
524 int err;
525
526 root_ctxt.func_id = hinic3_global_func_id(hwdev);
527
528 mgmt_msg_params_init_default(&msg_params, &root_ctxt,
529 sizeof(root_ctxt));
530
531 err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
532 COMM_CMD_SET_VAT, &msg_params);
533 if (err || root_ctxt.head.status) {
534 dev_err(hwdev->dev,
535 "Failed to set root context, err: %d, status: 0x%x\n",
536 err, root_ctxt.head.status);
537 return -EFAULT;
538 }
539
540 return 0;
541 }
542