| /* |
| * Copyright (c) 2017, Mellanox Technologies. All rights reserved. |
| * |
| * This software is available to you under a choice of one of two |
| * licenses. You may choose to be licensed under the terms of the GNU |
| * General Public License (GPL) Version 2, available from the file |
| * COPYING in the main directory of this source tree, or the |
| * OpenIB.org BSD license below: |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <linux/etherdevice.h> |
| #include <linux/mlx5/driver.h> |
| #include <linux/mlx5/device.h> |
| |
| #include "mlx5_core.h" |
| #include "fpga/cmd.h" |
| |
| #define MLX5_FPGA_ACCESS_REG_SZ (MLX5_ST_SZ_DW(fpga_access_reg) + \ |
| MLX5_FPGA_ACCESS_REG_SIZE_MAX) |
| |
| int mlx5_fpga_access_reg(struct mlx5_core_dev *dev, u8 size, u64 addr, |
| void *buf, bool write) |
| { |
| u32 in[MLX5_FPGA_ACCESS_REG_SZ] = {0}; |
| u32 out[MLX5_FPGA_ACCESS_REG_SZ]; |
| int err; |
| |
| if (size & 3) |
| return -EINVAL; |
| if (addr & 3) |
| return -EINVAL; |
| if (size > MLX5_FPGA_ACCESS_REG_SIZE_MAX) |
| return -EINVAL; |
| |
| MLX5_SET(fpga_access_reg, in, size, size); |
| MLX5_SET64(fpga_access_reg, in, address, addr); |
| if (write) |
| memcpy(MLX5_ADDR_OF(fpga_access_reg, in, data), buf, size); |
| |
| err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), |
| MLX5_REG_FPGA_ACCESS_REG, 0, write); |
| if (err) |
| return err; |
| |
| if (!write) |
| memcpy(buf, MLX5_ADDR_OF(fpga_access_reg, out, data), size); |
| |
| return 0; |
| } |
| |
| int mlx5_fpga_caps(struct mlx5_core_dev *dev) |
| { |
| u32 in[MLX5_ST_SZ_DW(fpga_cap)] = {0}; |
| |
| return mlx5_core_access_reg(dev, in, sizeof(in), dev->caps.fpga, |
| MLX5_ST_SZ_BYTES(fpga_cap), |
| MLX5_REG_FPGA_CAP, 0, 0); |
| } |
| |
| int mlx5_fpga_ctrl_op(struct mlx5_core_dev *dev, u8 op) |
| { |
| u32 in[MLX5_ST_SZ_DW(fpga_ctrl)] = {0}; |
| u32 out[MLX5_ST_SZ_DW(fpga_ctrl)]; |
| |
| MLX5_SET(fpga_ctrl, in, operation, op); |
| |
| return mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), |
| MLX5_REG_FPGA_CTRL, 0, true); |
| } |
| |
| int mlx5_fpga_sbu_caps(struct mlx5_core_dev *dev, void *caps, int size) |
| { |
| unsigned int cap_size = MLX5_CAP_FPGA(dev, sandbox_extended_caps_len); |
| u64 addr = MLX5_CAP64_FPGA(dev, sandbox_extended_caps_addr); |
| unsigned int read; |
| int ret = 0; |
| |
| if (cap_size > size) { |
| mlx5_core_warn(dev, "Not enough buffer %u for FPGA SBU caps %u", |
| size, cap_size); |
| return -EINVAL; |
| } |
| |
| while (cap_size > 0) { |
| read = min_t(unsigned int, cap_size, |
| MLX5_FPGA_ACCESS_REG_SIZE_MAX); |
| |
| ret = mlx5_fpga_access_reg(dev, read, addr, caps, false); |
| if (ret) { |
| mlx5_core_warn(dev, "Error reading FPGA SBU caps %u bytes at address 0x%llx: %d", |
| read, addr, ret); |
| return ret; |
| } |
| |
| cap_size -= read; |
| addr += read; |
| caps += read; |
| } |
| |
| return ret; |
| } |
| |
| int mlx5_fpga_query(struct mlx5_core_dev *dev, struct mlx5_fpga_query *query) |
| { |
| u32 in[MLX5_ST_SZ_DW(fpga_ctrl)] = {0}; |
| u32 out[MLX5_ST_SZ_DW(fpga_ctrl)]; |
| int err; |
| |
| err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), |
| MLX5_REG_FPGA_CTRL, 0, false); |
| if (err) |
| return err; |
| |
| query->status = MLX5_GET(fpga_ctrl, out, status); |
| query->admin_image = MLX5_GET(fpga_ctrl, out, flash_select_admin); |
| query->oper_image = MLX5_GET(fpga_ctrl, out, flash_select_oper); |
| return 0; |
| } |
| |
| int mlx5_fpga_create_qp(struct mlx5_core_dev *dev, void *fpga_qpc, |
| u32 *fpga_qpn) |
| { |
| u32 out[MLX5_ST_SZ_DW(fpga_create_qp_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(fpga_create_qp_in)] = {}; |
| int ret; |
| |
| MLX5_SET(fpga_create_qp_in, in, opcode, MLX5_CMD_OP_FPGA_CREATE_QP); |
| memcpy(MLX5_ADDR_OF(fpga_create_qp_in, in, fpga_qpc), fpga_qpc, |
| MLX5_FLD_SZ_BYTES(fpga_create_qp_in, fpga_qpc)); |
| |
| ret = mlx5_cmd_exec_inout(dev, fpga_create_qp, in, out); |
| if (ret) |
| return ret; |
| |
| memcpy(fpga_qpc, MLX5_ADDR_OF(fpga_create_qp_out, out, fpga_qpc), |
| MLX5_FLD_SZ_BYTES(fpga_create_qp_out, fpga_qpc)); |
| *fpga_qpn = MLX5_GET(fpga_create_qp_out, out, fpga_qpn); |
| return ret; |
| } |
| |
| int mlx5_fpga_modify_qp(struct mlx5_core_dev *dev, u32 fpga_qpn, |
| enum mlx5_fpga_qpc_field_select fields, |
| void *fpga_qpc) |
| { |
| u32 in[MLX5_ST_SZ_DW(fpga_modify_qp_in)] = {}; |
| |
| MLX5_SET(fpga_modify_qp_in, in, opcode, MLX5_CMD_OP_FPGA_MODIFY_QP); |
| MLX5_SET(fpga_modify_qp_in, in, field_select, fields); |
| MLX5_SET(fpga_modify_qp_in, in, fpga_qpn, fpga_qpn); |
| memcpy(MLX5_ADDR_OF(fpga_modify_qp_in, in, fpga_qpc), fpga_qpc, |
| MLX5_FLD_SZ_BYTES(fpga_modify_qp_in, fpga_qpc)); |
| |
| return mlx5_cmd_exec_in(dev, fpga_modify_qp, in); |
| } |
| |
| int mlx5_fpga_query_qp(struct mlx5_core_dev *dev, |
| u32 fpga_qpn, void *fpga_qpc) |
| { |
| u32 out[MLX5_ST_SZ_DW(fpga_query_qp_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(fpga_query_qp_in)] = {}; |
| int ret; |
| |
| MLX5_SET(fpga_query_qp_in, in, opcode, MLX5_CMD_OP_FPGA_QUERY_QP); |
| MLX5_SET(fpga_query_qp_in, in, fpga_qpn, fpga_qpn); |
| |
| ret = mlx5_cmd_exec_inout(dev, fpga_query_qp, in, out); |
| if (ret) |
| return ret; |
| |
| memcpy(fpga_qpc, MLX5_ADDR_OF(fpga_query_qp_out, out, fpga_qpc), |
| MLX5_FLD_SZ_BYTES(fpga_query_qp_out, fpga_qpc)); |
| return ret; |
| } |
| |
| int mlx5_fpga_destroy_qp(struct mlx5_core_dev *dev, u32 fpga_qpn) |
| { |
| u32 in[MLX5_ST_SZ_DW(fpga_destroy_qp_in)] = {}; |
| |
| MLX5_SET(fpga_destroy_qp_in, in, opcode, MLX5_CMD_OP_FPGA_DESTROY_QP); |
| MLX5_SET(fpga_destroy_qp_in, in, fpga_qpn, fpga_qpn); |
| |
| return mlx5_cmd_exec_in(dev, fpga_destroy_qp, in); |
| } |
| |
| int mlx5_fpga_query_qp_counters(struct mlx5_core_dev *dev, u32 fpga_qpn, |
| bool clear, struct mlx5_fpga_qp_counters *data) |
| { |
| u32 out[MLX5_ST_SZ_DW(fpga_query_qp_counters_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(fpga_query_qp_counters_in)] = {}; |
| int ret; |
| |
| MLX5_SET(fpga_query_qp_counters_in, in, opcode, |
| MLX5_CMD_OP_FPGA_QUERY_QP_COUNTERS); |
| MLX5_SET(fpga_query_qp_counters_in, in, clear, clear); |
| MLX5_SET(fpga_query_qp_counters_in, in, fpga_qpn, fpga_qpn); |
| |
| ret = mlx5_cmd_exec_inout(dev, fpga_query_qp_counters, in, out); |
| if (ret) |
| return ret; |
| |
| data->rx_ack_packets = MLX5_GET64(fpga_query_qp_counters_out, out, |
| rx_ack_packets); |
| data->rx_send_packets = MLX5_GET64(fpga_query_qp_counters_out, out, |
| rx_send_packets); |
| data->tx_ack_packets = MLX5_GET64(fpga_query_qp_counters_out, out, |
| tx_ack_packets); |
| data->tx_send_packets = MLX5_GET64(fpga_query_qp_counters_out, out, |
| tx_send_packets); |
| data->rx_total_drop = MLX5_GET64(fpga_query_qp_counters_out, out, |
| rx_total_drop); |
| |
| return ret; |
| } |