linux/linux-5.4.31/drivers/net/ethernet/huawei/hinic/hinic_port.c

1071 lines
28 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Huawei HiNIC PCI Express Linux driver
* Copyright(c) 2017 Huawei Technologies Co., Ltd
*/
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/errno.h>
#include "hinic_hw_if.h"
#include "hinic_hw_dev.h"
#include "hinic_port.h"
#include "hinic_dev.h"
#define HINIC_MIN_MTU_SIZE 256
#define HINIC_MAX_JUMBO_FRAME_SIZE 15872
enum mac_op {
MAC_DEL,
MAC_SET,
};
/**
* change_mac - change(add or delete) mac address
* @nic_dev: nic device
* @addr: mac address
* @vlan_id: vlan number to set with the mac
* @op: add or delete the mac
*
* Return 0 - Success, negative - Failure
**/
static int change_mac(struct hinic_dev *nic_dev, const u8 *addr,
u16 vlan_id, enum mac_op op)
{
struct net_device *netdev = nic_dev->netdev;
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_port_mac_cmd port_mac_cmd;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
enum hinic_port_cmd cmd;
u16 out_size;
int err;
if (vlan_id >= VLAN_N_VID) {
netif_err(nic_dev, drv, netdev, "Invalid VLAN number\n");
return -EINVAL;
}
if (op == MAC_SET)
cmd = HINIC_PORT_CMD_SET_MAC;
else
cmd = HINIC_PORT_CMD_DEL_MAC;
port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
port_mac_cmd.vlan_id = vlan_id;
memcpy(port_mac_cmd.mac, addr, ETH_ALEN);
err = hinic_port_msg_cmd(hwdev, cmd, &port_mac_cmd,
sizeof(port_mac_cmd),
&port_mac_cmd, &out_size);
if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) {
dev_err(&pdev->dev, "Failed to change MAC, ret = %d\n",
port_mac_cmd.status);
return -EFAULT;
}
return 0;
}
/**
* hinic_port_add_mac - add mac address
* @nic_dev: nic device
* @addr: mac address
* @vlan_id: vlan number to set with the mac
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_add_mac(struct hinic_dev *nic_dev,
const u8 *addr, u16 vlan_id)
{
return change_mac(nic_dev, addr, vlan_id, MAC_SET);
}
/**
* hinic_port_del_mac - remove mac address
* @nic_dev: nic device
* @addr: mac address
* @vlan_id: vlan number that is connected to the mac
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_del_mac(struct hinic_dev *nic_dev, const u8 *addr,
u16 vlan_id)
{
return change_mac(nic_dev, addr, vlan_id, MAC_DEL);
}
/**
* hinic_port_get_mac - get the mac address of the nic device
* @nic_dev: nic device
* @addr: returned mac address
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_port_mac_cmd port_mac_cmd;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_MAC,
&port_mac_cmd, sizeof(port_mac_cmd),
&port_mac_cmd, &out_size);
if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) {
dev_err(&pdev->dev, "Failed to get mac, ret = %d\n",
port_mac_cmd.status);
return -EFAULT;
}
memcpy(addr, port_mac_cmd.mac, ETH_ALEN);
return 0;
}
/**
* hinic_port_set_mtu - set mtu
* @nic_dev: nic device
* @new_mtu: new mtu
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu)
{
struct net_device *netdev = nic_dev->netdev;
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_port_mtu_cmd port_mtu_cmd;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
int err, max_frame;
u16 out_size;
if (new_mtu < HINIC_MIN_MTU_SIZE) {
netif_err(nic_dev, drv, netdev, "mtu < MIN MTU size");
return -EINVAL;
}
max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
if (max_frame > HINIC_MAX_JUMBO_FRAME_SIZE) {
netif_err(nic_dev, drv, netdev, "mtu > MAX MTU size");
return -EINVAL;
}
port_mtu_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
port_mtu_cmd.mtu = new_mtu;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_CHANGE_MTU,
&port_mtu_cmd, sizeof(port_mtu_cmd),
&port_mtu_cmd, &out_size);
if (err || (out_size != sizeof(port_mtu_cmd)) || port_mtu_cmd.status) {
dev_err(&pdev->dev, "Failed to set mtu, ret = %d\n",
port_mtu_cmd.status);
return -EFAULT;
}
return 0;
}
/**
* hinic_port_add_vlan - add vlan to the nic device
* @nic_dev: nic device
* @vlan_id: the vlan number to add
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_add_vlan(struct hinic_dev *nic_dev, u16 vlan_id)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_port_vlan_cmd port_vlan_cmd;
port_vlan_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
port_vlan_cmd.vlan_id = vlan_id;
return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_ADD_VLAN,
&port_vlan_cmd, sizeof(port_vlan_cmd),
NULL, NULL);
}
/**
* hinic_port_del_vlan - delete vlan from the nic device
* @nic_dev: nic device
* @vlan_id: the vlan number to delete
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_port_vlan_cmd port_vlan_cmd;
port_vlan_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
port_vlan_cmd.vlan_id = vlan_id;
return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_DEL_VLAN,
&port_vlan_cmd, sizeof(port_vlan_cmd),
NULL, NULL);
}
/**
* hinic_port_set_rx_mode - set rx mode in the nic device
* @nic_dev: nic device
* @rx_mode: the rx mode to set
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_set_rx_mode(struct hinic_dev *nic_dev, u32 rx_mode)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_port_rx_mode_cmd rx_mode_cmd;
rx_mode_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
rx_mode_cmd.rx_mode = rx_mode;
return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_MODE,
&rx_mode_cmd, sizeof(rx_mode_cmd),
NULL, NULL);
}
/**
* hinic_port_link_state - get the link state
* @nic_dev: nic device
* @link_state: the returned link state
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_link_state(struct hinic_dev *nic_dev,
enum hinic_port_link_state *link_state)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct hinic_port_link_cmd link_cmd;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
dev_err(&pdev->dev, "unsupported PCI Function type\n");
return -EINVAL;
}
link_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_STATE,
&link_cmd, sizeof(link_cmd),
&link_cmd, &out_size);
if (err || (out_size != sizeof(link_cmd)) || link_cmd.status) {
dev_err(&pdev->dev, "Failed to get link state, ret = %d\n",
link_cmd.status);
return -EINVAL;
}
*link_state = link_cmd.state;
return 0;
}
/**
* hinic_port_set_state - set port state
* @nic_dev: nic device
* @state: the state to set
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_port_state_cmd port_state;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
dev_err(&pdev->dev, "unsupported PCI Function type\n");
return -EINVAL;
}
port_state.state = state;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PORT_STATE,
&port_state, sizeof(port_state),
&port_state, &out_size);
if (err || (out_size != sizeof(port_state)) || port_state.status) {
dev_err(&pdev->dev, "Failed to set port state, ret = %d\n",
port_state.status);
return -EFAULT;
}
return 0;
}
/**
* hinic_port_set_func_state- set func device state
* @nic_dev: nic device
* @state: the state to set
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_set_func_state(struct hinic_dev *nic_dev,
enum hinic_func_port_state state)
{
struct hinic_port_func_state_cmd func_state;
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
func_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
func_state.state = state;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_FUNC_STATE,
&func_state, sizeof(func_state),
&func_state, &out_size);
if (err || (out_size != sizeof(func_state)) || func_state.status) {
dev_err(&pdev->dev, "Failed to set port func state, ret = %d\n",
func_state.status);
return -EFAULT;
}
return 0;
}
/**
* hinic_port_get_cap - get port capabilities
* @nic_dev: nic device
* @port_cap: returned port capabilities
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_get_cap(struct hinic_dev *nic_dev,
struct hinic_port_cap *port_cap)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
port_cap->func_idx = HINIC_HWIF_FUNC_IDX(hwif);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_CAP,
port_cap, sizeof(*port_cap),
port_cap, &out_size);
if (err || (out_size != sizeof(*port_cap)) || port_cap->status) {
dev_err(&pdev->dev,
"Failed to get port capabilities, ret = %d\n",
port_cap->status);
return -EINVAL;
}
return 0;
}
/**
* hinic_port_set_tso - set port tso configuration
* @nic_dev: nic device
* @state: the tso state to set
*
* Return 0 - Success, negative - Failure
**/
int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct hinic_tso_config tso_cfg = {0};
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
tso_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
tso_cfg.tso_en = state;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_TSO,
&tso_cfg, sizeof(tso_cfg),
&tso_cfg, &out_size);
if (err || out_size != sizeof(tso_cfg) || tso_cfg.status) {
dev_err(&pdev->dev,
"Failed to set port tso, ret = %d\n",
tso_cfg.status);
return -EINVAL;
}
return 0;
}
int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en)
{
struct hinic_checksum_offload rx_csum_cfg = {0};
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u16 out_size;
int err;
if (!hwdev)
return -EINVAL;
hwif = hwdev->hwif;
pdev = hwif->pdev;
rx_csum_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
rx_csum_cfg.rx_csum_offload = en;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_CSUM,
&rx_csum_cfg, sizeof(rx_csum_cfg),
&rx_csum_cfg, &out_size);
if (err || !out_size || rx_csum_cfg.status) {
dev_err(&pdev->dev,
"Failed to set rx csum offload, ret = %d\n",
rx_csum_cfg.status);
return -EINVAL;
}
return 0;
}
int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_vlan_cfg vlan_cfg;
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u16 out_size;
int err;
if (!hwdev)
return -EINVAL;
hwif = hwdev->hwif;
pdev = hwif->pdev;
vlan_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
vlan_cfg.vlan_rx_offload = en;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD,
&vlan_cfg, sizeof(vlan_cfg),
&vlan_cfg, &out_size);
if (err || !out_size || vlan_cfg.status) {
dev_err(&pdev->dev,
"Failed to set rx vlan offload, err: %d, status: 0x%x, out size: 0x%x\n",
err, vlan_cfg.status, out_size);
return -EINVAL;
}
return 0;
}
int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_rq_num rq_num = { 0 };
u16 out_size = sizeof(rq_num);
int err;
rq_num.func_id = HINIC_HWIF_FUNC_IDX(hwif);
rq_num.num_rqs = num_rqs;
rq_num.rq_depth = ilog2(HINIC_SQ_DEPTH);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RQ_IQ_MAP,
&rq_num, sizeof(rq_num),
&rq_num, &out_size);
if (err || !out_size || rq_num.status) {
dev_err(&pdev->dev,
"Failed to rxq number, ret = %d\n",
rq_num.status);
return -EINVAL;
}
return 0;
}
static int hinic_set_rx_lro(struct hinic_dev *nic_dev, u8 ipv4_en, u8 ipv6_en,
u8 max_wqe_num)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct hinic_lro_config lro_cfg = { 0 };
struct pci_dev *pdev = hwif->pdev;
u16 out_size = sizeof(lro_cfg);
int err;
lro_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
lro_cfg.lro_ipv4_en = ipv4_en;
lro_cfg.lro_ipv6_en = ipv6_en;
lro_cfg.lro_max_wqe_num = max_wqe_num;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LRO,
&lro_cfg, sizeof(lro_cfg),
&lro_cfg, &out_size);
if (err || !out_size || lro_cfg.status) {
dev_err(&pdev->dev,
"Failed to set lro offload, ret = %d\n",
lro_cfg.status);
return -EINVAL;
}
return 0;
}
static int hinic_set_rx_lro_timer(struct hinic_dev *nic_dev, u32 timer_value)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_lro_timer lro_timer = { 0 };
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size = sizeof(lro_timer);
int err;
lro_timer.status = 0;
lro_timer.type = 0;
lro_timer.enable = 1;
lro_timer.timer = timer_value;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LRO_TIMER,
&lro_timer, sizeof(lro_timer),
&lro_timer, &out_size);
if (lro_timer.status == 0xFF) {
/* For this case, we think status (0xFF) is OK */
lro_timer.status = 0;
dev_dbg(&pdev->dev,
"Set lro timer not supported by the current FW version, it will be 1ms default\n");
}
if (err || !out_size || lro_timer.status) {
dev_err(&pdev->dev,
"Failed to set lro timer, ret = %d\n",
lro_timer.status);
return -EINVAL;
}
return 0;
}
int hinic_set_rx_lro_state(struct hinic_dev *nic_dev, u8 lro_en,
u32 lro_timer, u32 wqe_num)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
u8 ipv4_en;
u8 ipv6_en;
int err;
if (!hwdev)
return -EINVAL;
ipv4_en = lro_en ? 1 : 0;
ipv6_en = lro_en ? 1 : 0;
err = hinic_set_rx_lro(nic_dev, ipv4_en, ipv6_en, (u8)wqe_num);
if (err)
return err;
err = hinic_set_rx_lro_timer(nic_dev, lro_timer);
if (err)
return err;
return 0;
}
int hinic_rss_set_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx,
const u32 *indir_table)
{
struct hinic_rss_indirect_tbl *indir_tbl;
struct hinic_func_to_io *func_to_io;
struct hinic_cmdq_buf cmd_buf;
struct hinic_hwdev *hwdev;
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u32 indir_size;
u64 out_param;
int err, i;
u32 *temp;
hwdev = nic_dev->hwdev;
func_to_io = &hwdev->func_to_io;
hwif = hwdev->hwif;
pdev = hwif->pdev;
err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmd_buf);
if (err) {
dev_err(&pdev->dev, "Failed to allocate cmdq buf\n");
return err;
}
cmd_buf.size = sizeof(*indir_tbl);
indir_tbl = cmd_buf.buf;
indir_tbl->group_index = cpu_to_be32(tmpl_idx);
for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) {
indir_tbl->entry[i] = indir_table[i];
if (0x3 == (i & 0x3)) {
temp = (u32 *)&indir_tbl->entry[i - 3];
*temp = cpu_to_be32(*temp);
}
}
/* cfg the rss indirect table by command queue */
indir_size = HINIC_RSS_INDIR_SIZE / 2;
indir_tbl->offset = 0;
indir_tbl->size = cpu_to_be32(indir_size);
err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC,
HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE,
&cmd_buf, &out_param);
if (err || out_param != 0) {
dev_err(&pdev->dev, "Failed to set rss indir table\n");
err = -EFAULT;
goto free_buf;
}
indir_tbl->offset = cpu_to_be32(indir_size);
indir_tbl->size = cpu_to_be32(indir_size);
memcpy(&indir_tbl->entry[0], &indir_tbl->entry[indir_size], indir_size);
err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC,
HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE,
&cmd_buf, &out_param);
if (err || out_param != 0) {
dev_err(&pdev->dev, "Failed to set rss indir table\n");
err = -EFAULT;
}
free_buf:
hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmd_buf);
return err;
}
int hinic_rss_get_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx,
u32 *indir_table)
{
struct hinic_rss_indir_table rss_cfg = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size = sizeof(rss_cfg);
int err = 0, i;
rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
rss_cfg.template_id = tmpl_idx;
err = hinic_port_msg_cmd(hwdev,
HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL,
&rss_cfg, sizeof(rss_cfg), &rss_cfg,
&out_size);
if (err || !out_size || rss_cfg.status) {
dev_err(&pdev->dev, "Failed to get indir table, err: %d, status: 0x%x, out size: 0x%x\n",
err, rss_cfg.status, out_size);
return -EINVAL;
}
hinic_be32_to_cpu(rss_cfg.indir, HINIC_RSS_INDIR_SIZE);
for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++)
indir_table[i] = rss_cfg.indir[i];
return 0;
}
int hinic_set_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx,
struct hinic_rss_type rss_type)
{
struct hinic_rss_context_tbl *ctx_tbl;
struct hinic_func_to_io *func_to_io;
struct hinic_cmdq_buf cmd_buf;
struct hinic_hwdev *hwdev;
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u64 out_param;
u32 ctx = 0;
int err;
hwdev = nic_dev->hwdev;
func_to_io = &hwdev->func_to_io;
hwif = hwdev->hwif;
pdev = hwif->pdev;
err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmd_buf);
if (err) {
dev_err(&pdev->dev, "Failed to allocate cmd buf\n");
return -ENOMEM;
}
ctx |= HINIC_RSS_TYPE_SET(1, VALID) |
HINIC_RSS_TYPE_SET(rss_type.ipv4, IPV4) |
HINIC_RSS_TYPE_SET(rss_type.ipv6, IPV6) |
HINIC_RSS_TYPE_SET(rss_type.ipv6_ext, IPV6_EXT) |
HINIC_RSS_TYPE_SET(rss_type.tcp_ipv4, TCP_IPV4) |
HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6, TCP_IPV6) |
HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6_ext, TCP_IPV6_EXT) |
HINIC_RSS_TYPE_SET(rss_type.udp_ipv4, UDP_IPV4) |
HINIC_RSS_TYPE_SET(rss_type.udp_ipv6, UDP_IPV6);
cmd_buf.size = sizeof(struct hinic_rss_context_tbl);
ctx_tbl = (struct hinic_rss_context_tbl *)cmd_buf.buf;
ctx_tbl->group_index = cpu_to_be32(tmpl_idx);
ctx_tbl->offset = 0;
ctx_tbl->size = sizeof(u32);
ctx_tbl->size = cpu_to_be32(ctx_tbl->size);
ctx_tbl->rsvd = 0;
ctx_tbl->ctx = cpu_to_be32(ctx);
/* cfg the rss context table by command queue */
err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC,
HINIC_UCODE_CMD_SET_RSS_CONTEXT_TABLE,
&cmd_buf, &out_param);
hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmd_buf);
if (err || out_param != 0) {
dev_err(&pdev->dev, "Failed to set rss context table, err: %d\n",
err);
return -EFAULT;
}
return 0;
}
int hinic_get_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx,
struct hinic_rss_type *rss_type)
{
struct hinic_rss_context_table ctx_tbl = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u16 out_size = sizeof(ctx_tbl);
int err;
if (!hwdev || !rss_type)
return -EINVAL;
hwif = hwdev->hwif;
pdev = hwif->pdev;
ctx_tbl.func_id = HINIC_HWIF_FUNC_IDX(hwif);
ctx_tbl.template_id = tmpl_idx;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_CTX_TBL,
&ctx_tbl, sizeof(ctx_tbl),
&ctx_tbl, &out_size);
if (err || !out_size || ctx_tbl.status) {
dev_err(&pdev->dev, "Failed to get hash type, err: %d, status: 0x%x, out size: 0x%x\n",
err, ctx_tbl.status, out_size);
return -EINVAL;
}
rss_type->ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV4);
rss_type->ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6);
rss_type->ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6_EXT);
rss_type->tcp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV4);
rss_type->tcp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6);
rss_type->tcp_ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context,
TCP_IPV6_EXT);
rss_type->udp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV4);
rss_type->udp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV6);
return 0;
}
int hinic_rss_set_template_tbl(struct hinic_dev *nic_dev, u32 template_id,
const u8 *temp)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct hinic_rss_key rss_key = { 0 };
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
rss_key.func_id = HINIC_HWIF_FUNC_IDX(hwif);
rss_key.template_id = template_id;
memcpy(rss_key.key, temp, HINIC_RSS_KEY_SIZE);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL,
&rss_key, sizeof(rss_key),
&rss_key, &out_size);
if (err || !out_size || rss_key.status) {
dev_err(&pdev->dev,
"Failed to set rss hash key, err: %d, status: 0x%x, out size: 0x%x\n",
err, rss_key.status, out_size);
return -EINVAL;
}
return 0;
}
int hinic_rss_get_template_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx,
u8 *temp)
{
struct hinic_rss_template_key temp_key = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u16 out_size = sizeof(temp_key);
int err;
if (!hwdev || !temp)
return -EINVAL;
hwif = hwdev->hwif;
pdev = hwif->pdev;
temp_key.func_id = HINIC_HWIF_FUNC_IDX(hwif);
temp_key.template_id = tmpl_idx;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL,
&temp_key, sizeof(temp_key),
&temp_key, &out_size);
if (err || !out_size || temp_key.status) {
dev_err(&pdev->dev, "Failed to set hash key, err: %d, status: 0x%x, out size: 0x%x\n",
err, temp_key.status, out_size);
return -EINVAL;
}
memcpy(temp, temp_key.key, HINIC_RSS_KEY_SIZE);
return 0;
}
int hinic_rss_set_hash_engine(struct hinic_dev *nic_dev, u8 template_id,
u8 type)
{
struct hinic_rss_engine_type rss_engine = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
rss_engine.func_id = HINIC_HWIF_FUNC_IDX(hwif);
rss_engine.hash_engine = type;
rss_engine.template_id = template_id;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RSS_HASH_ENGINE,
&rss_engine, sizeof(rss_engine),
&rss_engine, &out_size);
if (err || !out_size || rss_engine.status) {
dev_err(&pdev->dev,
"Failed to set hash engine, err: %d, status: 0x%x, out size: 0x%x\n",
err, rss_engine.status, out_size);
return -EINVAL;
}
return 0;
}
int hinic_rss_get_hash_engine(struct hinic_dev *nic_dev, u8 tmpl_idx, u8 *type)
{
struct hinic_rss_engine_type hash_type = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u16 out_size = sizeof(hash_type);
int err;
if (!hwdev || !type)
return -EINVAL;
hwif = hwdev->hwif;
pdev = hwif->pdev;
hash_type.func_id = HINIC_HWIF_FUNC_IDX(hwif);
hash_type.template_id = tmpl_idx;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_HASH_ENGINE,
&hash_type, sizeof(hash_type),
&hash_type, &out_size);
if (err || !out_size || hash_type.status) {
dev_err(&pdev->dev, "Failed to get hash engine, err: %d, status: 0x%x, out size: 0x%x\n",
err, hash_type.status, out_size);
return -EINVAL;
}
*type = hash_type.hash_engine;
return 0;
}
int hinic_rss_cfg(struct hinic_dev *nic_dev, u8 rss_en, u8 template_id)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_rss_config rss_cfg = { 0 };
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
rss_cfg.rss_en = rss_en;
rss_cfg.template_id = template_id;
rss_cfg.rq_priority_number = 0;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_CFG,
&rss_cfg, sizeof(rss_cfg),
&rss_cfg, &out_size);
if (err || !out_size || rss_cfg.status) {
dev_err(&pdev->dev,
"Failed to set rss cfg, err: %d, status: 0x%x, out size: 0x%x\n",
err, rss_cfg.status, out_size);
return -EINVAL;
}
return 0;
}
int hinic_rss_template_alloc(struct hinic_dev *nic_dev, u8 *tmpl_idx)
{
struct hinic_rss_template_mgmt template_mgmt = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif);
template_mgmt.cmd = NIC_RSS_CMD_TEMP_ALLOC;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR,
&template_mgmt, sizeof(template_mgmt),
&template_mgmt, &out_size);
if (err || !out_size || template_mgmt.status) {
dev_err(&pdev->dev, "Failed to alloc rss template, err: %d, status: 0x%x, out size: 0x%x\n",
err, template_mgmt.status, out_size);
return -EINVAL;
}
*tmpl_idx = template_mgmt.template_id;
return 0;
}
int hinic_rss_template_free(struct hinic_dev *nic_dev, u8 tmpl_idx)
{
struct hinic_rss_template_mgmt template_mgmt = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size;
int err;
template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif);
template_mgmt.template_id = tmpl_idx;
template_mgmt.cmd = NIC_RSS_CMD_TEMP_FREE;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR,
&template_mgmt, sizeof(template_mgmt),
&template_mgmt, &out_size);
if (err || !out_size || template_mgmt.status) {
dev_err(&pdev->dev, "Failed to free rss template, err: %d, status: 0x%x, out size: 0x%x\n",
err, template_mgmt.status, out_size);
return -EINVAL;
}
return 0;
}
int hinic_get_vport_stats(struct hinic_dev *nic_dev,
struct hinic_vport_stats *stats)
{
struct hinic_cmd_vport_stats vport_stats = { 0 };
struct hinic_port_stats_info stats_info = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
u16 out_size = sizeof(vport_stats);
struct pci_dev *pdev = hwif->pdev;
int err;
stats_info.stats_version = HINIC_PORT_STATS_VERSION;
stats_info.func_id = HINIC_HWIF_FUNC_IDX(hwif);
stats_info.stats_size = sizeof(vport_stats);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_VPORT_STAT,
&stats_info, sizeof(stats_info),
&vport_stats, &out_size);
if (err || !out_size || vport_stats.status) {
dev_err(&pdev->dev,
"Failed to get function statistics, err: %d, status: 0x%x, out size: 0x%x\n",
err, vport_stats.status, out_size);
return -EFAULT;
}
memcpy(stats, &vport_stats.stats, sizeof(*stats));
return 0;
}
int hinic_get_phy_port_stats(struct hinic_dev *nic_dev,
struct hinic_phy_port_stats *stats)
{
struct hinic_port_stats_info stats_info = { 0 };
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct hinic_port_stats *port_stats;
u16 out_size = sizeof(*port_stats);
struct pci_dev *pdev = hwif->pdev;
int err;
port_stats = kzalloc(sizeof(*port_stats), GFP_KERNEL);
if (!port_stats)
return -ENOMEM;
stats_info.stats_version = HINIC_PORT_STATS_VERSION;
stats_info.stats_size = sizeof(*port_stats);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PORT_STATISTICS,
&stats_info, sizeof(stats_info),
port_stats, &out_size);
if (err || !out_size || port_stats->status) {
dev_err(&pdev->dev,
"Failed to get port statistics, err: %d, status: 0x%x, out size: 0x%x\n",
err, port_stats->status, out_size);
err = -EINVAL;
goto out;
}
memcpy(stats, &port_stats->stats, sizeof(*stats));
out:
kfree(port_stats);
return err;
}
int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_version_info up_ver = {0};
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u16 out_size;
int err;
if (!hwdev)
return -EINVAL;
hwif = hwdev->hwif;
pdev = hwif->pdev;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_MGMT_VERSION,
&up_ver, sizeof(up_ver), &up_ver,
&out_size);
if (err || !out_size || up_ver.status) {
dev_err(&pdev->dev,
"Failed to get mgmt version, err: %d, status: 0x%x, out size: 0x%x\n",
err, up_ver.status, out_size);
return -EINVAL;
}
snprintf(mgmt_ver, HINIC_MGMT_VERSION_MAX_LEN, "%s", up_ver.ver);
return 0;
}