summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/huawei/hinic/hinic_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/huawei/hinic/hinic_main.c')
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_main.c205
1 files changed, 189 insertions, 16 deletions
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c
index e9e6f4c9309a..501056fd32ee 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_main.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c
@@ -18,6 +18,7 @@
#include <linux/semaphore.h>
#include <linux/workqueue.h>
#include <net/ip.h>
+#include <net/devlink.h>
#include <linux/bitops.h>
#include <linux/bitmap.h>
#include <linux/delay.h>
@@ -25,6 +26,7 @@
#include "hinic_hw_qp.h"
#include "hinic_hw_dev.h"
+#include "hinic_devlink.h"
#include "hinic_port.h"
#include "hinic_tx.h"
#include "hinic_rx.h"
@@ -69,6 +71,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,
@@ -438,8 +444,11 @@ int hinic_open(struct net_device *netdev)
if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
hinic_notify_all_vfs_link_changed(nic_dev->hwdev, link_state);
- if (link_state == HINIC_LINK_STATE_UP)
+ if (link_state == HINIC_LINK_STATE_UP) {
nic_dev->flags |= HINIC_LINK_UP;
+ nic_dev->cable_unplugged = false;
+ nic_dev->module_unrecognized = false;
+ }
nic_dev->flags |= HINIC_INTF_UP;
@@ -887,6 +896,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
@@ -909,6 +938,8 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size,
down(&nic_dev->mgmt_lock);
nic_dev->flags |= HINIC_LINK_UP;
+ nic_dev->cable_unplugged = false;
+ nic_dev->module_unrecognized = false;
if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) ==
(HINIC_LINK_UP | HINIC_INTF_UP)) {
@@ -918,6 +949,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);
@@ -942,34 +976,132 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size,
*out_size = sizeof(*ret_link_status);
}
+static void cable_plug_event(void *handle,
+ void *buf_in, u16 in_size,
+ void *buf_out, u16 *out_size)
+{
+ struct hinic_cable_plug_event *plug_event = buf_in;
+ struct hinic_dev *nic_dev = handle;
+
+ nic_dev->cable_unplugged = plug_event->plugged ? false : true;
+
+ *out_size = sizeof(*plug_event);
+ plug_event = buf_out;
+ plug_event->status = 0;
+}
+
+static void link_err_event(void *handle,
+ void *buf_in, u16 in_size,
+ void *buf_out, u16 *out_size)
+{
+ struct hinic_link_err_event *link_err = buf_in;
+ struct hinic_dev *nic_dev = handle;
+
+ if (link_err->err_type >= LINK_ERR_NUM)
+ netif_info(nic_dev, link, nic_dev->netdev,
+ "Link failed, Unknown error type: 0x%x\n",
+ link_err->err_type);
+ else
+ nic_dev->module_unrecognized = true;
+
+ *out_size = sizeof(*link_err);
+ link_err = buf_out;
+ link_err->status = 0;
+}
+
static int set_features(struct hinic_dev *nic_dev,
netdev_features_t pre_features,
netdev_features_t features, bool force_change)
{
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);
}
/**
@@ -986,11 +1118,19 @@ static int nic_dev_init(struct pci_dev *pdev)
struct hinic_dev *nic_dev;
struct net_device *netdev;
struct hinic_hwdev *hwdev;
+ struct devlink *devlink;
int err, num_qps;
- hwdev = hinic_init_hwdev(pdev);
+ devlink = hinic_devlink_alloc();
+ if (!devlink) {
+ dev_err(&pdev->dev, "Hinic devlink alloc failed\n");
+ return -ENOMEM;
+ }
+
+ hwdev = hinic_init_hwdev(pdev, devlink);
if (IS_ERR(hwdev)) {
dev_err(&pdev->dev, "Failed to initialize HW device\n");
+ hinic_devlink_free(devlink);
return PTR_ERR(hwdev);
}
@@ -1008,8 +1148,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
@@ -1031,6 +1169,9 @@ static int nic_dev_init(struct pci_dev *pdev)
nic_dev->sriov_info.hwdev = hwdev;
nic_dev->sriov_info.pdev = pdev;
nic_dev->max_qps = num_qps;
+ nic_dev->devlink = devlink;
+
+ hinic_set_ethtool_ops(netdev);
sema_init(&nic_dev->mgmt_lock, 1);
@@ -1095,13 +1236,30 @@ static int nic_dev_init(struct pci_dev *pdev)
hinic_hwdev_cb_register(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS,
nic_dev, link_status_event_handler);
+ hinic_hwdev_cb_register(nic_dev->hwdev,
+ HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT,
+ nic_dev, cable_plug_event);
+ hinic_hwdev_cb_register(nic_dev->hwdev,
+ HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT,
+ nic_dev, link_err_event);
err = set_features(nic_dev, 0, nic_dev->netdev->features, true);
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,17 +1269,24 @@ 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_ERR_EVENT);
+ hinic_hwdev_cb_unregister(nic_dev->hwdev,
+ HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT);
+ hinic_hwdev_cb_unregister(nic_dev->hwdev,
HINIC_MGMT_MSG_CMD_LINK_STATUS);
cancel_work_sync(&rx_mode_work->work);
err_set_mtu:
-err_get_mac:
+ hinic_port_del_mac(nic_dev, netdev->dev_addr, 0);
err_add_mac:
+err_get_mac:
pci_set_drvdata(pdev, NULL);
destroy_workqueue(nic_dev->workq);
-
err_workq:
err_vlan_bitmap:
free_netdev(netdev);
@@ -1129,6 +1294,7 @@ err_vlan_bitmap:
err_alloc_etherdev:
err_num_qps:
hinic_free_hwdev(hwdev);
+ hinic_devlink_free(devlink);
return err;
}
@@ -1215,6 +1381,7 @@ static void hinic_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct hinic_dev *nic_dev = netdev_priv(netdev);
+ struct devlink *devlink = nic_dev->devlink;
struct hinic_rx_mode_work *rx_mode_work;
if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) {
@@ -1224,9 +1391,15 @@ 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,
+ HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT);
+ hinic_hwdev_cb_unregister(nic_dev->hwdev,
+ HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT);
+ hinic_hwdev_cb_unregister(nic_dev->hwdev,
HINIC_MGMT_MSG_CMD_LINK_STATUS);
rx_mode_work = &nic_dev->rx_mode_work;
@@ -1236,12 +1409,12 @@ static void hinic_remove(struct pci_dev *pdev)
destroy_workqueue(nic_dev->workq);
- hinic_vf_func_free(nic_dev->hwdev);
-
hinic_free_hwdev(nic_dev->hwdev);
free_netdev(netdev);
+ hinic_devlink_free(devlink);
+
pci_release_regions(pdev);
pci_disable_device(pdev);