Commit 89304496 authored by David S. Miller's avatar David S. Miller

Merge branch 'hinic-add-some-ethtool-ops-support'

Luo bin says:

====================
hinic: add some ethtool ops support

patch #1: support to set and get pause params with
          "ethtool -A/a" cmd
patch #2: support to set and get irq coalesce params with
          "ethtool -C/c" cmd
patch #3: support to do self test with "ethtool -t" cmd
patch #4: support to identify physical device with "ethtool -p" cmd
patch #5: support to get eeprom information with "ethtool -m" cmd
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 491f14db 2ac84cd1
......@@ -20,11 +20,14 @@
#define HINIC_DRV_NAME "hinic"
#define LP_PKT_CNT 64
enum hinic_flags {
HINIC_LINK_UP = BIT(0),
HINIC_INTF_UP = BIT(1),
HINIC_RSS_ENABLE = BIT(2),
HINIC_LINK_DOWN = BIT(3),
HINIC_LP_TEST = BIT(4),
};
struct hinic_rx_mode_work {
......@@ -49,6 +52,12 @@ enum hinic_rss_hash_type {
HINIC_RSS_HASH_ENGINE_TYPE_MAX,
};
struct hinic_intr_coal_info {
u8 pending_limt;
u8 coalesce_timer_cfg;
u8 resend_timer_cfg;
};
struct hinic_dev {
struct net_device *netdev;
struct hinic_hwdev *hwdev;
......@@ -82,7 +91,12 @@ struct hinic_dev {
struct hinic_rss_type rss_type;
u8 *rss_hkey_user;
s32 *rss_indir_user;
struct hinic_intr_coal_info *rx_intr_coalesce;
struct hinic_intr_coal_info *tx_intr_coalesce;
struct hinic_sriov_info sriov_info;
int lb_test_rx_idx;
int lb_pkt_len;
u8 *lb_test_rx_buf;
};
#endif
......@@ -83,6 +83,8 @@ static int parse_capability(struct hinic_hwdev *hwdev,
nic_cap->max_vf_qps = dev_cap->max_vf_sqs + 1;
}
hwdev->port_id = dev_cap->port_id;
return 0;
}
......@@ -705,6 +707,68 @@ static int hinic_l2nic_reset(struct hinic_hwdev *hwdev)
return 0;
}
int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
struct hinic_msix_config *interrupt_info)
{
u16 out_size = sizeof(*interrupt_info);
struct hinic_pfhwdev *pfhwdev;
int err;
if (!hwdev || !interrupt_info)
return -EINVAL;
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,
interrupt_info, sizeof(*interrupt_info),
interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
if (err || !out_size || interrupt_info->status) {
dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
err, interrupt_info->status, out_size);
return -EIO;
}
return 0;
}
int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
struct hinic_msix_config *interrupt_info)
{
u16 out_size = sizeof(*interrupt_info);
struct hinic_msix_config temp_info;
struct hinic_pfhwdev *pfhwdev;
int err;
if (!hwdev)
return -EINVAL;
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
err = hinic_get_interrupt_cfg(hwdev, &temp_info);
if (err)
return -EINVAL;
interrupt_info->lli_credit_cnt = temp_info.lli_timer_cnt;
interrupt_info->lli_timer_cnt = temp_info.lli_timer_cnt;
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
interrupt_info, sizeof(*interrupt_info),
interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
if (err || !out_size || interrupt_info->status) {
dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
err, interrupt_info->status, out_size);
return -EIO;
}
return 0;
}
/**
* hinic_init_hwdev - Initialize the NIC HW
* @pdev: the NIC pci device
......@@ -777,6 +841,8 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev)
goto err_dev_cap;
}
mutex_init(&hwdev->func_to_io.nic_cfg.cfg_mutex);
err = hinic_vf_func_init(hwdev);
if (err) {
dev_err(&pdev->dev, "Failed to init nic mbox\n");
......
......@@ -48,6 +48,8 @@ enum hinic_port_cmd {
HINIC_PORT_CMD_ADD_VLAN = 3,
HINIC_PORT_CMD_DEL_VLAN = 4,
HINIC_PORT_CMD_SET_PFC = 5,
HINIC_PORT_CMD_SET_MAC = 9,
HINIC_PORT_CMD_GET_MAC = 10,
HINIC_PORT_CMD_DEL_MAC = 11,
......@@ -95,6 +97,9 @@ enum hinic_port_cmd {
HINIC_PORT_CMD_FWCTXT_INIT = 69,
HINIC_PORT_CMD_GET_LOOPBACK_MODE = 72,
HINIC_PORT_CMD_SET_LOOPBACK_MODE,
HINIC_PORT_CMD_ENABLE_SPOOFCHK = 78,
HINIC_PORT_CMD_GET_MGMT_VERSION = 88,
......@@ -125,9 +130,13 @@ enum hinic_port_cmd {
HINIC_PORT_CMD_SET_AUTONEG = 219,
HINIC_PORT_CMD_GET_STD_SFP_INFO = 240,
HINIC_PORT_CMD_SET_LRO_TIMER = 244,
HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 249,
HINIC_PORT_CMD_GET_SFP_ABS = 251,
};
/* cmd of mgmt CPU message for HILINK module */
......@@ -283,6 +292,21 @@ struct hinic_cmd_l2nic_reset {
u16 reset_flag;
};
struct hinic_msix_config {
u8 status;
u8 version;
u8 rsvd0[6];
u16 func_id;
u16 msix_index;
u8 pending_cnt;
u8 coalesce_timer_cnt;
u8 lli_timer_cnt;
u8 lli_credit_cnt;
u8 resend_timer_cnt;
u8 rsvd1[3];
};
struct hinic_hwdev {
struct hinic_hwif *hwif;
struct msix_entry *msix_entries;
......@@ -292,6 +316,7 @@ struct hinic_hwdev {
struct hinic_mbox_func_to_func *func_to_func;
struct hinic_cap nic_cap;
u8 port_id;
};
struct hinic_nic_cb {
......@@ -376,4 +401,10 @@ int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq,
void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index,
enum hinic_msix_state flag);
int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
struct hinic_msix_config *interrupt_info);
int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
struct hinic_msix_config *interrupt_info);
#endif
......@@ -47,6 +47,15 @@ struct hinic_free_db_area {
struct semaphore idx_lock;
};
struct hinic_nic_cfg {
/* lock for getting nic cfg */
struct mutex cfg_mutex;
bool pause_set;
u32 auto_neg;
u32 rx_pause;
u32 tx_pause;
};
struct hinic_func_to_io {
struct hinic_hwif *hwif;
struct hinic_hwdev *hwdev;
......@@ -78,6 +87,7 @@ struct hinic_func_to_io {
u16 max_vfs;
struct vf_data_storage *vf_infos;
u8 link_status;
struct hinic_nic_cfg nic_cfg;
};
struct hinic_wq_page_size {
......
......@@ -78,6 +78,11 @@ enum hinic_comm_cmd {
HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP = 0x33,
HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,
HINIC_COMM_CMD_SET_LED_STATUS = 0x4a,
HINIC_COMM_CMD_L2NIC_RESET = 0x4b,
HINIC_COMM_CMD_PAGESIZE_SET = 0x50,
......
......@@ -69,6 +69,10 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)");
#define HINIC_WAIT_SRIOV_CFG_TIMEOUT 15000
#define HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT 2
#define HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG 32
#define HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG 7
static int change_mac_addr(struct net_device *netdev, const u8 *addr);
static int set_features(struct hinic_dev *nic_dev,
......@@ -887,6 +891,26 @@ static void netdev_features_init(struct net_device *netdev)
netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
}
static void hinic_refresh_nic_cfg(struct hinic_dev *nic_dev)
{
struct hinic_nic_cfg *nic_cfg = &nic_dev->hwdev->func_to_io.nic_cfg;
struct hinic_pause_config pause_info = {0};
struct hinic_port_cap port_cap = {0};
if (hinic_port_get_cap(nic_dev, &port_cap))
return;
mutex_lock(&nic_cfg->cfg_mutex);
if (nic_cfg->pause_set || !port_cap.autoneg_state) {
nic_cfg->auto_neg = port_cap.autoneg_state;
pause_info.auto_neg = nic_cfg->auto_neg;
pause_info.rx_pause = nic_cfg->rx_pause;
pause_info.tx_pause = nic_cfg->tx_pause;
hinic_set_hw_pause_info(nic_dev->hwdev, &pause_info);
}
mutex_unlock(&nic_cfg->cfg_mutex);
}
/**
* link_status_event_handler - link event handler
* @handle: nic device for the handler
......@@ -918,6 +942,9 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size,
up(&nic_dev->mgmt_lock);
if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
hinic_refresh_nic_cfg(nic_dev);
netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is UP\n");
} else {
down(&nic_dev->mgmt_lock);
......@@ -948,28 +975,93 @@ static int set_features(struct hinic_dev *nic_dev,
{
netdev_features_t changed = force_change ? ~0 : pre_features ^ features;
u32 csum_en = HINIC_RX_CSUM_OFFLOAD_EN;
netdev_features_t failed_features = 0;
int ret = 0;
int err = 0;
if (changed & NETIF_F_TSO)
err = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
if (changed & NETIF_F_TSO) {
ret = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
HINIC_TSO_ENABLE : HINIC_TSO_DISABLE);
if (ret) {
err = ret;
failed_features |= NETIF_F_TSO;
}
}
if (changed & NETIF_F_RXCSUM)
err = hinic_set_rx_csum_offload(nic_dev, csum_en);
if (changed & NETIF_F_RXCSUM) {
ret = hinic_set_rx_csum_offload(nic_dev, csum_en);
if (ret) {
err = ret;
failed_features |= NETIF_F_RXCSUM;
}
}
if (changed & NETIF_F_LRO) {
err = hinic_set_rx_lro_state(nic_dev,
ret = hinic_set_rx_lro_state(nic_dev,
!!(features & NETIF_F_LRO),
HINIC_LRO_RX_TIMER_DEFAULT,
HINIC_LRO_MAX_WQE_NUM_DEFAULT);
if (ret) {
err = ret;
failed_features |= NETIF_F_LRO;
}
}
if (changed & NETIF_F_HW_VLAN_CTAG_RX)
err = hinic_set_rx_vlan_offload(nic_dev,
if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
ret = hinic_set_rx_vlan_offload(nic_dev,
!!(features &
NETIF_F_HW_VLAN_CTAG_RX));
if (ret) {
err = ret;
failed_features |= NETIF_F_HW_VLAN_CTAG_RX;
}
}
return err;
if (err) {
nic_dev->netdev->features = features ^ failed_features;
return -EIO;
}
return 0;
}
static int hinic_init_intr_coalesce(struct hinic_dev *nic_dev)
{
u64 size;
u16 i;
size = sizeof(struct hinic_intr_coal_info) * nic_dev->max_qps;
nic_dev->rx_intr_coalesce = kzalloc(size, GFP_KERNEL);
if (!nic_dev->rx_intr_coalesce)
return -ENOMEM;
nic_dev->tx_intr_coalesce = kzalloc(size, GFP_KERNEL);
if (!nic_dev->tx_intr_coalesce) {
kfree(nic_dev->rx_intr_coalesce);
return -ENOMEM;
}
for (i = 0; i < nic_dev->max_qps; i++) {
nic_dev->rx_intr_coalesce[i].pending_limt =
HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT;
nic_dev->rx_intr_coalesce[i].coalesce_timer_cfg =
HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG;
nic_dev->rx_intr_coalesce[i].resend_timer_cfg =
HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG;
nic_dev->tx_intr_coalesce[i].pending_limt =
HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT;
nic_dev->tx_intr_coalesce[i].coalesce_timer_cfg =
HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG;
nic_dev->tx_intr_coalesce[i].resend_timer_cfg =
HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG;
}
return 0;
}
static void hinic_free_intr_coalesce(struct hinic_dev *nic_dev)
{
kfree(nic_dev->tx_intr_coalesce);
kfree(nic_dev->rx_intr_coalesce);
}
/**
......@@ -1008,8 +1100,6 @@ static int nic_dev_init(struct pci_dev *pdev)
goto err_alloc_etherdev;
}
hinic_set_ethtool_ops(netdev);
if (!HINIC_IS_VF(hwdev->hwif))
netdev->netdev_ops = &hinic_netdev_ops;
else
......@@ -1032,6 +1122,8 @@ static int nic_dev_init(struct pci_dev *pdev)
nic_dev->sriov_info.pdev = pdev;
nic_dev->max_qps = num_qps;
hinic_set_ethtool_ops(netdev);
sema_init(&nic_dev->mgmt_lock, 1);
tx_stats = &nic_dev->tx_stats;
......@@ -1100,8 +1192,19 @@ static int nic_dev_init(struct pci_dev *pdev)
if (err)
goto err_set_features;
/* enable pause and disable pfc by default */
err = hinic_dcb_set_pfc(nic_dev->hwdev, 0, 0);
if (err)
goto err_set_pfc;
SET_NETDEV_DEV(netdev, &pdev->dev);
err = hinic_init_intr_coalesce(nic_dev);
if (err) {
dev_err(&pdev->dev, "Failed to init_intr_coalesce\n");
goto err_init_intr;
}
err = register_netdev(netdev);
if (err) {
dev_err(&pdev->dev, "Failed to register netdev\n");
......@@ -1111,6 +1214,9 @@ static int nic_dev_init(struct pci_dev *pdev)
return 0;
err_reg_netdev:
hinic_free_intr_coalesce(nic_dev);
err_init_intr:
err_set_pfc:
err_set_features:
hinic_hwdev_cb_unregister(nic_dev->hwdev,
HINIC_MGMT_MSG_CMD_LINK_STATUS);
......@@ -1224,6 +1330,8 @@ static void hinic_remove(struct pci_dev *pdev)
unregister_netdev(netdev);
hinic_free_intr_coalesce(nic_dev);
hinic_port_del_mac(nic_dev, netdev->dev_addr, 0);
hinic_hwdev_cb_unregister(nic_dev->hwdev,
......
......@@ -1082,6 +1082,7 @@ int hinic_get_link_mode(struct hinic_hwdev *hwdev,
if (!hwdev || !link_mode)
return -EINVAL;
link_mode->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
out_size = sizeof(*link_mode);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_MODE,
......@@ -1172,6 +1173,8 @@ int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev,
u16 out_size = sizeof(*pause_info);
int err;
pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PAUSE_INFO,
pause_info, sizeof(*pause_info),
pause_info, &out_size);
......@@ -1190,6 +1193,8 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
u16 out_size = sizeof(*pause_info);
int err;
pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PAUSE_INFO,
pause_info, sizeof(*pause_info),
pause_info, &out_size);
......@@ -1201,3 +1206,192 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
return 0;
}
int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap)
{
struct hinic_nic_cfg *nic_cfg = &hwdev->func_to_io.nic_cfg;
struct hinic_set_pfc pfc = {0};
u16 out_size = sizeof(pfc);
int err;
if (HINIC_IS_VF(hwdev->hwif))
return 0;
mutex_lock(&nic_cfg->cfg_mutex);
pfc.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
pfc.pfc_bitmap = pfc_bitmap;
pfc.pfc_en = pfc_en;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PFC,
&pfc, sizeof(pfc), &pfc, &out_size);
if (err || pfc.status || !out_size) {
dev_err(&hwdev->hwif->pdev->dev, "Failed to %s pfc, err: %d, status: 0x%x, out size: 0x%x\n",
pfc_en ? "enable" : "disable", err, pfc.status,
out_size);
mutex_unlock(&nic_cfg->cfg_mutex);
return -EIO;
}
/* pause settings is opposite from pfc */
nic_cfg->rx_pause = pfc_en ? 0 : 1;
nic_cfg->tx_pause = pfc_en ? 0 : 1;
mutex_unlock(&nic_cfg->cfg_mutex);
return 0;
}
int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable)
{
struct hinic_port_loopback lb = {0};
u16 out_size = sizeof(lb);
int err;
lb.mode = mode;
lb.en = enable;
if (mode < LOOP_MODE_MIN || mode > LOOP_MODE_MAX) {
dev_err(&hwdev->hwif->pdev->dev,
"Invalid loopback mode %d to set\n", mode);
return -EINVAL;
}
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LOOPBACK_MODE,
&lb, sizeof(lb), &lb, &out_size);
if (err || !out_size || lb.status) {
dev_err(&hwdev->hwif->pdev->dev,
"Failed to set loopback mode %d en %d, err: %d, status: 0x%x, out size: 0x%x\n",
mode, enable, err, lb.status, out_size);
return -EIO;
}
return 0;
}
static int _set_led_status(struct hinic_hwdev *hwdev, u8 port,
enum hinic_led_type type,
enum hinic_led_mode mode, u8 reset)
{
struct hinic_led_info led_info = {0};
u16 out_size = sizeof(led_info);
struct hinic_pfhwdev *pfhwdev;
int err;
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
led_info.port = port;
led_info.reset = reset;
led_info.type = type;
led_info.mode = mode;
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
HINIC_COMM_CMD_SET_LED_STATUS,
&led_info, sizeof(led_info),
&led_info, &out_size, HINIC_MGMT_MSG_SYNC);
if (err || led_info.status || !out_size) {
dev_err(&hwdev->hwif->pdev->dev, "Failed to set led status, err: %d, status: 0x%x, out size: 0x%x\n",
err, led_info.status, out_size);
return -EIO;
}
return 0;
}
int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port,
enum hinic_led_type type, enum hinic_led_mode mode)
{
if (!hwdev)
return -EINVAL;
return _set_led_status(hwdev, port, type, mode, 0);
}
int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port)
{
int err;
if (!hwdev)
return -EINVAL;
err = _set_led_status(hwdev, port, HINIC_LED_TYPE_INVALID,
HINIC_LED_MODE_INVALID, 1);
if (err)
dev_err(&hwdev->hwif->pdev->dev,
"Failed to reset led status\n");
return err;
}
static bool hinic_if_sfp_absent(struct hinic_hwdev *hwdev)
{
struct hinic_cmd_get_light_module_abs sfp_abs = {0};
u16 out_size = sizeof(sfp_abs);
u8 port_id = hwdev->port_id;
int err;
sfp_abs.port_id = port_id;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_SFP_ABS,
&sfp_abs, sizeof(sfp_abs), &sfp_abs,
&out_size);
if (sfp_abs.status || err || !out_size) {
dev_err(&hwdev->hwif->pdev->dev,
"Failed to get port%d sfp absent status, err: %d, status: 0x%x, out size: 0x%x\n",
port_id, err, sfp_abs.status, out_size);
return true;
}
return ((sfp_abs.abs_status == 0) ? false : true);
}
int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len)
{
struct hinic_cmd_get_std_sfp_info sfp_info = {0};
u16 out_size = sizeof(sfp_info);
u8 port_id;
int err;
if (!hwdev || !data || !len)
return -EINVAL;
port_id = hwdev->port_id;
if (hinic_if_sfp_absent(hwdev))
return -ENXIO;
sfp_info.port_id = port_id;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_STD_SFP_INFO,
&sfp_info, sizeof(sfp_info), &sfp_info,
&out_size);
if (sfp_info.status || err || !out_size) {
dev_err(&hwdev->hwif->pdev->dev,
"Failed to get port%d sfp eeprom information, err: %d, status: 0x%x, out size: 0x%x\n",
port_id, err, sfp_info.status, out_size);
return -EIO;
}
*len = min_t(u16, sfp_info.eeprom_len, STD_SFP_INFO_MAX_SIZE);
memcpy(data, sfp_info.sfp_info, STD_SFP_INFO_MAX_SIZE);
return 0;
}
int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1)
{
u8 sfp_data[STD_SFP_INFO_MAX_SIZE];
u16 len;
int err;
if (hinic_if_sfp_absent(hwdev))
return -ENXIO;
err = hinic_get_sfp_eeprom(hwdev, sfp_data, &len);
if (err)
return err;
*data0 = sfp_data[0];
*data1 = sfp_data[1];
return 0;
}
......@@ -641,6 +641,68 @@ struct hinic_pause_config {
u32 tx_pause;
};
struct hinic_set_pfc {
u8 status;
u8 version;
u8 rsvd0[6];
u16 func_id;
u8 pfc_en;
u8 pfc_bitmap;
u8 rsvd1[4];
};
/* get or set loopback mode, need to modify by base API */
#define HINIC_INTERNAL_LP_MODE 5
#define LOOP_MODE_MIN 1
#define LOOP_MODE_MAX 6
struct hinic_port_loopback {
u8 status;
u8 version;
u8 rsvd[6];
u32 mode;
u32 en;
};
struct hinic_led_info {
u8 status;
u8 version;
u8 rsvd0[6];
u8 port;
u8 type;
u8 mode;
u8 reset;
};
#define STD_SFP_INFO_MAX_SIZE 640
struct hinic_cmd_get_light_module_abs {
u8 status;
u8 version;
u8 rsvd0[6];
u8 port_id;
u8 abs_status; /* 0:present, 1:absent */
u8 rsv[2];
};
#define STD_SFP_INFO_MAX_SIZE 640
struct hinic_cmd_get_std_sfp_info {
u8 status;
u8 version;
u8 rsvd0[6];
u8 port_id;
u8 wire_type;
u16 eeprom_len;
u32 rsvd;
u8 sfp_info[STD_SFP_INFO_MAX_SIZE];
};
int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
u16 vlan_id);
......@@ -736,6 +798,38 @@ int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev,
int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
struct hinic_pause_config *pause_info);
int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap);
int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable);
enum hinic_led_mode {
HINIC_LED_MODE_ON,
HINIC_LED_MODE_OFF,
HINIC_LED_MODE_FORCE_1HZ,
HINIC_LED_MODE_FORCE_2HZ,
HINIC_LED_MODE_FORCE_4HZ,
HINIC_LED_MODE_1HZ,
HINIC_LED_MODE_2HZ,
HINIC_LED_MODE_4HZ,
HINIC_LED_MODE_INVALID,
};
enum hinic_led_type {
HINIC_LED_TYPE_LINK,
HINIC_LED_TYPE_LOW_SPEED,
HINIC_LED_TYPE_HIGH_SPEED,
HINIC_LED_TYPE_INVALID,
};
int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port);
int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port,
enum hinic_led_type type, enum hinic_led_mode mode);
int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1);
int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len);
int hinic_open(struct net_device *netdev);
int hinic_close(struct net_device *netdev);
......
......@@ -316,6 +316,39 @@ static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb,
return num_wqes;
}
static void hinic_copy_lp_data(struct hinic_dev *nic_dev,
struct sk_buff *skb)
{
struct net_device *netdev = nic_dev->netdev;
u8 *lb_buf = nic_dev->lb_test_rx_buf;
int lb_len = nic_dev->lb_pkt_len;
int pkt_offset, frag_len, i;
void *frag_data = NULL;
if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) {
nic_dev->lb_test_rx_idx = 0;
netif_warn(nic_dev, drv, netdev, "Loopback test warning, receive too more test pkts\n");
}
if (skb->len != nic_dev->lb_pkt_len) {
netif_warn(nic_dev, drv, netdev, "Wrong packet length\n");
nic_dev->lb_test_rx_idx++;
return;
}
pkt_offset = nic_dev->lb_test_rx_idx * lb_len;
frag_len = (int)skb_headlen(skb);
memcpy(lb_buf + pkt_offset, skb->data, frag_len);
pkt_offset += frag_len;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]);
frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]);
memcpy((lb_buf + pkt_offset), frag_data, frag_len);
pkt_offset += frag_len;
}
nic_dev->lb_test_rx_idx++;
}
/**
* rxq_recv - Rx handler
* @rxq: rx queue
......@@ -330,6 +363,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
u64 pkt_len = 0, rx_bytes = 0;
struct hinic_rq *rq = rxq->rq;
struct hinic_rq_wqe *rq_wqe;
struct hinic_dev *nic_dev;
unsigned int free_wqebbs;
struct hinic_rq_cqe *cqe;
int num_wqes, pkts = 0;
......@@ -342,6 +376,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
u32 vlan_len;
u16 vid;
nic_dev = netdev_priv(netdev);
while (pkts < budget) {
num_wqes = 0;
......@@ -384,6 +420,9 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
}
if (unlikely(nic_dev->flags & HINIC_LP_TEST))
hinic_copy_lp_data(nic_dev, skb);
skb_record_rx_queue(skb, qp->q_id);
skb->protocol = eth_type_trans(skb, rxq->netdev);
......@@ -478,11 +517,15 @@ static irqreturn_t rx_irq(int irq, void *data)
static int rx_request_irq(struct hinic_rxq *rxq)
{
struct hinic_dev *nic_dev = netdev_priv(rxq->netdev);
struct hinic_msix_config interrupt_info = {0};
struct hinic_intr_coal_info *intr_coal = NULL;
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_rq *rq = rxq->rq;
struct hinic_qp *qp;
int err;
qp = container_of(rq, struct hinic_qp, rq);
rx_add_napi(rxq);
hinic_hwdev_msix_set(hwdev, rq->msix_entry,
......@@ -490,13 +533,26 @@ static int rx_request_irq(struct hinic_rxq *rxq)
RX_IRQ_NO_LLI_TIMER, RX_IRQ_NO_CREDIT,
RX_IRQ_NO_RESEND_TIMER);
intr_coal = &nic_dev->rx_intr_coalesce[qp->q_id];
interrupt_info.msix_index = rq->msix_entry;
interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg;
interrupt_info.pending_cnt = intr_coal->pending_limt;
interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg;
err = hinic_set_interrupt_cfg(hwdev, &interrupt_info);
if (err) {
netif_err(nic_dev, drv, rxq->netdev,
"Failed to set RX interrupt coalescing attribute\n");
rx_del_napi(rxq);
return err;
}
err = request_irq(rq->irq, rx_irq, 0, rxq->irq_name, rxq);
if (err) {
rx_del_napi(rxq);
return err;
}
qp = container_of(rq, struct hinic_qp, rq);
cpumask_set_cpu(qp->q_id % num_online_cpus(), &rq->affinity_mask);
return irq_set_affinity_hint(rq->irq, &rq->affinity_mask);
}
......
......@@ -905,7 +905,6 @@ int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting)
err = hinic_set_vf_spoofchk(sriov_info->hwdev,
OS_VF_ID_TO_HW(vf), setting);
if (!err) {
netif_info(nic_dev, drv, netdev, "Set VF %d spoofchk %s successfully\n",
vf, setting ? "on" : "off");
......@@ -1020,6 +1019,7 @@ static int cfg_mbx_pf_proc_vf_msg(void *hwdev, u16 vf_id, u8 cmd, void *buf_in,
dev_cap->max_vf = cap->max_vf;
dev_cap->max_sqs = cap->max_vf_qps;
dev_cap->max_rqs = cap->max_vf_qps;
dev_cap->port_id = dev->port_id;
*out_size = sizeof(*dev_cap);
......
......@@ -459,6 +459,67 @@ static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task,
return 0;
}
netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
u16 prod_idx, q_id = skb->queue_mapping;
struct netdev_queue *netdev_txq;
int nr_sges, err = NETDEV_TX_OK;
struct hinic_sq_wqe *sq_wqe;
unsigned int wqe_size;
struct hinic_txq *txq;
struct hinic_qp *qp;
txq = &nic_dev->txqs[q_id];
qp = container_of(txq->sq, struct hinic_qp, sq);
nr_sges = skb_shinfo(skb)->nr_frags + 1;
err = tx_map_skb(nic_dev, skb, txq->sges);
if (err)
goto skb_error;
wqe_size = HINIC_SQ_WQE_SIZE(nr_sges);
sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx);
if (!sq_wqe) {
netif_stop_subqueue(netdev, qp->q_id);
sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx);
if (sq_wqe) {
netif_wake_subqueue(nic_dev->netdev, qp->q_id);
goto process_sq_wqe;
}
tx_unmap_skb(nic_dev, skb, txq->sges);
u64_stats_update_begin(&txq->txq_stats.syncp);
txq->txq_stats.tx_busy++;
u64_stats_update_end(&txq->txq_stats.syncp);
err = NETDEV_TX_BUSY;
wqe_size = 0;
goto flush_skbs;
}
process_sq_wqe:
hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges);
hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size);
flush_skbs:
netdev_txq = netdev_get_tx_queue(netdev, q_id);
if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq)))
hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0);
return err;
skb_error:
dev_kfree_skb_any(skb);
u64_stats_update_begin(&txq->txq_stats.syncp);
txq->txq_stats.tx_dropped++;
u64_stats_update_end(&txq->txq_stats.syncp);
return NETDEV_TX_OK;
}
netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
......@@ -718,12 +779,17 @@ static irqreturn_t tx_irq(int irq, void *data)
static int tx_request_irq(struct hinic_txq *txq)
{
struct hinic_dev *nic_dev = netdev_priv(txq->netdev);
struct hinic_msix_config interrupt_info = {0};
struct hinic_intr_coal_info *intr_coal = NULL;
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_sq *sq = txq->sq;
struct hinic_qp *qp;
int err;
qp = container_of(sq, struct hinic_qp, sq);
tx_napi_add(txq, nic_dev->tx_weight);
hinic_hwdev_msix_set(nic_dev->hwdev, sq->msix_entry,
......@@ -731,6 +797,20 @@ static int tx_request_irq(struct hinic_txq *txq)
TX_IRQ_NO_LLI_TIMER, TX_IRQ_NO_CREDIT,
TX_IRQ_NO_RESEND_TIMER);
intr_coal = &nic_dev->tx_intr_coalesce[qp->q_id];
interrupt_info.msix_index = sq->msix_entry;
interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg;
interrupt_info.pending_cnt = intr_coal->pending_limt;
interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg;
err = hinic_set_interrupt_cfg(hwdev, &interrupt_info);
if (err) {
netif_err(nic_dev, drv, txq->netdev,
"Failed to set TX interrupt coalescing attribute\n");
tx_napi_del(txq);
return err;
}
err = request_irq(sq->irq, tx_irq, 0, txq->irq_name, txq);
if (err) {
dev_err(&pdev->dev, "Failed to request Tx irq\n");
......
......@@ -44,6 +44,8 @@ void hinic_txq_clean_stats(struct hinic_txq *txq);
void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats);
netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment