xref: /freebsd/sys/dev/mlx5/mlx5_fpga/mlx5fpga_sdk.c (revision 95ee2897)
1 /*-
2  * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32 
33 #include <linux/errno.h>
34 #include <linux/err.h>
35 #include <linux/completion.h>
36 #include <dev/mlx5/device.h>
37 #include <dev/mlx5/mlx5_fpga/core.h>
38 #include <dev/mlx5/mlx5_fpga/conn.h>
39 #include <dev/mlx5/mlx5_fpga/sdk.h>
40 #include <dev/mlx5/mlx5_fpga/xfer.h>
41 #include <dev/mlx5/mlx5_core/mlx5_core.h>
42 /* #include "accel/ipsec.h" */
43 
44 #define MLX5_FPGA_LOAD_TIMEOUT 25000 /* msec */
45 
46 struct mem_transfer {
47 	struct mlx5_fpga_transaction t;
48 	struct completion comp;
49 	u8 status;
50 };
51 
52 struct mlx5_fpga_conn *
mlx5_fpga_sbu_conn_create(struct mlx5_fpga_device * fdev,struct mlx5_fpga_conn_attr * attr)53 mlx5_fpga_sbu_conn_create(struct mlx5_fpga_device *fdev,
54 			  struct mlx5_fpga_conn_attr *attr)
55 {
56 #ifdef NOT_YET
57 	/* XXXKIB */
58 	return mlx5_fpga_conn_create(fdev, attr, MLX5_FPGA_QPC_QP_TYPE_SANDBOX_QP);
59 #else
60 	return (NULL);
61 #endif
62 }
63 EXPORT_SYMBOL(mlx5_fpga_sbu_conn_create);
64 
mlx5_fpga_sbu_conn_destroy(struct mlx5_fpga_conn * conn)65 void mlx5_fpga_sbu_conn_destroy(struct mlx5_fpga_conn *conn)
66 {
67 #ifdef NOT_YET
68 	/* XXXKIB */
69 	mlx5_fpga_conn_destroy(conn);
70 #endif
71 }
72 EXPORT_SYMBOL(mlx5_fpga_sbu_conn_destroy);
73 
mlx5_fpga_sbu_conn_sendmsg(struct mlx5_fpga_conn * conn,struct mlx5_fpga_dma_buf * buf)74 int mlx5_fpga_sbu_conn_sendmsg(struct mlx5_fpga_conn *conn,
75 			       struct mlx5_fpga_dma_buf *buf)
76 {
77 #ifdef NOT_YET
78 	/* XXXKIB */
79 	return mlx5_fpga_conn_send(conn, buf);
80 #else
81 	return (0);
82 #endif
83 }
84 EXPORT_SYMBOL(mlx5_fpga_sbu_conn_sendmsg);
85 
mem_complete(const struct mlx5_fpga_transaction * complete,u8 status)86 static void mem_complete(const struct mlx5_fpga_transaction *complete,
87 			 u8 status)
88 {
89 	struct mem_transfer *xfer;
90 
91 	mlx5_fpga_dbg(complete->conn->fdev,
92 		      "transaction %p complete status %u", complete, status);
93 
94 	xfer = container_of(complete, struct mem_transfer, t);
95 	xfer->status = status;
96 	complete_all(&xfer->comp);
97 }
98 
mem_transaction(struct mlx5_fpga_device * fdev,size_t size,u64 addr,void * buf,enum mlx5_fpga_direction direction)99 static int mem_transaction(struct mlx5_fpga_device *fdev, size_t size, u64 addr,
100 			   void *buf, enum mlx5_fpga_direction direction)
101 {
102 	int ret;
103 	struct mem_transfer xfer;
104 
105 	if (!fdev->shell_conn) {
106 		ret = -ENOTCONN;
107 		goto out;
108 	}
109 
110 	xfer.t.data = buf;
111 	xfer.t.size = size;
112 	xfer.t.addr = addr;
113 	xfer.t.conn = fdev->shell_conn;
114 	xfer.t.direction = direction;
115 	xfer.t.complete1 = mem_complete;
116 	init_completion(&xfer.comp);
117 	ret = mlx5_fpga_xfer_exec(&xfer.t);
118 	if (ret) {
119 		mlx5_fpga_dbg(fdev, "Transfer execution failed: %d\n", ret);
120 		goto out;
121 	}
122 	wait_for_completion(&xfer.comp);
123 	if (xfer.status != 0)
124 		ret = -EIO;
125 
126 out:
127 	return ret;
128 }
129 
mlx5_fpga_mem_read_i2c(struct mlx5_fpga_device * fdev,size_t size,u64 addr,u8 * buf)130 static int mlx5_fpga_mem_read_i2c(struct mlx5_fpga_device *fdev, size_t size,
131 				  u64 addr, u8 *buf)
132 {
133 	size_t max_size = MLX5_FPGA_ACCESS_REG_SIZE_MAX;
134 	size_t bytes_done = 0;
135 	u8 actual_size;
136 	int err = 0;
137 
138 	if (!size)
139 		return -EINVAL;
140 
141 	if (!fdev->mdev)
142 		return -ENOTCONN;
143 
144 	while (bytes_done < size) {
145 		actual_size = min(max_size, (size - bytes_done));
146 
147 		err = mlx5_fpga_access_reg(fdev->mdev, actual_size,
148 					   addr + bytes_done,
149 					   buf + bytes_done, false);
150 		if (err) {
151 			mlx5_fpga_err(fdev, "Failed to read over I2C: %d\n",
152 				      err);
153 			break;
154 		}
155 
156 		bytes_done += actual_size;
157 	}
158 
159 	return err;
160 }
161 
mlx5_fpga_mem_write_i2c(struct mlx5_fpga_device * fdev,size_t size,u64 addr,u8 * buf)162 static int mlx5_fpga_mem_write_i2c(struct mlx5_fpga_device *fdev, size_t size,
163 				   u64 addr, u8 *buf)
164 {
165 	size_t max_size = MLX5_FPGA_ACCESS_REG_SIZE_MAX;
166 	size_t bytes_done = 0;
167 	u8 actual_size;
168 	int err = 0;
169 
170 	if (!size)
171 		return -EINVAL;
172 
173 	if (!fdev->mdev)
174 		return -ENOTCONN;
175 
176 	while (bytes_done < size) {
177 		actual_size = min(max_size, (size - bytes_done));
178 
179 		err = mlx5_fpga_access_reg(fdev->mdev, actual_size,
180 					   addr + bytes_done,
181 					   buf + bytes_done, true);
182 		if (err) {
183 			mlx5_fpga_err(fdev, "Failed to write FPGA crspace\n");
184 			break;
185 		}
186 
187 		bytes_done += actual_size;
188 	}
189 
190 	return err;
191 }
192 
mlx5_fpga_mem_read(struct mlx5_fpga_device * fdev,size_t size,u64 addr,void * buf,enum mlx5_fpga_access_type access_type)193 int mlx5_fpga_mem_read(struct mlx5_fpga_device *fdev, size_t size, u64 addr,
194 		       void *buf, enum mlx5_fpga_access_type access_type)
195 {
196 	int ret;
197 
198 	if (access_type == MLX5_FPGA_ACCESS_TYPE_DONTCARE)
199 		access_type = fdev->shell_conn ? MLX5_FPGA_ACCESS_TYPE_RDMA :
200 						 MLX5_FPGA_ACCESS_TYPE_I2C;
201 
202 	mlx5_fpga_dbg(fdev, "Reading %zu bytes at 0x%jx over %s",
203 		      size, (uintmax_t)addr, access_type ? "RDMA" : "I2C");
204 
205 	switch (access_type) {
206 	case MLX5_FPGA_ACCESS_TYPE_RDMA:
207 		ret = mem_transaction(fdev, size, addr, buf, MLX5_FPGA_READ);
208 		if (ret)
209 			return ret;
210 		break;
211 	case MLX5_FPGA_ACCESS_TYPE_I2C:
212 		ret = mlx5_fpga_mem_read_i2c(fdev, size, addr, buf);
213 		if (ret)
214 			return ret;
215 		break;
216 	default:
217 		mlx5_fpga_warn(fdev, "Unexpected read access_type %u\n",
218 			       access_type);
219 		return -EACCES;
220 	}
221 
222 	return size;
223 }
224 EXPORT_SYMBOL(mlx5_fpga_mem_read);
225 
mlx5_fpga_mem_write(struct mlx5_fpga_device * fdev,size_t size,u64 addr,void * buf,enum mlx5_fpga_access_type access_type)226 int mlx5_fpga_mem_write(struct mlx5_fpga_device *fdev, size_t size, u64 addr,
227 			void *buf, enum mlx5_fpga_access_type access_type)
228 {
229 	int ret;
230 
231 	if (access_type == MLX5_FPGA_ACCESS_TYPE_DONTCARE)
232 		access_type = fdev->shell_conn ? MLX5_FPGA_ACCESS_TYPE_RDMA :
233 						 MLX5_FPGA_ACCESS_TYPE_I2C;
234 
235 	mlx5_fpga_dbg(fdev, "Writing %zu bytes at 0x%jx over %s",
236 		      size, (uintmax_t)addr, access_type ? "RDMA" : "I2C");
237 
238 	switch (access_type) {
239 	case MLX5_FPGA_ACCESS_TYPE_RDMA:
240 		ret = mem_transaction(fdev, size, addr, buf, MLX5_FPGA_WRITE);
241 		if (ret)
242 			return ret;
243 		break;
244 	case MLX5_FPGA_ACCESS_TYPE_I2C:
245 		ret = mlx5_fpga_mem_write_i2c(fdev, size, addr, buf);
246 		if (ret)
247 			return ret;
248 		break;
249 	default:
250 		mlx5_fpga_warn(fdev, "Unexpected write access_type %u\n",
251 			       access_type);
252 		return -EACCES;
253 	}
254 
255 	return size;
256 }
257 EXPORT_SYMBOL(mlx5_fpga_mem_write);
258 
mlx5_fpga_get_sbu_caps(struct mlx5_fpga_device * fdev,int size,void * buf)259 int mlx5_fpga_get_sbu_caps(struct mlx5_fpga_device *fdev, int size, void *buf)
260 {
261 	return mlx5_fpga_sbu_caps(fdev->mdev, buf, size);
262 }
263 EXPORT_SYMBOL(mlx5_fpga_get_sbu_caps);
264 
mlx5_fpga_ddr_size_get(struct mlx5_fpga_device * fdev)265 u64 mlx5_fpga_ddr_size_get(struct mlx5_fpga_device *fdev)
266 {
267 	return (u64)MLX5_CAP_FPGA(fdev->mdev, fpga_ddr_size) << 10;
268 }
269 EXPORT_SYMBOL(mlx5_fpga_ddr_size_get);
270 
mlx5_fpga_ddr_base_get(struct mlx5_fpga_device * fdev)271 u64 mlx5_fpga_ddr_base_get(struct mlx5_fpga_device *fdev)
272 {
273 	return MLX5_CAP64_FPGA(fdev->mdev, fpga_ddr_start_addr);
274 }
275 EXPORT_SYMBOL(mlx5_fpga_ddr_base_get);
276 
mlx5_fpga_client_data_set(struct mlx5_fpga_device * fdev,struct mlx5_fpga_client * client,void * data)277 void mlx5_fpga_client_data_set(struct mlx5_fpga_device *fdev,
278 			       struct mlx5_fpga_client *client, void *data)
279 {
280 	struct mlx5_fpga_client_data *context;
281 
282 	list_for_each_entry(context, &fdev->client_data_list, list) {
283 		if (context->client != client)
284 			continue;
285 		context->data = data;
286 		return;
287 	}
288 
289 	mlx5_fpga_warn(fdev, "No client context found for %s\n", client->name);
290 }
291 EXPORT_SYMBOL(mlx5_fpga_client_data_set);
292 
mlx5_fpga_client_data_get(struct mlx5_fpga_device * fdev,struct mlx5_fpga_client * client)293 void *mlx5_fpga_client_data_get(struct mlx5_fpga_device *fdev,
294 				struct mlx5_fpga_client *client)
295 {
296 	struct mlx5_fpga_client_data *context;
297 	void *ret = NULL;
298 
299 	list_for_each_entry(context, &fdev->client_data_list, list) {
300 		if (context->client != client)
301 			continue;
302 		ret = context->data;
303 		goto out;
304 	}
305 	mlx5_fpga_warn(fdev, "No client context found for %s\n", client->name);
306 
307 out:
308 	return ret;
309 }
310 EXPORT_SYMBOL(mlx5_fpga_client_data_get);
311 
mlx5_fpga_device_query(struct mlx5_fpga_device * fdev,struct mlx5_fpga_query * query)312 void mlx5_fpga_device_query(struct mlx5_fpga_device *fdev,
313 			    struct mlx5_fpga_query *query)
314 {
315 	unsigned long flags;
316 
317 	spin_lock_irqsave(&fdev->state_lock, flags);
318 	query->image_status = fdev->image_status;
319 	query->admin_image = fdev->last_admin_image;
320 	query->oper_image = fdev->last_oper_image;
321 	spin_unlock_irqrestore(&fdev->state_lock, flags);
322 }
323 EXPORT_SYMBOL(mlx5_fpga_device_query);
324 
mlx5_fpga_device_reload_cmd(struct mlx5_fpga_device * fdev)325 static int mlx5_fpga_device_reload_cmd(struct mlx5_fpga_device *fdev)
326 {
327 	struct mlx5_core_dev *mdev = fdev->mdev;
328 	unsigned long timeout;
329 	unsigned long flags;
330 	int err = 0;
331 
332 	mlx5_fpga_info(fdev, "mlx5/fpga - reload started\n");
333 	fdev->fdev_state = MLX5_FDEV_STATE_IN_PROGRESS;
334 	reinit_completion(&fdev->load_event);
335 	err = mlx5_fpga_ctrl_op(mdev, MLX5_FPGA_CTRL_OPERATION_RELOAD);
336 	if (err) {
337 		mlx5_fpga_err(fdev, "Failed to request reload: %d\n",
338 			      err);
339 		goto out;
340 	}
341 	timeout = jiffies + msecs_to_jiffies(MLX5_FPGA_LOAD_TIMEOUT);
342 	err = wait_for_completion_timeout(&fdev->load_event,
343 					  timeout - jiffies);
344 	if (err < 0) {
345 		mlx5_fpga_err(fdev, "Failed waiting for reload: %d\n", err);
346 		fdev->fdev_state = MLX5_FDEV_STATE_FAILURE;
347 		goto out;
348 	}
349 	/* Check device loaded successful */
350 	err = mlx5_fpga_device_start(mdev);
351 	if (err) {
352 		mlx5_fpga_err(fdev, "Failed load check for reload: %d\n", err);
353 		fdev->fdev_state = MLX5_FDEV_STATE_FAILURE;
354 		goto out;
355 	}
356 	spin_lock_irqsave(&fdev->state_lock, flags);
357 	fdev->fdev_state = MLX5_FDEV_STATE_SUCCESS;
358 	spin_unlock_irqrestore(&fdev->state_lock, flags);
359 	mlx5_fpga_info(fdev, "mlx5/fpga - reload ended\n");
360 out:
361 	return err;
362 }
363 
mlx5_fpga_device_reload(struct mlx5_fpga_device * fdev,enum mlx5_fpga_image image)364 int mlx5_fpga_device_reload(struct mlx5_fpga_device *fdev,
365 			    enum mlx5_fpga_image image)
366 {
367 	struct mlx5_core_dev *mdev = fdev->mdev;
368 	unsigned long timeout;
369 	unsigned long flags;
370 	int err = 0;
371 
372 	spin_lock_irqsave(&fdev->state_lock, flags);
373 	switch (fdev->fdev_state) {
374 	case MLX5_FDEV_STATE_NONE:
375 		err = -ENODEV;
376 		break;
377 	case MLX5_FDEV_STATE_IN_PROGRESS:
378 		err = -EBUSY;
379 		break;
380 	case MLX5_FDEV_STATE_SUCCESS:
381 	case MLX5_FDEV_STATE_FAILURE:
382 	case MLX5_FDEV_STATE_DISCONNECTED:
383 		break;
384 	}
385 	spin_unlock_irqrestore(&fdev->state_lock, flags);
386 	if (err)
387 		return err;
388 
389 	mutex_lock(&mdev->intf_state_mutex);
390 
391 	if (image == MLX5_FPGA_IMAGE_RELOAD) {
392 		err = mlx5_fpga_device_reload_cmd(fdev);
393 		goto out;
394 	}
395 
396 	clear_bit(MLX5_INTERFACE_STATE_UP, &mdev->intf_state);
397 
398 	mlx5_unregister_device(mdev);
399 	/* XXXKIB	mlx5_accel_ipsec_cleanup(mdev); */
400 	mlx5_fpga_device_stop(mdev);
401 
402 	fdev->fdev_state = MLX5_FDEV_STATE_IN_PROGRESS;
403 	reinit_completion(&fdev->load_event);
404 
405 	if (image <= MLX5_FPGA_IMAGE_FACTORY) {
406 		mlx5_fpga_info(fdev, "Loading from flash\n");
407 		err = mlx5_fpga_load(mdev, image);
408 		if (err) {
409 			mlx5_fpga_err(fdev, "Failed to request load: %d\n",
410 				      err);
411 			goto out;
412 		}
413 	} else if (image == MLX5_FPGA_IMAGE_RESET) {
414 		mlx5_fpga_info(fdev, "Resetting\n");
415 		err = mlx5_fpga_ctrl_op(mdev, MLX5_FPGA_CTRL_OPERATION_RESET);
416 		if (err) {
417 			mlx5_fpga_err(fdev, "Failed to request reset: %d\n",
418 				      err);
419 			goto out;
420 		}
421 	} else {
422 		mlx5_fpga_err(fdev, "Unknown command: %d\n",
423 			      image);
424 		goto out;
425 	}
426 
427 	timeout = jiffies + msecs_to_jiffies(MLX5_FPGA_LOAD_TIMEOUT);
428 	err = wait_for_completion_timeout(&fdev->load_event, timeout - jiffies);
429 	if (err < 0) {
430 		mlx5_fpga_err(fdev, "Failed waiting for FPGA load: %d\n", err);
431 		fdev->fdev_state = MLX5_FDEV_STATE_FAILURE;
432 		goto out;
433 	}
434 
435 	err = mlx5_fpga_device_start(mdev);
436 	if (err) {
437 		mlx5_core_err(mdev, "fpga device start failed %d\n", err);
438 		goto out;
439 	}
440 	/* XXXKIB err = mlx5_accel_ipsec_init(mdev); */
441 	if (err) {
442 		mlx5_core_err(mdev, "IPSec device start failed %d\n", err);
443 		goto err_fpga;
444 	}
445 
446 	err = mlx5_register_device(mdev);
447 	if (err) {
448 		mlx5_core_err(mdev, "mlx5_register_device failed %d\n", err);
449 		fdev->fdev_state = MLX5_FDEV_STATE_FAILURE;
450 		goto err_ipsec;
451 	}
452 
453 	set_bit(MLX5_INTERFACE_STATE_UP, &mdev->intf_state);
454 	goto out;
455 
456 err_ipsec:
457 	/* XXXKIB mlx5_accel_ipsec_cleanup(mdev); */
458 err_fpga:
459 	mlx5_fpga_device_stop(mdev);
460 out:
461 	mutex_unlock(&mdev->intf_state_mutex);
462 	return err;
463 }
464 EXPORT_SYMBOL(mlx5_fpga_device_reload);
465 
mlx5_fpga_flash_select(struct mlx5_fpga_device * fdev,enum mlx5_fpga_image image)466 int mlx5_fpga_flash_select(struct mlx5_fpga_device *fdev,
467 			   enum mlx5_fpga_image image)
468 {
469 	unsigned long flags;
470 	int err;
471 
472 	spin_lock_irqsave(&fdev->state_lock, flags);
473 	switch (fdev->fdev_state) {
474 	case MLX5_FDEV_STATE_NONE:
475 		spin_unlock_irqrestore(&fdev->state_lock, flags);
476 		return -ENODEV;
477 	case MLX5_FDEV_STATE_DISCONNECTED:
478 	case MLX5_FDEV_STATE_IN_PROGRESS:
479 	case MLX5_FDEV_STATE_SUCCESS:
480 	case MLX5_FDEV_STATE_FAILURE:
481 		break;
482 	}
483 	spin_unlock_irqrestore(&fdev->state_lock, flags);
484 
485 	err = mlx5_fpga_image_select(fdev->mdev, image);
486 	if (err)
487 		mlx5_fpga_err(fdev, "Failed to select flash image: %d\n", err);
488 	else
489 		fdev->last_admin_image = image;
490 	return err;
491 }
492 EXPORT_SYMBOL(mlx5_fpga_flash_select);
493 
mlx5_fpga_connectdisconnect(struct mlx5_fpga_device * fdev,enum mlx5_fpga_connect * connect)494 int mlx5_fpga_connectdisconnect(struct mlx5_fpga_device *fdev,
495 				enum mlx5_fpga_connect *connect)
496 {
497 	unsigned long flags;
498 	int err;
499 
500 	spin_lock_irqsave(&fdev->state_lock, flags);
501 	switch (fdev->fdev_state) {
502 	case MLX5_FDEV_STATE_NONE:
503 		spin_unlock_irqrestore(&fdev->state_lock, flags);
504 		return -ENODEV;
505 	case MLX5_FDEV_STATE_IN_PROGRESS:
506 	case MLX5_FDEV_STATE_SUCCESS:
507 	case MLX5_FDEV_STATE_FAILURE:
508 	case MLX5_FDEV_STATE_DISCONNECTED:
509 		break;
510 	}
511 	spin_unlock_irqrestore(&fdev->state_lock, flags);
512 
513 	err = mlx5_fpga_ctrl_connect(fdev->mdev, connect);
514 	if (err)
515 		mlx5_fpga_err(fdev, "Failed to connect/disconnect: %d\n", err);
516 	return err;
517 }
518 EXPORT_SYMBOL(mlx5_fpga_connectdisconnect);
519 
mlx5_fpga_temperature(struct mlx5_fpga_device * fdev,struct mlx5_fpga_temperature * temp)520 int mlx5_fpga_temperature(struct mlx5_fpga_device *fdev,
521 			  struct mlx5_fpga_temperature *temp)
522 {
523 	return mlx5_fpga_query_mtmp(fdev->mdev, temp);
524 }
525 EXPORT_SYMBOL(mlx5_fpga_temperature);
526 
mlx5_fpga_dev(struct mlx5_fpga_device * fdev)527 struct device *mlx5_fpga_dev(struct mlx5_fpga_device *fdev)
528 {
529 	return &fdev->mdev->pdev->dev;
530 }
531 EXPORT_SYMBOL(mlx5_fpga_dev);
532 
mlx5_fpga_get_cap(struct mlx5_fpga_device * fdev,u32 * fpga_caps)533 void mlx5_fpga_get_cap(struct mlx5_fpga_device *fdev, u32 *fpga_caps)
534 {
535 	unsigned long flags;
536 
537 	spin_lock_irqsave(&fdev->state_lock, flags);
538 	memcpy(fpga_caps, &fdev->mdev->caps.fpga, sizeof(fdev->mdev->caps.fpga));
539 	spin_unlock_irqrestore(&fdev->state_lock, flags);
540 }
541 EXPORT_SYMBOL(mlx5_fpga_get_cap);
542