xref: /linux/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c (revision 8a5f956a9fb7d74fff681145082acfad5afa6bb8)
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 
12 int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
13 				    const 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_SET;
22 
23 	msix_cfg.lli_credit_cnt = info->lli_credit_limit;
24 	msix_cfg.lli_timer_cnt = info->lli_timer_cfg;
25 	msix_cfg.pending_cnt = info->pending_limit;
26 	msix_cfg.coalesce_timer_cnt = info->coalesc_timer_cfg;
27 	msix_cfg.resend_timer_cnt = info->resend_timer_cfg;
28 
29 	mgmt_msg_params_init_default(&msg_params, &msix_cfg, sizeof(msix_cfg));
30 
31 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
32 				       COMM_CMD_CFG_MSIX_CTRL_REG, &msg_params);
33 	if (err || msix_cfg.head.status) {
34 		dev_err(hwdev->dev,
35 			"Failed to set interrupt config, err: %d, status: 0x%x\n",
36 			err, msix_cfg.head.status);
37 		return -EINVAL;
38 	}
39 
40 	return 0;
41 }
42 
43 int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag)
44 {
45 	struct comm_cmd_func_reset func_reset = {};
46 	struct mgmt_msg_params msg_params = {};
47 	int err;
48 
49 	func_reset.func_id = func_id;
50 	func_reset.reset_flag = reset_flag;
51 
52 	mgmt_msg_params_init_default(&msg_params, &func_reset,
53 				     sizeof(func_reset));
54 
55 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
56 				       COMM_CMD_FUNC_RESET, &msg_params);
57 	if (err || func_reset.head.status) {
58 		dev_err(hwdev->dev, "Failed to reset func resources, reset_flag 0x%llx, err: %d, status: 0x%x\n",
59 			reset_flag, err, func_reset.head.status);
60 		return -EIO;
61 	}
62 
63 	return 0;
64 }
65 
66 static int hinic3_comm_features_nego(struct hinic3_hwdev *hwdev, u8 opcode,
67 				     u64 *s_feature, u16 size)
68 {
69 	struct comm_cmd_feature_nego feature_nego = {};
70 	struct mgmt_msg_params msg_params = {};
71 	int err;
72 
73 	feature_nego.func_id = hinic3_global_func_id(hwdev);
74 	feature_nego.opcode = opcode;
75 	if (opcode == MGMT_MSG_CMD_OP_SET)
76 		memcpy(feature_nego.s_feature, s_feature,
77 		       array_size(size, sizeof(u64)));
78 
79 	mgmt_msg_params_init_default(&msg_params, &feature_nego,
80 				     sizeof(feature_nego));
81 
82 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
83 				       COMM_CMD_FEATURE_NEGO, &msg_params);
84 	if (err || feature_nego.head.status) {
85 		dev_err(hwdev->dev, "Failed to negotiate feature, err: %d, status: 0x%x\n",
86 			err, feature_nego.head.status);
87 		return -EINVAL;
88 	}
89 
90 	if (opcode == MGMT_MSG_CMD_OP_GET)
91 		memcpy(s_feature, feature_nego.s_feature,
92 		       array_size(size, sizeof(u64)));
93 
94 	return 0;
95 }
96 
97 int hinic3_get_comm_features(struct hinic3_hwdev *hwdev, u64 *s_feature,
98 			     u16 size)
99 {
100 	return hinic3_comm_features_nego(hwdev, MGMT_MSG_CMD_OP_GET, s_feature,
101 					 size);
102 }
103 
104 int hinic3_set_comm_features(struct hinic3_hwdev *hwdev, u64 *s_feature,
105 			     u16 size)
106 {
107 	return hinic3_comm_features_nego(hwdev, MGMT_MSG_CMD_OP_SET, s_feature,
108 					 size);
109 }
110 
111 int hinic3_get_global_attr(struct hinic3_hwdev *hwdev,
112 			   struct comm_global_attr *attr)
113 {
114 	struct comm_cmd_get_glb_attr get_attr = {};
115 	struct mgmt_msg_params msg_params = {};
116 	int err;
117 
118 	mgmt_msg_params_init_default(&msg_params, &get_attr, sizeof(get_attr));
119 
120 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
121 				       COMM_CMD_GET_GLOBAL_ATTR, &msg_params);
122 	if (err || get_attr.head.status) {
123 		dev_err(hwdev->dev,
124 			"Failed to get global attribute, err: %d, status: 0x%x\n",
125 			err, get_attr.head.status);
126 		return -EIO;
127 	}
128 
129 	memcpy(attr, &get_attr.attr, sizeof(*attr));
130 
131 	return 0;
132 }
133 
134 int hinic3_set_func_svc_used_state(struct hinic3_hwdev *hwdev, u16 svc_type,
135 				   u8 state)
136 {
137 	struct comm_cmd_set_func_svc_used_state used_state = {};
138 	struct mgmt_msg_params msg_params = {};
139 	int err;
140 
141 	used_state.func_id = hinic3_global_func_id(hwdev);
142 	used_state.svc_type = svc_type;
143 	used_state.used_state = state;
144 
145 	mgmt_msg_params_init_default(&msg_params, &used_state,
146 				     sizeof(used_state));
147 
148 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
149 				       COMM_CMD_SET_FUNC_SVC_USED_STATE,
150 				       &msg_params);
151 	if (err || used_state.head.status) {
152 		dev_err(hwdev->dev,
153 			"Failed to set func service used state, err: %d, status: 0x%x\n",
154 			err, used_state.head.status);
155 		return -EIO;
156 	}
157 
158 	return 0;
159 }
160 
161 int hinic3_set_dma_attr_tbl(struct hinic3_hwdev *hwdev, u8 entry_idx, u8 st,
162 			    u8 at, u8 ph, u8 no_snooping, u8 tph_en)
163 {
164 	struct comm_cmd_set_dma_attr dma_attr = {};
165 	struct mgmt_msg_params msg_params = {};
166 	int err;
167 
168 	dma_attr.func_id = hinic3_global_func_id(hwdev);
169 	dma_attr.entry_idx = entry_idx;
170 	dma_attr.st = st;
171 	dma_attr.at = at;
172 	dma_attr.ph = ph;
173 	dma_attr.no_snooping = no_snooping;
174 	dma_attr.tph_en = tph_en;
175 
176 	mgmt_msg_params_init_default(&msg_params, &dma_attr, sizeof(dma_attr));
177 
178 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
179 				       COMM_CMD_SET_DMA_ATTR, &msg_params);
180 	if (err || dma_attr.head.status) {
181 		dev_err(hwdev->dev, "Failed to set dma attr, err: %d, status: 0x%x\n",
182 			err, dma_attr.head.status);
183 		return -EIO;
184 	}
185 
186 	return 0;
187 }
188 
189 int hinic3_set_wq_page_size(struct hinic3_hwdev *hwdev, u16 func_idx,
190 			    u32 page_size)
191 {
192 	struct comm_cmd_cfg_wq_page_size page_size_info = {};
193 	struct mgmt_msg_params msg_params = {};
194 	int err;
195 
196 	page_size_info.func_id = func_idx;
197 	page_size_info.page_size = ilog2(page_size / HINIC3_MIN_PAGE_SIZE);
198 	page_size_info.opcode = MGMT_MSG_CMD_OP_SET;
199 
200 	mgmt_msg_params_init_default(&msg_params, &page_size_info,
201 				     sizeof(page_size_info));
202 
203 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
204 				       COMM_CMD_CFG_PAGESIZE, &msg_params);
205 	if (err || page_size_info.head.status) {
206 		dev_err(hwdev->dev,
207 			"Failed to set wq page size, err: %d, status: 0x%x\n",
208 			err, page_size_info.head.status);
209 		return -EFAULT;
210 	}
211 
212 	return 0;
213 }
214 
215 int hinic3_set_cmdq_depth(struct hinic3_hwdev *hwdev, u16 cmdq_depth)
216 {
217 	struct comm_cmd_set_root_ctxt root_ctxt = {};
218 	struct mgmt_msg_params msg_params = {};
219 	int err;
220 
221 	root_ctxt.func_id = hinic3_global_func_id(hwdev);
222 
223 	root_ctxt.set_cmdq_depth = 1;
224 	root_ctxt.cmdq_depth = ilog2(cmdq_depth);
225 
226 	mgmt_msg_params_init_default(&msg_params, &root_ctxt,
227 				     sizeof(root_ctxt));
228 
229 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
230 				       COMM_CMD_SET_VAT, &msg_params);
231 	if (err || root_ctxt.head.status) {
232 		dev_err(hwdev->dev,
233 			"Failed to set cmdq depth, err: %d, status: 0x%x\n",
234 			err, root_ctxt.head.status);
235 		return -EFAULT;
236 	}
237 
238 	return 0;
239 }
240 
241 #define HINIC3_WAIT_CMDQ_IDLE_TIMEOUT    5000
242 
243 static enum hinic3_wait_return check_cmdq_stop_handler(void *priv_data)
244 {
245 	struct hinic3_hwdev *hwdev = priv_data;
246 	enum hinic3_cmdq_type cmdq_type;
247 	struct hinic3_cmdqs *cmdqs;
248 
249 	cmdqs = hwdev->cmdqs;
250 	for (cmdq_type = 0; cmdq_type < cmdqs->cmdq_num; cmdq_type++) {
251 		if (!hinic3_cmdq_idle(&cmdqs->cmdq[cmdq_type]))
252 			return HINIC3_WAIT_PROCESS_WAITING;
253 	}
254 
255 	return HINIC3_WAIT_PROCESS_CPL;
256 }
257 
258 static int wait_cmdq_stop(struct hinic3_hwdev *hwdev)
259 {
260 	struct hinic3_cmdqs *cmdqs = hwdev->cmdqs;
261 	enum hinic3_cmdq_type cmdq_type;
262 	int err;
263 
264 	if (!(cmdqs->status & HINIC3_CMDQ_ENABLE))
265 		return 0;
266 
267 	cmdqs->status &= ~HINIC3_CMDQ_ENABLE;
268 	err = hinic3_wait_for_timeout(hwdev, check_cmdq_stop_handler,
269 				      HINIC3_WAIT_CMDQ_IDLE_TIMEOUT,
270 				      USEC_PER_MSEC);
271 
272 	if (err)
273 		goto err_reenable_cmdq;
274 
275 	return 0;
276 
277 err_reenable_cmdq:
278 	for (cmdq_type = 0; cmdq_type < cmdqs->cmdq_num; cmdq_type++) {
279 		if (!hinic3_cmdq_idle(&cmdqs->cmdq[cmdq_type]))
280 			dev_err(hwdev->dev, "Cmdq %d is busy\n", cmdq_type);
281 	}
282 	cmdqs->status |= HINIC3_CMDQ_ENABLE;
283 
284 	return err;
285 }
286 
287 int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev)
288 {
289 	struct comm_cmd_clear_resource clear_db = {};
290 	struct comm_cmd_clear_resource clr_res = {};
291 	struct hinic3_hwif *hwif = hwdev->hwif;
292 	struct mgmt_msg_params msg_params = {};
293 	int ret = 0;
294 	int err;
295 
296 	err = wait_cmdq_stop(hwdev);
297 	if (err) {
298 		dev_warn(hwdev->dev, "CMDQ is still working, CMDQ timeout value is unreasonable\n");
299 		ret = err;
300 	}
301 
302 	hinic3_toggle_doorbell(hwif, DISABLE_DOORBELL);
303 
304 	clear_db.func_id = hwif->attr.func_global_idx;
305 	mgmt_msg_params_init_default(&msg_params, &clear_db, sizeof(clear_db));
306 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
307 				       COMM_CMD_FLUSH_DOORBELL, &msg_params);
308 	if (err || clear_db.head.status) {
309 		dev_warn(hwdev->dev, "Failed to flush doorbell, err: %d, status: 0x%x\n",
310 			 err, clear_db.head.status);
311 		if (err)
312 			ret = err;
313 		else
314 			ret = -EFAULT;
315 	}
316 
317 	clr_res.func_id = hwif->attr.func_global_idx;
318 	msg_params.buf_in = &clr_res;
319 	msg_params.in_size = sizeof(clr_res);
320 	err = hinic3_send_mbox_to_mgmt_no_ack(hwdev, MGMT_MOD_COMM,
321 					      COMM_CMD_START_FLUSH,
322 					      &msg_params);
323 	if (err) {
324 		dev_warn(hwdev->dev, "Failed to notice flush message, err: %d\n",
325 			 err);
326 		ret = err;
327 	}
328 
329 	hinic3_toggle_doorbell(hwif, ENABLE_DOORBELL);
330 
331 	err = hinic3_reinit_cmdq_ctxts(hwdev);
332 	if (err) {
333 		dev_warn(hwdev->dev, "Failed to reinit cmdq\n");
334 		ret = err;
335 	}
336 
337 	return ret;
338 }
339 
340 static int get_hw_rx_buf_size_idx(int rx_buf_sz, u16 *buf_sz_idx)
341 {
342 	/* Supported RX buffer sizes in bytes. Configured by array index. */
343 	static const int supported_sizes[16] = {
344 		[0] = 32,     [1] = 64,     [2] = 96,     [3] = 128,
345 		[4] = 192,    [5] = 256,    [6] = 384,    [7] = 512,
346 		[8] = 768,    [9] = 1024,   [10] = 1536,  [11] = 2048,
347 		[12] = 3072,  [13] = 4096,  [14] = 8192,  [15] = 16384,
348 	};
349 	u16 idx;
350 
351 	/* Scan from biggest to smallest. Choose supported size that is equal or
352 	 * smaller. For smaller value HW will under-utilize posted buffers. For
353 	 * bigger value HW may overrun posted buffers.
354 	 */
355 	idx = ARRAY_SIZE(supported_sizes);
356 	while (idx > 0) {
357 		idx--;
358 		if (supported_sizes[idx] <= rx_buf_sz) {
359 			*buf_sz_idx = idx;
360 			return 0;
361 		}
362 	}
363 
364 	return -EINVAL;
365 }
366 
367 int hinic3_set_root_ctxt(struct hinic3_hwdev *hwdev, u32 rq_depth, u32 sq_depth,
368 			 int rx_buf_sz)
369 {
370 	struct comm_cmd_set_root_ctxt root_ctxt = {};
371 	struct mgmt_msg_params msg_params = {};
372 	u16 buf_sz_idx;
373 	int err;
374 
375 	err = get_hw_rx_buf_size_idx(rx_buf_sz, &buf_sz_idx);
376 	if (err)
377 		return err;
378 
379 	root_ctxt.func_id = hinic3_global_func_id(hwdev);
380 
381 	root_ctxt.set_cmdq_depth = 0;
382 	root_ctxt.cmdq_depth = 0;
383 
384 	root_ctxt.lro_en = 1;
385 
386 	root_ctxt.rq_depth  = ilog2(rq_depth);
387 	root_ctxt.rx_buf_sz = buf_sz_idx;
388 	root_ctxt.sq_depth  = ilog2(sq_depth);
389 
390 	mgmt_msg_params_init_default(&msg_params, &root_ctxt,
391 				     sizeof(root_ctxt));
392 
393 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
394 				       COMM_CMD_SET_VAT, &msg_params);
395 	if (err || root_ctxt.head.status) {
396 		dev_err(hwdev->dev,
397 			"Failed to set root context, err: %d, status: 0x%x\n",
398 			err, root_ctxt.head.status);
399 		return -EFAULT;
400 	}
401 
402 	return 0;
403 }
404 
405 int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev)
406 {
407 	struct comm_cmd_set_root_ctxt root_ctxt = {};
408 	struct mgmt_msg_params msg_params = {};
409 	int err;
410 
411 	root_ctxt.func_id = hinic3_global_func_id(hwdev);
412 
413 	mgmt_msg_params_init_default(&msg_params, &root_ctxt,
414 				     sizeof(root_ctxt));
415 
416 	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
417 				       COMM_CMD_SET_VAT, &msg_params);
418 	if (err || root_ctxt.head.status) {
419 		dev_err(hwdev->dev,
420 			"Failed to set root context, err: %d, status: 0x%x\n",
421 			err, root_ctxt.head.status);
422 		return -EFAULT;
423 	}
424 
425 	return 0;
426 }
427