aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2022-08-30 11:29:29 +0200
committerPaolo Abeni <pabeni@redhat.com>2022-08-30 11:29:30 +0200
commitd287532edfc2b6cec45e5eb00332a4c6fddf07a3 (patch)
treea1356f1c309b4daf91514b53dbd99d7ac51823cf
parentMerge branch 'sparx5-mrouter' (diff)
parentnfp: add support for eeprom get and set command (diff)
downloadlinux-dev-d287532edfc2b6cec45e5eb00332a4c6fddf07a3.tar.xz
linux-dev-d287532edfc2b6cec45e5eb00332a4c6fddf07a3.zip
Merge branch 'nfp-port-speed-and-eeprom-get-set-updates'
Simon Horman says: ==================== nfp: port speed and eeprom get/set updates this short series is the initial updates for the NFP driver for the v6.1 Kernel. It covers two enhancements: 1. Patches 1/3 and 2/3: - Support cases where application firmware does not know port speeds a priori by relaying this information from the management firmware to the application firmware. This allows the existing mechanism, whereby the driver reports port speeds to user-space as provided by the application firmware, to work in this case. 2. Patch 2/3: - Add support for eeprom get and set command ==================== Link: https://lore.kernel.org/r/20220825141223.22346-1-simon.horman@corigine.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_main.h5
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_common.c9
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c8
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h11
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c187
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_main.c98
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_port.h2
7 files changed, 295 insertions, 25 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h
index f56ca11de134..6805af186f1b 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_main.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h
@@ -65,6 +65,7 @@ struct nfp_dumpspec {
* @num_vfs: Number of SR-IOV VFs enabled
* @fw_loaded: Is the firmware loaded?
* @unload_fw_on_remove:Do we need to unload firmware on driver removal?
+ * @sp_indiff: Is the firmware indifferent to physical port speed?
* @ctrl_vnic: Pointer to the control vNIC if available
* @mip: MIP handle
* @rtbl: RTsym table
@@ -114,6 +115,7 @@ struct nfp_pf {
bool fw_loaded;
bool unload_fw_on_remove;
+ bool sp_indiff;
struct nfp_net *ctrl_vnic;
@@ -190,4 +192,7 @@ int nfp_shared_buf_pool_set(struct nfp_pf *pf, unsigned int sb,
int nfp_devlink_params_register(struct nfp_pf *pf);
void nfp_devlink_params_unregister(struct nfp_pf *pf);
+
+unsigned int nfp_net_lr2speed(unsigned int linkrate);
+unsigned int nfp_net_speed2lr(unsigned int speed);
#endif /* NFP_MAIN_H */
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index cf4d6f1129fa..fda3e77f28d2 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -474,19 +474,22 @@ static void nfp_net_read_link_status(struct nfp_net *nn)
{
unsigned long flags;
bool link_up;
- u32 sts;
+ u16 sts;
spin_lock_irqsave(&nn->link_status_lock, flags);
- sts = nn_readl(nn, NFP_NET_CFG_STS);
+ sts = nn_readw(nn, NFP_NET_CFG_STS);
link_up = !!(sts & NFP_NET_CFG_STS_LINK);
if (nn->link_up == link_up)
goto out;
nn->link_up = link_up;
- if (nn->port)
+ if (nn->port) {
set_bit(NFP_PORT_CHANGED, &nn->port->flags);
+ if (nn->port->link_cb)
+ nn->port->link_cb(nn->port);
+ }
if (nn->link_up) {
netif_carrier_on(nn->dp.netdev);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c
index c3a763134e79..d81bd8697047 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c
@@ -148,6 +148,14 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
true))
return -EINVAL;
break;
+ case NFP_NET_CFG_TLV_TYPE_SP_INDIFF:
+ if (length) {
+ dev_err(dev, "Unexpected len of SP_INDIFF TLV:%u\n", length);
+ return -EINVAL;
+ }
+
+ caps->sp_indiff = true;
+ break;
default:
if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr))
break;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
index ac05ec34d69e..1d53f721a1c8 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
@@ -193,6 +193,10 @@
#define NFP_NET_CFG_STS_LINK_RATE_40G 5
#define NFP_NET_CFG_STS_LINK_RATE_50G 6
#define NFP_NET_CFG_STS_LINK_RATE_100G 7
+/* NSP Link rate is a 16-bit word. It's determined by NSP and
+ * written to CFG BAR by NFP driver.
+ */
+#define NFP_NET_CFG_STS_NSP_LINK_RATE 0x0036
#define NFP_NET_CFG_CAP 0x0038
#define NFP_NET_CFG_MAX_TXRINGS 0x003c
#define NFP_NET_CFG_MAX_RXRINGS 0x0040
@@ -488,6 +492,10 @@
* %NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN:
* Same as %NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS, but crypto TLS does stream scan
* RX sync, rather than kernel-assisted sync.
+ *
+ * %NFP_NET_CFG_TLV_TYPE_SP_INDIFF:
+ * Empty, indicate the firmware is indifferent to port speed. Then no need to
+ * reload driver and firmware when port speed is changed.
*/
#define NFP_NET_CFG_TLV_TYPE_UNKNOWN 0
#define NFP_NET_CFG_TLV_TYPE_RESERVED 1
@@ -501,6 +509,7 @@
#define NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS 11 /* see crypto/fw.h */
#define NFP_NET_CFG_TLV_TYPE_VNIC_STATS 12
#define NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN 13
+#define NFP_NET_CFG_TLV_TYPE_SP_INDIFF 14
struct device;
@@ -515,6 +524,7 @@ struct device;
* @vnic_stats_off: offset of vNIC stats area
* @vnic_stats_cnt: number of vNIC stats
* @tls_resync_ss: TLS resync will be performed via stream scan
+ * @sp_indiff: Firmware is indifferent to port speed
*/
struct nfp_net_tlv_caps {
u32 me_freq_mhz;
@@ -527,6 +537,7 @@ struct nfp_net_tlv_caps {
unsigned int vnic_stats_off;
unsigned int vnic_stats_cnt;
unsigned int tls_resync_ss:1;
+ unsigned int sp_indiff:1;
};
int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
index eeb1455a4e5d..ac31c79ccd3a 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
@@ -273,20 +273,11 @@ static int
nfp_net_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
- static const u32 ls_to_ethtool[] = {
- [NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0,
- [NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN,
- [NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000,
- [NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000,
- [NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000,
- [NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000,
- [NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000,
- [NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000,
- };
struct nfp_eth_table_port *eth_port;
struct nfp_port *port;
struct nfp_net *nn;
- u32 sts, ls;
+ unsigned int speed;
+ u16 sts;
/* Init to unknowns */
ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
@@ -319,18 +310,15 @@ nfp_net_get_link_ksettings(struct net_device *netdev,
return -EOPNOTSUPP;
nn = netdev_priv(netdev);
- sts = nn_readl(nn, NFP_NET_CFG_STS);
-
- ls = FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts);
- if (ls == NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED)
+ sts = nn_readw(nn, NFP_NET_CFG_STS);
+ speed = nfp_net_lr2speed(FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts));
+ if (!speed)
return -EOPNOTSUPP;
- if (ls == NFP_NET_CFG_STS_LINK_RATE_UNKNOWN ||
- ls >= ARRAY_SIZE(ls_to_ethtool))
- return 0;
-
- cmd->base.speed = ls_to_ethtool[ls];
- cmd->base.duplex = DUPLEX_FULL;
+ if (speed != SPEED_UNKNOWN) {
+ cmd->base.speed = speed;
+ cmd->base.duplex = DUPLEX_FULL;
+ }
return 0;
}
@@ -1676,6 +1664,160 @@ static int nfp_net_set_phys_id(struct net_device *netdev,
return err;
}
+#define NFP_EEPROM_LEN ETH_ALEN
+
+static int
+nfp_net_get_eeprom_len(struct net_device *netdev)
+{
+ struct nfp_eth_table_port *eth_port;
+ struct nfp_port *port;
+
+ port = nfp_port_from_netdev(netdev);
+ eth_port = __nfp_port_get_eth_port(port);
+ if (!eth_port)
+ return 0;
+
+ return NFP_EEPROM_LEN;
+}
+
+static int
+nfp_net_get_nsp_hwindex(struct net_device *netdev,
+ struct nfp_nsp **nspptr,
+ u32 *index)
+{
+ struct nfp_eth_table_port *eth_port;
+ struct nfp_port *port;
+ struct nfp_nsp *nsp;
+ int err;
+
+ port = nfp_port_from_netdev(netdev);
+ eth_port = __nfp_port_get_eth_port(port);
+ if (!eth_port)
+ return -EOPNOTSUPP;
+
+ nsp = nfp_nsp_open(port->app->cpp);
+ if (IS_ERR(nsp)) {
+ err = PTR_ERR(nsp);
+ netdev_err(netdev, "Failed to access the NSP: %d\n", err);
+ return err;
+ }
+
+ if (!nfp_nsp_has_hwinfo_lookup(nsp)) {
+ netdev_err(netdev, "NSP doesn't support PF MAC generation\n");
+ nfp_nsp_close(nsp);
+ return -EOPNOTSUPP;
+ }
+
+ *nspptr = nsp;
+ *index = eth_port->eth_index;
+
+ return 0;
+}
+
+static int
+nfp_net_get_port_mac_by_hwinfo(struct net_device *netdev,
+ u8 *mac_addr)
+{
+ char hwinfo[32] = {};
+ struct nfp_nsp *nsp;
+ u32 index;
+ int err;
+
+ err = nfp_net_get_nsp_hwindex(netdev, &nsp, &index);
+ if (err)
+ return err;
+
+ snprintf(hwinfo, sizeof(hwinfo), "eth%u.mac", index);
+ err = nfp_nsp_hwinfo_lookup(nsp, hwinfo, sizeof(hwinfo));
+ nfp_nsp_close(nsp);
+ if (err) {
+ netdev_err(netdev, "Reading persistent MAC address failed: %d\n",
+ err);
+ return -EOPNOTSUPP;
+ }
+
+ if (sscanf(hwinfo, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ &mac_addr[0], &mac_addr[1], &mac_addr[2],
+ &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) {
+ netdev_err(netdev, "Can't parse persistent MAC address (%s)\n",
+ hwinfo);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int
+nfp_net_set_port_mac_by_hwinfo(struct net_device *netdev,
+ u8 *mac_addr)
+{
+ char hwinfo[32] = {};
+ struct nfp_nsp *nsp;
+ u32 index;
+ int err;
+
+ err = nfp_net_get_nsp_hwindex(netdev, &nsp, &index);
+ if (err)
+ return err;
+
+ snprintf(hwinfo, sizeof(hwinfo),
+ "eth%u.mac=%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ index, mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
+ mac_addr[4], mac_addr[5]);
+
+ err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
+ nfp_nsp_close(nsp);
+ if (err) {
+ netdev_err(netdev, "HWinfo set failed: %d, hwinfo: %s\n",
+ err, hwinfo);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int
+nfp_net_get_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u8 buf[NFP_EEPROM_LEN] = {};
+
+ if (eeprom->len == 0)
+ return -EINVAL;
+
+ if (nfp_net_get_port_mac_by_hwinfo(netdev, buf))
+ return -EOPNOTSUPP;
+
+ eeprom->magic = nn->pdev->vendor | (nn->pdev->device << 16);
+ memcpy(bytes, buf + eeprom->offset, eeprom->len);
+
+ return 0;
+}
+
+static int
+nfp_net_set_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u8 buf[NFP_EEPROM_LEN] = {};
+
+ if (eeprom->len == 0)
+ return -EINVAL;
+
+ if (eeprom->magic != (nn->pdev->vendor | nn->pdev->device << 16))
+ return -EINVAL;
+
+ if (nfp_net_get_port_mac_by_hwinfo(netdev, buf))
+ return -EOPNOTSUPP;
+
+ memcpy(buf + eeprom->offset, bytes, eeprom->len);
+ if (nfp_net_set_port_mac_by_hwinfo(netdev, buf))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
static const struct ethtool_ops nfp_net_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES |
@@ -1699,6 +1841,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {
.set_dump = nfp_app_set_dump,
.get_dump_flag = nfp_app_get_dump_flag,
.get_dump_data = nfp_app_get_dump_data,
+ .get_eeprom_len = nfp_net_get_eeprom_len,
+ .get_eeprom = nfp_net_get_eeprom,
+ .set_eeprom = nfp_net_set_eeprom,
.get_module_info = nfp_port_get_module_info,
.get_module_eeprom = nfp_port_get_module_eeprom,
.get_coalesce = nfp_net_get_coalesce,
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
index ca4e05650fe6..e2d4c487e8de 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
@@ -202,7 +202,11 @@ nfp_net_pf_alloc_vnics(struct nfp_pf *pf, void __iomem *ctrl_bar,
goto err_free_prev;
}
+ if (nn->port)
+ nn->port->link_cb = nfp_net_refresh_port_table;
+
ctrl_bar += NFP_PF_CSR_SLICE_SIZE;
+ pf->sp_indiff |= nn->tlv_caps.sp_indiff;
/* Kill the vNIC if app init marked it as invalid */
if (nn->port && nn->port->type == NFP_PORT_INVALID)
@@ -304,6 +308,37 @@ err_prev_deinit:
return err;
}
+static int nfp_net_pf_cfg_nsp(struct nfp_pf *pf, bool sp_indiff)
+{
+ struct nfp_nsp *nsp;
+ char hwinfo[32];
+ int err;
+
+ nsp = nfp_nsp_open(pf->cpp);
+ if (IS_ERR(nsp)) {
+ err = PTR_ERR(nsp);
+ return err;
+ }
+
+ snprintf(hwinfo, sizeof(hwinfo), "sp_indiff=%d", sp_indiff);
+ err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
+ if (err)
+ nfp_warn(pf->cpp, "HWinfo(sp_indiff=%d) set failed: %d\n", sp_indiff, err);
+
+ nfp_nsp_close(nsp);
+ return err;
+}
+
+static int nfp_net_pf_init_nsp(struct nfp_pf *pf)
+{
+ return nfp_net_pf_cfg_nsp(pf, pf->sp_indiff);
+}
+
+static void nfp_net_pf_clean_nsp(struct nfp_pf *pf)
+{
+ (void)nfp_net_pf_cfg_nsp(pf, false);
+}
+
static int
nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride)
{
@@ -315,6 +350,8 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride)
if (IS_ERR(pf->app))
return PTR_ERR(pf->app);
+ pf->sp_indiff |= pf->app->type->id == NFP_APP_FLOWER_NIC;
+
devl_lock(devlink);
err = nfp_app_init(pf->app);
devl_unlock(devlink);
@@ -523,6 +560,57 @@ err_unmap_ctrl:
return err;
}
+static const unsigned int lr_to_speed[] = {
+ [NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0,
+ [NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN,
+ [NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000,
+ [NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000,
+ [NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000,
+ [NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000,
+ [NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000,
+ [NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000,
+};
+
+unsigned int nfp_net_lr2speed(unsigned int linkrate)
+{
+ if (linkrate < ARRAY_SIZE(lr_to_speed))
+ return lr_to_speed[linkrate];
+
+ return SPEED_UNKNOWN;
+}
+
+unsigned int nfp_net_speed2lr(unsigned int speed)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lr_to_speed); i++) {
+ if (speed == lr_to_speed[i])
+ return i;
+ }
+
+ return NFP_NET_CFG_STS_LINK_RATE_UNKNOWN;
+}
+
+static void nfp_net_notify_port_speed(struct nfp_port *port)
+{
+ struct net_device *netdev = port->netdev;
+ struct nfp_net *nn;
+ u16 sts;
+
+ if (!nfp_netdev_is_nfp_net(netdev))
+ return;
+
+ nn = netdev_priv(netdev);
+ sts = nn_readw(nn, NFP_NET_CFG_STS);
+
+ if (!(sts & NFP_NET_CFG_STS_LINK)) {
+ nn_writew(nn, NFP_NET_CFG_STS_NSP_LINK_RATE, NFP_NET_CFG_STS_LINK_RATE_UNKNOWN);
+ return;
+ }
+
+ nn_writew(nn, NFP_NET_CFG_STS_NSP_LINK_RATE, nfp_net_speed2lr(port->eth_port->speed));
+}
+
static int
nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port,
struct nfp_eth_table *eth_table)
@@ -544,6 +632,7 @@ nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port,
}
memcpy(port->eth_port, eth_port, sizeof(*eth_port));
+ nfp_net_notify_port_speed(port);
return 0;
}
@@ -725,10 +814,14 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
if (err)
goto err_clean_ddir;
- err = nfp_net_pf_alloc_irqs(pf);
+ err = nfp_net_pf_init_nsp(pf);
if (err)
goto err_free_vnics;
+ err = nfp_net_pf_alloc_irqs(pf);
+ if (err)
+ goto err_clean_nsp;
+
err = nfp_net_pf_app_start(pf);
if (err)
goto err_free_irqs;
@@ -746,6 +839,8 @@ err_stop_app:
nfp_net_pf_app_stop(pf);
err_free_irqs:
nfp_net_pf_free_irqs(pf);
+err_clean_nsp:
+ nfp_net_pf_clean_nsp(pf);
err_free_vnics:
nfp_net_pf_free_vnics(pf);
err_clean_ddir:
@@ -776,6 +871,7 @@ void nfp_net_pci_remove(struct nfp_pf *pf)
nfp_net_pf_free_vnic(pf, nn);
}
+ nfp_net_pf_clean_nsp(pf);
nfp_net_pf_app_stop(pf);
/* stop app first, to avoid double free of ctrl vNIC's ddir */
nfp_net_debugfs_dir_clean(&pf->ddir);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h
index d1ebe6c72f7f..6793cdf9ff11 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_port.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h
@@ -46,6 +46,7 @@ enum nfp_port_flags {
* @tc_offload_cnt: number of active TC offloads, how offloads are counted
* is not defined, use as a boolean
* @app: backpointer to the app structure
+ * @link_cb: callback when link status changed
* @dl_port: devlink port structure
* @eth_id: for %NFP_PORT_PHYS_PORT port ID in NFP enumeration scheme
* @eth_forced: for %NFP_PORT_PHYS_PORT port is forced UP or DOWN, don't change
@@ -66,6 +67,7 @@ struct nfp_port {
unsigned long tc_offload_cnt;
struct nfp_app *app;
+ void (*link_cb)(struct nfp_port *port);
struct devlink_port dl_port;