summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/sfc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/sfc')
-rw-r--r--drivers/net/ethernet/sfc/Kconfig1
-rw-r--r--drivers/net/ethernet/sfc/Makefile3
-rw-r--r--drivers/net/ethernet/sfc/ef100_netdev.c30
-rw-r--r--drivers/net/ethernet/sfc/ef100_nic.c114
-rw-r--r--drivers/net/ethernet/sfc/ef100_nic.h7
-rw-r--r--drivers/net/ethernet/sfc/ef100_rep.c57
-rw-r--r--drivers/net/ethernet/sfc/ef100_rep.h10
-rw-r--r--drivers/net/ethernet/sfc/efx.c4
-rw-r--r--drivers/net/ethernet/sfc/efx_devlink.c731
-rw-r--r--drivers/net/ethernet/sfc/efx_devlink.h47
-rw-r--r--drivers/net/ethernet/sfc/mae.c218
-rw-r--r--drivers/net/ethernet/sfc/mae.h40
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c72
-rw-r--r--drivers/net/ethernet/sfc/mcdi.h8
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h8
-rw-r--r--drivers/net/ethernet/sfc/siena/efx.c4
16 files changed, 1315 insertions, 39 deletions
diff --git a/drivers/net/ethernet/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig
index 0950e6b0508f..4af36ba8906b 100644
--- a/drivers/net/ethernet/sfc/Kconfig
+++ b/drivers/net/ethernet/sfc/Kconfig
@@ -22,6 +22,7 @@ config SFC
depends on PTP_1588_CLOCK_OPTIONAL
select MDIO
select CRC32
+ select NET_DEVLINK
help
This driver supports 10/40-gigabit Ethernet cards based on
the Solarflare SFC9100-family controllers.
diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile
index 712a48d00069..55b9c73cd8ef 100644
--- a/drivers/net/ethernet/sfc/Makefile
+++ b/drivers/net/ethernet/sfc/Makefile
@@ -6,7 +6,8 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \
mcdi.o mcdi_port.o mcdi_port_common.o \
mcdi_functions.o mcdi_filters.o mcdi_mon.o \
ef100.o ef100_nic.o ef100_netdev.o \
- ef100_ethtool.o ef100_rx.o ef100_tx.o
+ ef100_ethtool.o ef100_rx.o ef100_tx.o \
+ efx_devlink.o
sfc-$(CONFIG_SFC_MTD) += mtd.o
sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
mae.o tc.o tc_bindings.o tc_counters.o
diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c
index ddcc325ed570..d916877b5a9a 100644
--- a/drivers/net/ethernet/sfc/ef100_netdev.c
+++ b/drivers/net/ethernet/sfc/ef100_netdev.c
@@ -24,6 +24,7 @@
#include "rx_common.h"
#include "ef100_sriov.h"
#include "tc_bindings.h"
+#include "efx_devlink.h"
static void ef100_update_name(struct efx_nic *efx)
{
@@ -332,9 +333,11 @@ void ef100_remove_netdev(struct efx_probe_data *probe_data)
efx_ef100_pci_sriov_disable(efx, true);
#endif
+ efx_fini_devlink_lock(efx);
ef100_unregister_netdev(efx);
#ifdef CONFIG_SFC_SRIOV
+ ef100_pf_unset_devlink_port(efx);
efx_fini_tc(efx);
#endif
@@ -345,6 +348,8 @@ void ef100_remove_netdev(struct efx_probe_data *probe_data)
kfree(efx->phy_data);
efx->phy_data = NULL;
+ efx_fini_devlink_and_unlock(efx);
+
free_netdev(efx->net_dev);
efx->net_dev = NULL;
efx->state = STATE_PROBED;
@@ -354,6 +359,7 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data)
{
struct efx_nic *efx = &probe_data->efx;
struct efx_probe_data **probe_ptr;
+ struct ef100_nic_data *nic_data;
struct net_device *net_dev;
int rc;
@@ -405,6 +411,20 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data)
/* Don't fail init if RSS setup doesn't work. */
efx_mcdi_push_default_indir_table(efx, efx->n_rx_channels);
+ nic_data = efx->nic_data;
+ rc = ef100_get_mac_address(efx, net_dev->perm_addr, CLIENT_HANDLE_SELF,
+ efx->type->is_vf);
+ if (rc)
+ return rc;
+ /* Assign MAC address */
+ eth_hw_addr_set(net_dev, net_dev->perm_addr);
+ ether_addr_copy(nic_data->port_id, net_dev->perm_addr);
+
+ /* devlink creation, registration and lock */
+ rc = efx_probe_devlink_and_lock(efx);
+ if (rc)
+ pci_info(efx->pci_dev, "devlink registration failed");
+
rc = ef100_register_netdev(efx);
if (rc)
goto fail;
@@ -413,6 +433,9 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data)
rc = ef100_probe_netdev_pf(efx);
if (rc)
goto fail;
+#ifdef CONFIG_SFC_SRIOV
+ ef100_pf_set_devlink_port(efx);
+#endif
}
efx->netdev_notifier.notifier_call = ef100_netdev_event;
@@ -423,6 +446,13 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data)
goto fail;
}
+ efx_probe_devlink_unlock(efx);
+ return rc;
fail:
+#ifdef CONFIG_SFC_SRIOV
+ /* remove devlink port if does exist */
+ ef100_pf_unset_devlink_port(efx);
+#endif
+ efx_probe_devlink_unlock(efx);
return rc;
}
diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c
index ad686c671ab8..4dc643b0d2db 100644
--- a/drivers/net/ethernet/sfc/ef100_nic.c
+++ b/drivers/net/ethernet/sfc/ef100_nic.c
@@ -130,23 +130,34 @@ static void ef100_mcdi_reboot_detected(struct efx_nic *efx)
/* MCDI calls
*/
-static int ef100_get_mac_address(struct efx_nic *efx, u8 *mac_address)
+int ef100_get_mac_address(struct efx_nic *efx, u8 *mac_address,
+ int client_handle, bool empty_ok)
{
- MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_MAC_ADDRESSES_OUT_LEN);
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_CLIENT_MAC_ADDRESSES_OUT_LEN(1));
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_CLIENT_MAC_ADDRESSES_IN_LEN);
size_t outlen;
int rc;
BUILD_BUG_ON(MC_CMD_GET_MAC_ADDRESSES_IN_LEN != 0);
+ MCDI_SET_DWORD(inbuf, GET_CLIENT_MAC_ADDRESSES_IN_CLIENT_HANDLE,
+ client_handle);
- rc = efx_mcdi_rpc(efx, MC_CMD_GET_MAC_ADDRESSES, NULL, 0,
- outbuf, sizeof(outbuf), &outlen);
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_CLIENT_MAC_ADDRESSES, inbuf,
+ sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
- if (outlen < MC_CMD_GET_MAC_ADDRESSES_OUT_LEN)
- return -EIO;
- ether_addr_copy(mac_address,
- MCDI_PTR(outbuf, GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE));
+ if (outlen >= MC_CMD_GET_CLIENT_MAC_ADDRESSES_OUT_LEN(1)) {
+ ether_addr_copy(mac_address,
+ MCDI_PTR(outbuf, GET_CLIENT_MAC_ADDRESSES_OUT_MAC_ADDRS));
+ } else if (empty_ok) {
+ pci_warn(efx->pci_dev,
+ "No MAC address provisioned for client ID %#x.\n",
+ client_handle);
+ eth_zero_addr(mac_address);
+ } else {
+ return -ENOENT;
+ }
return 0;
}
@@ -388,14 +399,14 @@ static int ef100_filter_table_up(struct efx_nic *efx)
* filter insertion will need to take the lock for read.
*/
up_write(&efx->filter_sem);
-#ifdef CONFIG_SFC_SRIOV
- rc = efx_tc_insert_rep_filters(efx);
+ if (IS_ENABLED(CONFIG_SFC_SRIOV))
+ rc = efx_tc_insert_rep_filters(efx);
+
/* Rep filter failure is nonfatal */
if (rc)
netif_warn(efx, drv, efx->net_dev,
"Failed to insert representor filters, rc %d\n",
rc);
-#endif
return 0;
fail_vlan0:
@@ -408,9 +419,8 @@ fail_unspec:
static void ef100_filter_table_down(struct efx_nic *efx)
{
-#ifdef CONFIG_SFC_SRIOV
- efx_tc_remove_rep_filters(efx);
-#endif
+ if (IS_ENABLED(CONFIG_SFC_SRIOV))
+ efx_tc_remove_rep_filters(efx);
down_write(&efx->filter_sem);
efx_mcdi_filter_del_vlan(efx, 0);
efx_mcdi_filter_del_vlan(efx, EFX_FILTER_VID_UNSPEC);
@@ -726,7 +736,6 @@ static unsigned int efx_ef100_recycle_ring_size(const struct efx_nic *efx)
return 10 * EFX_RECYCLE_RING_SIZE_10G;
}
-#ifdef CONFIG_SFC_SRIOV
static int efx_ef100_get_base_mport(struct efx_nic *efx)
{
struct ef100_nic_data *nic_data = efx->nic_data;
@@ -736,7 +745,7 @@ static int efx_ef100_get_base_mport(struct efx_nic *efx)
/* Construct mport selector for "physical network port" */
efx_mae_mport_wire(efx, &selector);
/* Look up actual mport ID */
- rc = efx_mae_lookup_mport(efx, selector, &id);
+ rc = efx_mae_fw_lookup_mport(efx, selector, &id);
if (rc)
return rc;
/* The ID should always fit in 16 bits, because that's how wide the
@@ -747,9 +756,21 @@ static int efx_ef100_get_base_mport(struct efx_nic *efx)
id);
nic_data->base_mport = id;
nic_data->have_mport = true;
+
+ /* Construct mport selector for "calling PF" */
+ efx_mae_mport_uplink(efx, &selector);
+ /* Look up actual mport ID */
+ rc = efx_mae_fw_lookup_mport(efx, selector, &id);
+ if (rc)
+ return rc;
+ if (id >> 16)
+ netif_warn(efx, probe, efx->net_dev, "Bad own m-port id %#x\n",
+ id);
+ nic_data->own_mport = id;
+ nic_data->have_own_mport = true;
+
return 0;
}
-#endif
static int compare_versions(const char *a, const char *b)
{
@@ -1098,23 +1119,42 @@ fail:
return rc;
}
+/* MCDI commands are related to the same device issuing them. This function
+ * allows to do an MCDI command on behalf of another device, mainly PFs setting
+ * things for VFs.
+ */
+int efx_ef100_lookup_client_id(struct efx_nic *efx, efx_qword_t pciefn, u32 *id)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_CLIENT_HANDLE_OUT_LEN);
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_CLIENT_HANDLE_IN_LEN);
+ u64 pciefn_flat = le64_to_cpu(pciefn.u64[0]);
+ size_t outlen;
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, GET_CLIENT_HANDLE_IN_TYPE,
+ MC_CMD_GET_CLIENT_HANDLE_IN_TYPE_FUNC);
+ MCDI_SET_QWORD(inbuf, GET_CLIENT_HANDLE_IN_FUNC,
+ pciefn_flat);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_CLIENT_HANDLE, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ return rc;
+ if (outlen < sizeof(outbuf))
+ return -EIO;
+ *id = MCDI_DWORD(outbuf, GET_CLIENT_HANDLE_OUT_HANDLE);
+ return 0;
+}
+
int ef100_probe_netdev_pf(struct efx_nic *efx)
{
struct ef100_nic_data *nic_data = efx->nic_data;
struct net_device *net_dev = efx->net_dev;
int rc;
- rc = ef100_get_mac_address(efx, net_dev->perm_addr);
- if (rc)
- goto fail;
- /* Assign MAC address */
- eth_hw_addr_set(net_dev, net_dev->perm_addr);
- memcpy(nic_data->port_id, net_dev->perm_addr, ETH_ALEN);
-
- if (!nic_data->grp_mae)
+ if (!IS_ENABLED(CONFIG_SFC_SRIOV) || !nic_data->grp_mae)
return 0;
-#ifdef CONFIG_SFC_SRIOV
rc = efx_init_struct_tc(efx);
if (rc)
return rc;
@@ -1126,6 +1166,14 @@ int ef100_probe_netdev_pf(struct efx_nic *efx)
rc);
}
+ rc = efx_init_mae(efx);
+ if (rc)
+ netif_warn(efx, probe, net_dev,
+ "Failed to init MAE rc %d; representors will not function\n",
+ rc);
+ else
+ efx_ef100_init_reps(efx);
+
rc = efx_init_tc(efx);
if (rc) {
/* Either we don't have an MAE at all (i.e. legacy v-switching),
@@ -1141,10 +1189,6 @@ int ef100_probe_netdev_pf(struct efx_nic *efx)
net_dev->features |= NETIF_F_HW_TC;
efx->fixed_features |= NETIF_F_HW_TC;
}
-#endif
- return 0;
-
-fail:
return rc;
}
@@ -1157,6 +1201,11 @@ void ef100_remove(struct efx_nic *efx)
{
struct ef100_nic_data *nic_data = efx->nic_data;
+ if (IS_ENABLED(CONFIG_SFC_SRIOV) && efx->mae) {
+ efx_ef100_fini_reps(efx);
+ efx_fini_mae(efx);
+ }
+
efx_mcdi_detach(efx);
efx_mcdi_fini(efx);
if (nic_data)
@@ -1249,9 +1298,8 @@ const struct efx_nic_type ef100_pf_nic_type = {
.update_stats = ef100_update_stats,
.pull_stats = efx_mcdi_mac_pull_stats,
.stop_stats = efx_mcdi_mac_stop_stats,
-#ifdef CONFIG_SFC_SRIOV
- .sriov_configure = efx_ef100_sriov_configure,
-#endif
+ .sriov_configure = IS_ENABLED(CONFIG_SFC_SRIOV) ?
+ efx_ef100_sriov_configure : NULL,
/* Per-type bar/size configuration not used on ef100. Location of
* registers is defined by extended capabilities.
diff --git a/drivers/net/ethernet/sfc/ef100_nic.h b/drivers/net/ethernet/sfc/ef100_nic.h
index 0295933145fa..f1ed481c1260 100644
--- a/drivers/net/ethernet/sfc/ef100_nic.h
+++ b/drivers/net/ethernet/sfc/ef100_nic.h
@@ -74,6 +74,10 @@ struct ef100_nic_data {
u64 stats[EF100_STAT_COUNT];
u32 base_mport;
bool have_mport; /* base_mport was populated successfully */
+ u32 own_mport;
+ u32 local_mae_intf; /* interface_idx that corresponds to us, in mport enumerate */
+ bool have_own_mport; /* own_mport was populated successfully */
+ bool have_local_intf; /* local_mae_intf was populated successfully */
bool grp_mae; /* MAE Privilege */
u16 tso_max_hdr_len;
u16 tso_max_payload_num_segs;
@@ -88,4 +92,7 @@ int efx_ef100_init_datapath_caps(struct efx_nic *efx);
int ef100_phy_probe(struct efx_nic *efx);
int ef100_filter_table_probe(struct efx_nic *efx);
+int ef100_get_mac_address(struct efx_nic *efx, u8 *mac_address,
+ int client_handle, bool empty_ok);
+int efx_ef100_lookup_client_id(struct efx_nic *efx, efx_qword_t pciefn, u32 *id);
#endif /* EFX_EF100_NIC_H */
diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c
index 81ab22c74635..0b3083ef0ead 100644
--- a/drivers/net/ethernet/sfc/ef100_rep.c
+++ b/drivers/net/ethernet/sfc/ef100_rep.c
@@ -9,12 +9,14 @@
* by the Free Software Foundation, incorporated herein by reference.
*/
+#include <linux/rhashtable.h>
#include "ef100_rep.h"
#include "ef100_netdev.h"
#include "ef100_nic.h"
#include "mae.h"
#include "rx_common.h"
#include "tc_bindings.h"
+#include "efx_devlink.h"
#define EFX_EF100_REP_DRIVER "efx_ef100_rep"
@@ -242,14 +244,11 @@ fail1:
static int efx_ef100_configure_rep(struct efx_rep *efv)
{
struct efx_nic *efx = efv->parent;
- u32 selector;
int rc;
efv->rx_pring_size = EFX_REP_DEFAULT_PSEUDO_RING_SIZE;
- /* Construct mport selector for corresponding VF */
- efx_mae_mport_vf(efx, efv->idx, &selector);
/* Look up actual mport ID */
- rc = efx_mae_lookup_mport(efx, selector, &efv->mport);
+ rc = efx_mae_lookup_mport(efx, efv->idx, &efv->mport);
if (rc)
return rc;
pci_dbg(efx->pci_dev, "VF %u has mport ID %#x\n", efv->idx, efv->mport);
@@ -299,6 +298,7 @@ int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i)
i, rc);
goto fail1;
}
+ ef100_rep_set_devlink_port(efv);
rc = register_netdev(efv->net_dev);
if (rc) {
pci_err(efx->pci_dev,
@@ -310,6 +310,7 @@ int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i)
efv->net_dev->name);
return 0;
fail2:
+ ef100_rep_unset_devlink_port(efv);
efx_ef100_deconfigure_rep(efv);
fail1:
efx_ef100_rep_destroy_netdev(efv);
@@ -325,6 +326,7 @@ void efx_ef100_vfrep_destroy(struct efx_nic *efx, struct efx_rep *efv)
return;
netif_dbg(efx, drv, rep_dev, "Removing VF representor\n");
unregister_netdev(rep_dev);
+ ef100_rep_unset_devlink_port(efv);
efx_ef100_deconfigure_rep(efv);
efx_ef100_rep_destroy_netdev(efv);
}
@@ -341,6 +343,53 @@ void efx_ef100_fini_vfreps(struct efx_nic *efx)
efx_ef100_vfrep_destroy(efx, efv);
}
+static bool ef100_mport_is_pcie_vnic(struct mae_mport_desc *mport_desc)
+{
+ return mport_desc->mport_type == MAE_MPORT_DESC_MPORT_TYPE_VNIC &&
+ mport_desc->vnic_client_type == MAE_MPORT_DESC_VNIC_CLIENT_TYPE_FUNCTION;
+}
+
+bool ef100_mport_on_local_intf(struct efx_nic *efx,
+ struct mae_mport_desc *mport_desc)
+{
+ struct ef100_nic_data *nic_data = efx->nic_data;
+ bool pcie_func;
+
+ pcie_func = ef100_mport_is_pcie_vnic(mport_desc);
+
+ return nic_data->have_local_intf && pcie_func &&
+ mport_desc->interface_idx == nic_data->local_mae_intf;
+}
+
+bool ef100_mport_is_vf(struct mae_mport_desc *mport_desc)
+{
+ bool pcie_func;
+
+ pcie_func = ef100_mport_is_pcie_vnic(mport_desc);
+ return pcie_func && (mport_desc->vf_idx != MAE_MPORT_DESC_VF_IDX_NULL);
+}
+
+void efx_ef100_init_reps(struct efx_nic *efx)
+{
+ struct ef100_nic_data *nic_data = efx->nic_data;
+ int rc;
+
+ nic_data->have_local_intf = false;
+ rc = efx_mae_enumerate_mports(efx);
+ if (rc)
+ pci_warn(efx->pci_dev,
+ "Could not enumerate mports (rc=%d), are we admin?",
+ rc);
+}
+
+void efx_ef100_fini_reps(struct efx_nic *efx)
+{
+ struct efx_mae *mae = efx->mae;
+
+ rhashtable_free_and_destroy(&mae->mports_ht, efx_mae_remove_mport,
+ NULL);
+}
+
static int efx_ef100_rep_poll(struct napi_struct *napi, int weight)
{
struct efx_rep *efv = container_of(napi, struct efx_rep, napi);
diff --git a/drivers/net/ethernet/sfc/ef100_rep.h b/drivers/net/ethernet/sfc/ef100_rep.h
index c21bc716f847..a042525a2240 100644
--- a/drivers/net/ethernet/sfc/ef100_rep.h
+++ b/drivers/net/ethernet/sfc/ef100_rep.h
@@ -22,6 +22,8 @@ struct efx_rep_sw_stats {
atomic64_t rx_dropped, tx_errors;
};
+struct devlink_port;
+
/**
* struct efx_rep - Private data for an Efx representor
*
@@ -39,6 +41,7 @@ struct efx_rep_sw_stats {
* @rx_lock: protects @rx_list
* @napi: NAPI control structure
* @stats: software traffic counters for netdev stats
+ * @dl_port: devlink port associated to this netdev representor
*/
struct efx_rep {
struct efx_nic *parent;
@@ -54,6 +57,7 @@ struct efx_rep {
spinlock_t rx_lock;
struct napi_struct napi;
struct efx_rep_sw_stats stats;
+ struct devlink_port *dl_port;
};
int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i);
@@ -67,4 +71,10 @@ void efx_ef100_rep_rx_packet(struct efx_rep *efv, struct efx_rx_buffer *rx_buf);
*/
struct efx_rep *efx_ef100_find_rep_by_mport(struct efx_nic *efx, u16 mport);
extern const struct net_device_ops efx_ef100_rep_netdev_ops;
+void efx_ef100_init_reps(struct efx_nic *efx);
+void efx_ef100_fini_reps(struct efx_nic *efx);
+struct mae_mport_desc;
+bool ef100_mport_on_local_intf(struct efx_nic *efx,
+ struct mae_mport_desc *mport_desc);
+bool ef100_mport_is_vf(struct mae_mport_desc *mport_desc);
#endif /* EF100_REP_H */
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 3a86f1213a05..02c2adeb0a12 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -1028,6 +1028,10 @@ static int efx_pci_probe_post_io(struct efx_nic *efx)
net_dev->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER;
net_dev->features |= efx->fixed_features;
+ net_dev->xdp_features = NETDEV_XDP_ACT_BASIC |
+ NETDEV_XDP_ACT_REDIRECT |
+ NETDEV_XDP_ACT_NDO_XMIT;
+
rc = efx_register_netdev(efx);
if (!rc)
return 0;
diff --git a/drivers/net/ethernet/sfc/efx_devlink.c b/drivers/net/ethernet/sfc/efx_devlink.c
new file mode 100644
index 000000000000..381b805659d3
--- /dev/null
+++ b/drivers/net/ethernet/sfc/efx_devlink.c
@@ -0,0 +1,731 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/****************************************************************************
+ * Driver for AMD network controllers and boards
+ * Copyright (C) 2023, Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include "net_driver.h"
+#include "ef100_nic.h"
+#include "efx_devlink.h"
+#include <linux/rtc.h>
+#include "mcdi.h"
+#include "mcdi_functions.h"
+#include "mcdi_pcol.h"
+#ifdef CONFIG_SFC_SRIOV
+#include "mae.h"
+#include "ef100_rep.h"
+#endif
+
+struct efx_devlink {
+ struct efx_nic *efx;
+};
+
+#ifdef CONFIG_SFC_SRIOV
+static void efx_devlink_del_port(struct devlink_port *dl_port)
+{
+ if (!dl_port)
+ return;
+ devl_port_unregister(dl_port);
+}
+
+static int efx_devlink_add_port(struct efx_nic *efx,
+ struct mae_mport_desc *mport)
+{
+ bool external = false;
+
+ if (!ef100_mport_on_local_intf(efx, mport))
+ external = true;
+
+ switch (mport->mport_type) {
+ case MAE_MPORT_DESC_MPORT_TYPE_VNIC:
+ if (mport->vf_idx != MAE_MPORT_DESC_VF_IDX_NULL)
+ devlink_port_attrs_pci_vf_set(&mport->dl_port, 0, mport->pf_idx,
+ mport->vf_idx,
+ external);
+ else
+ devlink_port_attrs_pci_pf_set(&mport->dl_port, 0, mport->pf_idx,
+ external);
+ break;
+ default:
+ /* MAE_MPORT_DESC_MPORT_ALIAS and UNDEFINED */
+ return 0;
+ }
+
+ mport->dl_port.index = mport->mport_id;
+
+ return devl_port_register(efx->devlink, &mport->dl_port, mport->mport_id);
+}
+
+static int efx_devlink_port_addr_get(struct devlink_port *port, u8 *hw_addr,
+ int *hw_addr_len,
+ struct netlink_ext_ack *extack)
+{
+ struct efx_devlink *devlink = devlink_priv(port->devlink);
+ struct mae_mport_desc *mport_desc;
+ efx_qword_t pciefn;
+ u32 client_id;
+ int rc = 0;
+
+ mport_desc = container_of(port, struct mae_mport_desc, dl_port);
+
+ if (!ef100_mport_on_local_intf(devlink->efx, mport_desc)) {
+ rc = -EINVAL;
+ NL_SET_ERR_MSG_FMT(extack,
+ "Port not on local interface (mport: %u)",
+ mport_desc->mport_id);
+ goto out;
+ }
+
+ if (ef100_mport_is_vf(mport_desc))
+ EFX_POPULATE_QWORD_3(pciefn,
+ PCIE_FUNCTION_PF, PCIE_FUNCTION_PF_NULL,
+ PCIE_FUNCTION_VF, mport_desc->vf_idx,
+ PCIE_FUNCTION_INTF, PCIE_INTERFACE_CALLER);
+ else
+ EFX_POPULATE_QWORD_3(pciefn,
+ PCIE_FUNCTION_PF, mport_desc->pf_idx,
+ PCIE_FUNCTION_VF, PCIE_FUNCTION_VF_NULL,
+ PCIE_FUNCTION_INTF, PCIE_INTERFACE_CALLER);
+
+ rc = efx_ef100_lookup_client_id(devlink->efx, pciefn, &client_id);
+ if (rc) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "No internal client_ID for port (mport: %u)",
+ mport_desc->mport_id);
+ goto out;
+ }
+
+ rc = ef100_get_mac_address(devlink->efx, hw_addr, client_id, true);
+ if (rc != 0)
+ NL_SET_ERR_MSG_FMT(extack,
+ "No available MAC for port (mport: %u)",
+ mport_desc->mport_id);
+out:
+ *hw_addr_len = ETH_ALEN;
+ return rc;
+}
+
+static int efx_devlink_port_addr_set(struct devlink_port *port,
+ const u8 *hw_addr, int hw_addr_len,
+ struct netlink_ext_ack *extack)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_CLIENT_MAC_ADDRESSES_IN_LEN(1));
+ struct efx_devlink *devlink = devlink_priv(port->devlink);
+ struct mae_mport_desc *mport_desc;
+ efx_qword_t pciefn;
+ u32 client_id;
+ int rc;
+
+ mport_desc = container_of(port, struct mae_mport_desc, dl_port);
+
+ if (!ef100_mport_is_vf(mport_desc)) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "port mac change not allowed (mport: %u)",
+ mport_desc->mport_id);
+ return -EPERM;
+ }
+
+ EFX_POPULATE_QWORD_3(pciefn,
+ PCIE_FUNCTION_PF, PCIE_FUNCTION_PF_NULL,
+ PCIE_FUNCTION_VF, mport_desc->vf_idx,
+ PCIE_FUNCTION_INTF, PCIE_INTERFACE_CALLER);
+
+ rc = efx_ef100_lookup_client_id(devlink->efx, pciefn, &client_id);
+ if (rc) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "No internal client_ID for port (mport: %u)",
+ mport_desc->mport_id);
+ return rc;
+ }
+
+ MCDI_SET_DWORD(inbuf, SET_CLIENT_MAC_ADDRESSES_IN_CLIENT_HANDLE,
+ client_id);
+
+ ether_addr_copy(MCDI_PTR(inbuf, SET_CLIENT_MAC_ADDRESSES_IN_MAC_ADDRS),
+ hw_addr);
+
+ rc = efx_mcdi_rpc(devlink->efx, MC_CMD_SET_CLIENT_MAC_ADDRESSES, inbuf,
+ sizeof(inbuf), NULL, 0, NULL);
+ if (rc)
+ NL_SET_ERR_MSG_FMT(extack,
+ "sfc MC_CMD_SET_CLIENT_MAC_ADDRESSES mcdi error (mport: %u)",
+ mport_desc->mport_id);
+
+ return rc;
+}
+
+#endif
+
+static int efx_devlink_info_nvram_partition(struct efx_nic *efx,
+ struct devlink_info_req *req,
+ unsigned int partition_type,
+ const char *version_name)
+{
+ char buf[EFX_MAX_VERSION_INFO_LEN];
+ u16 version[4];
+ int rc;
+
+ rc = efx_mcdi_nvram_metadata(efx, partition_type, NULL, version, NULL,
+ 0);
+ if (rc) {
+ netif_err(efx, drv, efx->net_dev, "mcdi nvram %s: failed\n",
+ version_name);
+ return rc;
+ }
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", version[0],
+ version[1], version[2], version[3]);
+ devlink_info_version_stored_put(req, version_name, buf);
+
+ return 0;
+}
+
+static int efx_devlink_info_stored_versions(struct efx_nic *efx,
+ struct devlink_info_req *req)
+{
+ int rc;
+
+ rc = efx_devlink_info_nvram_partition(efx, req,
+ NVRAM_PARTITION_TYPE_BUNDLE,
+ DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID);
+ if (rc)
+ return rc;
+
+ rc = efx_devlink_info_nvram_partition(efx, req,
+ NVRAM_PARTITION_TYPE_MC_FIRMWARE,
+ DEVLINK_INFO_VERSION_GENERIC_FW_MGMT);
+ if (rc)
+ return rc;
+
+ rc = efx_devlink_info_nvram_partition(efx, req,
+ NVRAM_PARTITION_TYPE_SUC_FIRMWARE,
+ EFX_DEVLINK_INFO_VERSION_FW_MGMT_SUC);
+ if (rc)
+ return rc;
+
+ rc = efx_devlink_info_nvram_partition(efx, req,
+ NVRAM_PARTITION_TYPE_EXPANSION_ROM,
+ EFX_DEVLINK_INFO_VERSION_FW_EXPROM);
+ if (rc)
+ return rc;
+
+ rc = efx_devlink_info_nvram_partition(efx, req,
+ NVRAM_PARTITION_TYPE_EXPANSION_UEFI,
+ EFX_DEVLINK_INFO_VERSION_FW_UEFI);
+ return rc;
+}
+
+#define EFX_VER_FLAG(_f) \
+ (MC_CMD_GET_VERSION_V5_OUT_ ## _f ## _PRESENT_LBN)
+
+static void efx_devlink_info_running_v2(struct efx_nic *efx,
+ struct devlink_info_req *req,
+ unsigned int flags, efx_dword_t *outbuf)
+{
+ char buf[EFX_MAX_VERSION_INFO_LEN];
+ union {
+ const __le32 *dwords;
+ const __le16 *words;
+ const char *str;
+ } ver;
+ struct rtc_time build_date;
+ unsigned int build_id;
+ size_t offset;
+ __maybe_unused u64 tstamp;
+
+ if (flags & BIT(EFX_VER_FLAG(BOARD_EXT_INFO))) {
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%s",
+ MCDI_PTR(outbuf, GET_VERSION_V2_OUT_BOARD_NAME));
+ devlink_info_version_fixed_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
+ buf);
+
+ /* Favour full board version if present (in V5 or later) */
+ if (~flags & BIT(EFX_VER_FLAG(BOARD_VERSION))) {
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u",
+ MCDI_DWORD(outbuf,
+ GET_VERSION_V2_OUT_BOARD_REVISION));
+ devlink_info_version_fixed_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_BOARD_REV,
+ buf);
+ }
+
+ ver.str = MCDI_PTR(outbuf, GET_VERSION_V2_OUT_BOARD_SERIAL);
+ if (ver.str[0])
+ devlink_info_board_serial_number_put(req, ver.str);
+ }
+
+ if (flags & BIT(EFX_VER_FLAG(FPGA_EXT_INFO))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V2_OUT_FPGA_VERSION);
+ offset = snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u_%c%u",
+ le32_to_cpu(ver.dwords[0]),
+ 'A' + le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]));
+
+ ver.str = MCDI_PTR(outbuf, GET_VERSION_V2_OUT_FPGA_EXTRA);
+ if (ver.str[0])
+ snprintf(&buf[offset], EFX_MAX_VERSION_INFO_LEN - offset,
+ " (%s)", ver.str);
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_FPGA_REV,
+ buf);
+ }
+
+ if (flags & BIT(EFX_VER_FLAG(CMC_EXT_INFO))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V2_OUT_CMCFW_VERSION);
+ offset = snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]),
+ le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]),
+ le32_to_cpu(ver.dwords[3]));
+
+#ifdef CONFIG_RTC_LIB
+ tstamp = MCDI_QWORD(outbuf,
+ GET_VERSION_V2_OUT_CMCFW_BUILD_DATE);
+ if (tstamp) {
+ rtc_time64_to_tm(tstamp, &build_date);
+ snprintf(&buf[offset], EFX_MAX_VERSION_INFO_LEN - offset,
+ " (%ptRd)", &build_date);
+ }
+#endif
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_FW_MGMT_CMC,
+ buf);
+ }
+
+ ver.words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_V2_OUT_VERSION);
+ offset = snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le16_to_cpu(ver.words[0]), le16_to_cpu(ver.words[1]),
+ le16_to_cpu(ver.words[2]), le16_to_cpu(ver.words[3]));
+ if (flags & BIT(EFX_VER_FLAG(MCFW_EXT_INFO))) {
+ build_id = MCDI_DWORD(outbuf, GET_VERSION_V2_OUT_MCFW_BUILD_ID);
+ snprintf(&buf[offset], EFX_MAX_VERSION_INFO_LEN - offset,
+ " (%x) %s", build_id,
+ MCDI_PTR(outbuf, GET_VERSION_V2_OUT_MCFW_BUILD_NAME));
+ }
+ devlink_info_version_running_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_FW_MGMT,
+ buf);
+
+ if (flags & BIT(EFX_VER_FLAG(SUCFW_EXT_INFO))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V2_OUT_SUCFW_VERSION);
+#ifdef CONFIG_RTC_LIB
+ tstamp = MCDI_QWORD(outbuf,
+ GET_VERSION_V2_OUT_SUCFW_BUILD_DATE);
+ rtc_time64_to_tm(tstamp, &build_date);
+#else
+ memset(&build_date, 0, sizeof(build_date));
+#endif
+ build_id = MCDI_DWORD(outbuf, GET_VERSION_V2_OUT_SUCFW_CHIP_ID);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN,
+ "%u.%u.%u.%u type %x (%ptRd)",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]), le32_to_cpu(ver.dwords[3]),
+ build_id, &build_date);
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_FW_MGMT_SUC,
+ buf);
+ }
+}
+
+static void efx_devlink_info_running_v3(struct efx_nic *efx,
+ struct devlink_info_req *req,
+ unsigned int flags, efx_dword_t *outbuf)
+{
+ char buf[EFX_MAX_VERSION_INFO_LEN];
+ union {
+ const __le32 *dwords;
+ const __le16 *words;
+ const char *str;
+ } ver;
+
+ if (flags & BIT(EFX_VER_FLAG(DATAPATH_HW_VERSION))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V3_OUT_DATAPATH_HW_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]));
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_DATAPATH_HW,
+ buf);
+ }
+
+ if (flags & BIT(EFX_VER_FLAG(DATAPATH_FW_VERSION))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V3_OUT_DATAPATH_FW_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]));
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_DATAPATH_FW,
+ buf);
+ }
+}
+
+static void efx_devlink_info_running_v4(struct efx_nic *efx,
+ struct devlink_info_req *req,
+ unsigned int flags, efx_dword_t *outbuf)
+{
+ char buf[EFX_MAX_VERSION_INFO_LEN];
+ union {
+ const __le32 *dwords;
+ const __le16 *words;
+ const char *str;
+ } ver;
+
+ if (flags & BIT(EFX_VER_FLAG(SOC_BOOT_VERSION))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V4_OUT_SOC_BOOT_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]),
+ le32_to_cpu(ver.dwords[3]));
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_SOC_BOOT,
+ buf);
+ }
+
+ if (flags & BIT(EFX_VER_FLAG(SOC_UBOOT_VERSION))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V4_OUT_SOC_UBOOT_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]),
+ le32_to_cpu(ver.dwords[3]));
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_SOC_UBOOT,
+ buf);
+ }
+
+ if (flags & BIT(EFX_VER_FLAG(SOC_MAIN_ROOTFS_VERSION))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V4_OUT_SOC_MAIN_ROOTFS_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]),
+ le32_to_cpu(ver.dwords[3]));
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_SOC_MAIN,
+ buf);
+ }
+
+ if (flags & BIT(EFX_VER_FLAG(SOC_RECOVERY_BUILDROOT_VERSION))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V4_OUT_SOC_RECOVERY_BUILDROOT_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]),
+ le32_to_cpu(ver.dwords[3]));
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_SOC_RECOVERY,
+ buf);
+ }
+
+ if (flags & BIT(EFX_VER_FLAG(SUCFW_VERSION)) &&
+ ~flags & BIT(EFX_VER_FLAG(SUCFW_EXT_INFO))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V4_OUT_SUCFW_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]),
+ le32_to_cpu(ver.dwords[3]));
+
+ devlink_info_version_running_put(req,
+ EFX_DEVLINK_INFO_VERSION_FW_MGMT_SUC,
+ buf);
+ }
+}
+
+static void efx_devlink_info_running_v5(struct efx_nic *efx,
+ struct devlink_info_req *req,
+ unsigned int flags, efx_dword_t *outbuf)
+{
+ char buf[EFX_MAX_VERSION_INFO_LEN];
+ union {
+ const __le32 *dwords;
+ const __le16 *words;
+ const char *str;
+ } ver;
+
+ if (flags & BIT(EFX_VER_FLAG(BOARD_VERSION))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V5_OUT_BOARD_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]),
+ le32_to_cpu(ver.dwords[3]));
+
+ devlink_info_version_running_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_BOARD_REV,
+ buf);
+ }
+
+ if (flags & BIT(EFX_VER_FLAG(BUNDLE_VERSION))) {
+ ver.dwords = (__le32 *)MCDI_PTR(outbuf,
+ GET_VERSION_V5_OUT_BUNDLE_VERSION);
+
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]),
+ le32_to_cpu(ver.dwords[2]),
+ le32_to_cpu(ver.dwords[3]));
+
+ devlink_info_version_running_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID,
+ buf);
+ }
+}
+
+static int efx_devlink_info_running_versions(struct efx_nic *efx,
+ struct devlink_info_req *req)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_VERSION_V5_OUT_LEN);
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_VERSION_EXT_IN_LEN);
+ char buf[EFX_MAX_VERSION_INFO_LEN];
+ union {
+ const __le32 *dwords;
+ const __le16 *words;
+ const char *str;
+ } ver;
+ size_t outlength;
+ unsigned int flags;
+ int rc;
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_VERSION, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlength);
+ if (rc || outlength < MC_CMD_GET_VERSION_OUT_LEN) {
+ netif_err(efx, drv, efx->net_dev,
+ "mcdi MC_CMD_GET_VERSION failed\n");
+ return rc;
+ }
+
+ /* Handle previous output */
+ if (outlength < MC_CMD_GET_VERSION_V2_OUT_LEN) {
+ ver.words = (__le16 *)MCDI_PTR(outbuf,
+ GET_VERSION_EXT_OUT_VERSION);
+ snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+ le16_to_cpu(ver.words[0]),
+ le16_to_cpu(ver.words[1]),
+ le16_to_cpu(ver.words[2]),
+ le16_to_cpu(ver.words[3]));
+
+ devlink_info_version_running_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_FW_MGMT,
+ buf);
+ return 0;
+ }
+
+ /* Handle V2 additions */
+ flags = MCDI_DWORD(outbuf, GET_VERSION_V2_OUT_FLAGS);
+ efx_devlink_info_running_v2(efx, req, flags, outbuf);
+
+ if (outlength < MC_CMD_GET_VERSION_V3_OUT_LEN)
+ return 0;
+
+ /* Handle V3 additions */
+ efx_devlink_info_running_v3(efx, req, flags, outbuf);
+
+ if (outlength < MC_CMD_GET_VERSION_V4_OUT_LEN)
+ return 0;
+
+ /* Handle V4 additions */
+ efx_devlink_info_running_v4(efx, req, flags, outbuf);
+
+ if (outlength < MC_CMD_GET_VERSION_V5_OUT_LEN)
+ return 0;
+
+ /* Handle V5 additions */
+ efx_devlink_info_running_v5(efx, req, flags, outbuf);
+
+ return 0;
+}
+
+#define EFX_MAX_SERIALNUM_LEN (ETH_ALEN * 2 + 1)
+
+static int efx_devlink_info_board_cfg(struct efx_nic *efx,
+ struct devlink_info_req *req)
+{
+ char sn[EFX_MAX_SERIALNUM_LEN];
+ u8 mac_address[ETH_ALEN];
+ int rc;
+
+ rc = efx_mcdi_get_board_cfg(efx, (u8 *)mac_address, NULL, NULL);
+ if (!rc) {
+ snprintf(sn, EFX_MAX_SERIALNUM_LEN, "%pm", mac_address);
+ devlink_info_serial_number_put(req, sn);
+ }
+ return rc;
+}
+
+static int efx_devlink_info_get(struct devlink *devlink,
+ struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ struct efx_devlink *devlink_private = devlink_priv(devlink);
+ struct efx_nic *efx = devlink_private->efx;
+ int rc;
+
+ /* Several different MCDI commands are used. We report first error
+ * through extack returning at that point. Specific error
+ * information via system messages.
+ */
+ rc = efx_devlink_info_board_cfg(efx, req);
+ if (rc) {
+ NL_SET_ERR_MSG_MOD(extack, "Getting board info failed");
+ return rc;
+ }
+ rc = efx_devlink_info_stored_versions(efx, req);
+ if (rc) {
+ NL_SET_ERR_MSG_MOD(extack, "Getting stored versions failed");
+ return rc;
+ }
+ rc = efx_devlink_info_running_versions(efx, req);
+ if (rc) {
+ NL_SET_ERR_MSG_MOD(extack, "Getting running versions failed");
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct devlink_ops sfc_devlink_ops = {
+ .info_get = efx_devlink_info_get,
+#ifdef CONFIG_SFC_SRIOV
+ .port_function_hw_addr_get = efx_devlink_port_addr_get,
+ .port_function_hw_addr_set = efx_devlink_port_addr_set,
+#endif
+};
+
+#ifdef CONFIG_SFC_SRIOV
+static struct devlink_port *ef100_set_devlink_port(struct efx_nic *efx, u32 idx)
+{
+ struct mae_mport_desc *mport;
+ u32 id;
+ int rc;
+
+ if (efx_mae_lookup_mport(efx, idx, &id)) {
+ /* This should not happen. */
+ if (idx == MAE_MPORT_DESC_VF_IDX_NULL)
+ pci_warn_once(efx->pci_dev, "No mport ID found for PF.\n");
+ else
+ pci_warn_once(efx->pci_dev, "No mport ID found for VF %u.\n",
+ idx);
+ return NULL;
+ }
+
+ mport = efx_mae_get_mport(efx, id);
+ if (!mport) {
+ /* This should not happen. */
+ if (idx == MAE_MPORT_DESC_VF_IDX_NULL)
+ pci_warn_once(efx->pci_dev, "No mport found for PF.\n");
+ else
+ pci_warn_once(efx->pci_dev, "No mport found for VF %u.\n",
+ idx);
+ return NULL;
+ }
+
+ rc = efx_devlink_add_port(efx, mport);
+ if (rc) {
+ if (idx == MAE_MPORT_DESC_VF_IDX_NULL)
+ pci_warn(efx->pci_dev,
+ "devlink port creation for PF failed.\n");
+ else
+ pci_warn(efx->pci_dev,
+ "devlink_port creation for VF %u failed.\n",
+ idx);
+ return NULL;
+ }
+
+ return &mport->dl_port;
+}
+
+void ef100_rep_set_devlink_port(struct efx_rep *efv)
+{
+ efv->dl_port = ef100_set_devlink_port(efv->parent, efv->idx);
+}
+
+void ef100_pf_set_devlink_port(struct efx_nic *efx)
+{
+ efx->dl_port = ef100_set_devlink_port(efx, MAE_MPORT_DESC_VF_IDX_NULL);
+}
+
+void ef100_rep_unset_devlink_port(struct efx_rep *efv)
+{
+ efx_devlink_del_port(efv->dl_port);
+}
+
+void ef100_pf_unset_devlink_port(struct efx_nic *efx)
+{
+ efx_devlink_del_port(efx->dl_port);
+}
+#endif
+
+void efx_fini_devlink_lock(struct efx_nic *efx)
+{
+ if (efx->devlink)
+ devl_lock(efx->devlink);
+}
+
+void efx_fini_devlink_and_unlock(struct efx_nic *efx)
+{
+ if (efx->devlink) {
+ devl_unregister(efx->devlink);
+ devl_unlock(efx->devlink);
+ devlink_free(efx->devlink);
+ efx->devlink = NULL;
+ }
+}
+
+int efx_probe_devlink_and_lock(struct efx_nic *efx)
+{
+ struct efx_devlink *devlink_private;
+
+ if (efx->type->is_vf)
+ return 0;
+
+ efx->devlink = devlink_alloc(&sfc_devlink_ops,
+ sizeof(struct efx_devlink),
+ &efx->pci_dev->dev);
+ if (!efx->devlink)
+ return -ENOMEM;
+
+ devl_lock(efx->devlink);
+ devlink_private = devlink_priv(efx->devlink);
+ devlink_private->efx = efx;
+
+ devl_register(efx->devlink);
+
+ return 0;
+}
+
+void efx_probe_devlink_unlock(struct efx_nic *efx)
+{
+ if (!efx->devlink)
+ return;
+
+ devl_unlock(efx->devlink);
+}
diff --git a/drivers/net/ethernet/sfc/efx_devlink.h b/drivers/net/ethernet/sfc/efx_devlink.h
new file mode 100644
index 000000000000..e5fd5e1dcc27
--- /dev/null
+++ b/drivers/net/ethernet/sfc/efx_devlink.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/****************************************************************************
+ * Driver for AMD network controllers and boards
+ * Copyright (C) 2023, Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef _EFX_DEVLINK_H
+#define _EFX_DEVLINK_H
+
+#include "net_driver.h"
+#include <net/devlink.h>
+
+/* Custom devlink-info version object names for details that do not map to the
+ * generic standardized names.
+ */
+#define EFX_DEVLINK_INFO_VERSION_FW_MGMT_SUC "fw.mgmt.suc"
+#define EFX_DEVLINK_INFO_VERSION_FW_MGMT_CMC "fw.mgmt.cmc"
+#define EFX_DEVLINK_INFO_VERSION_FPGA_REV "fpga.rev"
+#define EFX_DEVLINK_INFO_VERSION_DATAPATH_HW "fpga.app"
+#define EFX_DEVLINK_INFO_VERSION_DATAPATH_FW DEVLINK_INFO_VERSION_GENERIC_FW_APP
+#define EFX_DEVLINK_INFO_VERSION_SOC_BOOT "coproc.boot"
+#define EFX_DEVLINK_INFO_VERSION_SOC_UBOOT "coproc.uboot"
+#define EFX_DEVLINK_INFO_VERSION_SOC_MAIN "coproc.main"
+#define EFX_DEVLINK_INFO_VERSION_SOC_RECOVERY "coproc.recovery"
+#define EFX_DEVLINK_INFO_VERSION_FW_EXPROM "fw.exprom"
+#define EFX_DEVLINK_INFO_VERSION_FW_UEFI "fw.uefi"
+
+#define EFX_MAX_VERSION_INFO_LEN 64
+
+int efx_probe_devlink_and_lock(struct efx_nic *efx);
+void efx_probe_devlink_unlock(struct efx_nic *efx);
+void efx_fini_devlink_lock(struct efx_nic *efx);
+void efx_fini_devlink_and_unlock(struct efx_nic *efx);
+
+#ifdef CONFIG_SFC_SRIOV
+struct efx_rep;
+
+void ef100_pf_set_devlink_port(struct efx_nic *efx);
+void ef100_rep_set_devlink_port(struct efx_rep *efv);
+void ef100_pf_unset_devlink_port(struct efx_nic *efx);
+void ef100_rep_unset_devlink_port(struct efx_rep *efv);
+#endif
+#endif /* _EFX_DEVLINK_H */
diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c
index 583baf69981c..2d32abe5f478 100644
--- a/drivers/net/ethernet/sfc/mae.c
+++ b/drivers/net/ethernet/sfc/mae.c
@@ -9,8 +9,11 @@
* by the Free Software Foundation, incorporated herein by reference.
*/
+#include <linux/rhashtable.h>
+#include "ef100_nic.h"
#include "mae.h"
#include "mcdi.h"
+#include "mcdi_pcol.h"
#include "mcdi_pcol_mae.h"
int efx_mae_allocate_mport(struct efx_nic *efx, u32 *id, u32 *label)
@@ -94,7 +97,7 @@ void efx_mae_mport_mport(struct efx_nic *efx __always_unused, u32 mport_id, u32
}
/* id is really only 24 bits wide */
-int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id)
+int efx_mae_fw_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_LOOKUP_OUT_LEN);
MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_LOOKUP_IN_LEN);
@@ -485,11 +488,193 @@ int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt)
return 0;
}
+int efx_mae_lookup_mport(struct efx_nic *efx, u32 vf_idx, u32 *id)
+{
+ struct ef100_nic_data *nic_data = efx->nic_data;
+ struct efx_mae *mae = efx->mae;
+ struct rhashtable_iter walk;
+ struct mae_mport_desc *m;
+ int rc = -ENOENT;
+
+ rhashtable_walk_enter(&mae->mports_ht, &walk);
+ rhashtable_walk_start(&walk);
+ while ((m = rhashtable_walk_next(&walk)) != NULL) {
+ if (m->mport_type == MAE_MPORT_DESC_MPORT_TYPE_VNIC &&
+ m->interface_idx == nic_data->local_mae_intf &&
+ m->pf_idx == 0 &&
+ m->vf_idx == vf_idx) {
+ *id = m->mport_id;
+ rc = 0;
+ break;
+ }
+ }
+ rhashtable_walk_stop(&walk);
+ rhashtable_walk_exit(&walk);
+ return rc;
+}
+
static bool efx_mae_asl_id(u32 id)
{
return !!(id & BIT(31));
}
+/* mport handling */
+static const struct rhashtable_params efx_mae_mports_ht_params = {
+ .key_len = sizeof(u32),
+ .key_offset = offsetof(struct mae_mport_desc, mport_id),
+ .head_offset = offsetof(struct mae_mport_desc, linkage),
+};
+
+struct mae_mport_desc *efx_mae_get_mport(struct efx_nic *efx, u32 mport_id)
+{
+ return rhashtable_lookup_fast(&efx->mae->mports_ht, &mport_id,
+ efx_mae_mports_ht_params);
+}
+
+static int efx_mae_add_mport(struct efx_nic *efx, struct mae_mport_desc *desc)
+{
+ struct efx_mae *mae = efx->mae;
+ int rc;
+
+ rc = rhashtable_insert_fast(&mae->mports_ht, &desc->linkage,
+ efx_mae_mports_ht_params);
+
+ if (rc) {
+ pci_err(efx->pci_dev, "Failed to insert MPORT %08x, rc %d\n",
+ desc->mport_id, rc);
+ kfree(desc);
+ return rc;
+ }
+
+ return rc;
+}
+
+void efx_mae_remove_mport(void *desc, void *arg)
+{
+ struct mae_mport_desc *mport = desc;
+
+ synchronize_rcu();
+ kfree(mport);
+}
+
+static int efx_mae_process_mport(struct efx_nic *efx,
+ struct mae_mport_desc *desc)
+{
+ struct ef100_nic_data *nic_data = efx->nic_data;
+ struct mae_mport_desc *mport;
+
+ mport = efx_mae_get_mport(efx, desc->mport_id);
+ if (!IS_ERR_OR_NULL(mport)) {
+ netif_err(efx, drv, efx->net_dev,
+ "mport with id %u does exist!!!\n", desc->mport_id);
+ return -EEXIST;
+ }
+
+ if (nic_data->have_own_mport &&
+ desc->mport_id == nic_data->own_mport) {
+ WARN_ON(desc->mport_type != MAE_MPORT_DESC_MPORT_TYPE_VNIC);
+ WARN_ON(desc->vnic_client_type !=
+ MAE_MPORT_DESC_VNIC_CLIENT_TYPE_FUNCTION);
+ nic_data->local_mae_intf = desc->interface_idx;
+ nic_data->have_local_intf = true;
+ pci_dbg(efx->pci_dev, "MAE interface_idx is %u\n",
+ nic_data->local_mae_intf);
+ }
+
+ return efx_mae_add_mport(efx, desc);
+}
+
+#define MCDI_MPORT_JOURNAL_LEN \
+ ALIGN(MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_LENMAX_MCDI2, 4)
+
+int efx_mae_enumerate_mports(struct efx_nic *efx)
+{
+ efx_dword_t *outbuf = kzalloc(MCDI_MPORT_JOURNAL_LEN, GFP_KERNEL);
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_READ_JOURNAL_IN_LEN);
+ MCDI_DECLARE_STRUCT_PTR(desc);
+ size_t outlen, stride, count;
+ int rc = 0, i;
+
+ if (!outbuf)
+ return -ENOMEM;
+ do {
+ rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_READ_JOURNAL, inbuf,
+ sizeof(inbuf), outbuf,
+ MCDI_MPORT_JOURNAL_LEN, &outlen);
+ if (rc)
+ goto fail;
+ if (outlen < MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_MPORT_DESC_DATA_OFST) {
+ rc = -EIO;
+ goto fail;
+ }
+ count = MCDI_DWORD(outbuf, MAE_MPORT_READ_JOURNAL_OUT_MPORT_DESC_COUNT);
+ if (!count)
+ continue; /* not break; we want to look at MORE flag */
+ stride = MCDI_DWORD(outbuf, MAE_MPORT_READ_JOURNAL_OUT_SIZEOF_MPORT_DESC);
+ if (stride < MAE_MPORT_DESC_LEN) {
+ rc = -EIO;
+ goto fail;
+ }
+ if (outlen < MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_LEN(count * stride)) {
+ rc = -EIO;
+ goto fail;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct mae_mport_desc *d;
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ desc = (efx_dword_t *)
+ _MCDI_PTR(outbuf, MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_MPORT_DESC_DATA_OFST +
+ i * stride);
+ d->mport_id = MCDI_STRUCT_DWORD(desc, MAE_MPORT_DESC_MPORT_ID);
+ d->flags = MCDI_STRUCT_DWORD(desc, MAE_MPORT_DESC_FLAGS);
+ d->caller_flags = MCDI_STRUCT_DWORD(desc,
+ MAE_MPORT_DESC_CALLER_FLAGS);
+ d->mport_type = MCDI_STRUCT_DWORD(desc,
+ MAE_MPORT_DESC_MPORT_TYPE);
+ switch (d->mport_type) {
+ case MAE_MPORT_DESC_MPORT_TYPE_NET_PORT:
+ d->port_idx = MCDI_STRUCT_DWORD(desc,
+ MAE_MPORT_DESC_NET_PORT_IDX);
+ break;
+ case MAE_MPORT_DESC_MPORT_TYPE_ALIAS:
+ d->alias_mport_id = MCDI_STRUCT_DWORD(desc,
+ MAE_MPORT_DESC_ALIAS_DELIVER_MPORT_ID);
+ break;
+ case MAE_MPORT_DESC_MPORT_TYPE_VNIC:
+ d->vnic_client_type = MCDI_STRUCT_DWORD(desc,
+ MAE_MPORT_DESC_VNIC_CLIENT_TYPE);
+ d->interface_idx = MCDI_STRUCT_DWORD(desc,
+ MAE_MPORT_DESC_VNIC_FUNCTION_INTERFACE);
+ d->pf_idx = MCDI_STRUCT_WORD(desc,
+ MAE_MPORT_DESC_VNIC_FUNCTION_PF_IDX);
+ d->vf_idx = MCDI_STRUCT_WORD(desc,
+ MAE_MPORT_DESC_VNIC_FUNCTION_VF_IDX);
+ break;
+ default:
+ /* Unknown mport_type, just accept it */
+ break;
+ }
+ rc = efx_mae_process_mport(efx, d);
+ /* Any failure will be due to memory allocation faiure,
+ * so there is no point to try subsequent entries.
+ */
+ if (rc)
+ goto fail;
+ }
+ } while (MCDI_FIELD(outbuf, MAE_MPORT_READ_JOURNAL_OUT, MORE) &&
+ !WARN_ON(!count));
+fail:
+ kfree(outbuf);
+ return rc;
+}
+
int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
@@ -805,3 +990,34 @@ int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
return -EIO;
return 0;
}
+
+int efx_init_mae(struct efx_nic *efx)
+{
+ struct ef100_nic_data *nic_data = efx->nic_data;
+ struct efx_mae *mae;
+ int rc;
+
+ if (!nic_data->have_mport)
+ return -EINVAL;
+
+ mae = kmalloc(sizeof(*mae), GFP_KERNEL);
+ if (!mae)
+ return -ENOMEM;
+
+ rc = rhashtable_init(&mae->mports_ht, &efx_mae_mports_ht_params);
+ if (rc < 0) {
+ kfree(mae);
+ return rc;
+ }
+ efx->mae = mae;
+ mae->efx = efx;
+ return 0;
+}
+
+void efx_fini_mae(struct efx_nic *efx)
+{
+ struct efx_mae *mae = efx->mae;
+
+ kfree(mae);
+ efx->mae = NULL;
+}
diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h
index 72343e90e222..bec293a06733 100644
--- a/drivers/net/ethernet/sfc/mae.h
+++ b/drivers/net/ethernet/sfc/mae.h
@@ -13,6 +13,7 @@
#define EF100_MAE_H
/* MCDI interface for the ef100 Match-Action Engine */
+#include <net/devlink.h>
#include "net_driver.h"
#include "tc.h"
#include "mcdi_pcol.h" /* needed for various MC_CMD_MAE_*_NULL defines */
@@ -27,6 +28,40 @@ void efx_mae_mport_mport(struct efx_nic *efx, u32 mport_id, u32 *out);
int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id);
+struct mae_mport_desc {
+ u32 mport_id;
+ u32 flags;
+ u32 caller_flags; /* enum mae_mport_desc_caller_flags */
+ u32 mport_type; /* MAE_MPORT_DESC_MPORT_TYPE_* */
+ union {
+ u32 port_idx; /* for mport_type == NET_PORT */
+ u32 alias_mport_id; /* for mport_type == ALIAS */
+ struct { /* for mport_type == VNIC */
+ u32 vnic_client_type; /* MAE_MPORT_DESC_VNIC_CLIENT_TYPE_* */
+ u32 interface_idx;
+ u16 pf_idx;
+ u16 vf_idx;
+ };
+ };
+ struct rhash_head linkage;
+ struct devlink_port dl_port;
+};
+
+int efx_mae_enumerate_mports(struct efx_nic *efx);
+struct mae_mport_desc *efx_mae_get_mport(struct efx_nic *efx, u32 mport_id);
+void efx_mae_put_mport(struct efx_nic *efx, struct mae_mport_desc *desc);
+
+/**
+ * struct efx_mae - MAE information
+ *
+ * @efx: The associated NIC
+ * @mports_ht: m-port descriptions from MC_CMD_MAE_MPORT_READ_JOURNAL
+ */
+struct efx_mae {
+ struct efx_nic *efx;
+ struct rhashtable mports_ht;
+};
+
int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue);
int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue);
void efx_mae_counters_grant_credits(struct work_struct *work);
@@ -60,4 +95,9 @@ int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
u32 prio, u32 acts_id, u32 *id);
int efx_mae_delete_rule(struct efx_nic *efx, u32 id);
+int efx_init_mae(struct efx_nic *efx);
+void efx_fini_mae(struct efx_nic *efx);
+void efx_mae_remove_mport(void *desc, void *arg);
+int efx_mae_fw_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id);
+int efx_mae_lookup_mport(struct efx_nic *efx, u32 vf, u32 *id);
#endif /* EF100_MAE_H */
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index af338208eae9..a7f2c31071e8 100644
--- a/drivers/net/ethernet/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -2175,6 +2175,78 @@ int efx_mcdi_get_privilege_mask(struct efx_nic *efx, u32 *mask)
return 0;
}
+int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
+ u32 *subtype, u16 version[4], char *desc,
+ size_t descsize)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_METADATA_IN_LEN);
+ efx_dword_t *outbuf;
+ size_t outlen;
+ u32 flags;
+ int rc;
+
+ outbuf = kzalloc(MC_CMD_NVRAM_METADATA_OUT_LENMAX_MCDI2, GFP_KERNEL);
+ if (!outbuf)
+ return -ENOMEM;
+
+ MCDI_SET_DWORD(inbuf, NVRAM_METADATA_IN_TYPE, type);
+
+ rc = efx_mcdi_rpc_quiet(efx, MC_CMD_NVRAM_METADATA, inbuf,
+ sizeof(inbuf), outbuf,
+ MC_CMD_NVRAM_METADATA_OUT_LENMAX_MCDI2,
+ &outlen);
+ if (rc)
+ goto out_free;
+ if (outlen < MC_CMD_NVRAM_METADATA_OUT_LENMIN) {
+ rc = -EIO;
+ goto out_free;
+ }
+
+ flags = MCDI_DWORD(outbuf, NVRAM_METADATA_OUT_FLAGS);
+
+ if (desc && descsize > 0) {
+ if (flags & BIT(MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_VALID_LBN)) {
+ if (descsize <=
+ MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_NUM(outlen)) {
+ rc = -E2BIG;
+ goto out_free;
+ }
+
+ strncpy(desc,
+ MCDI_PTR(outbuf, NVRAM_METADATA_OUT_DESCRIPTION),
+ MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_NUM(outlen));
+ desc[MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_NUM(outlen)] = '\0';
+ } else {
+ desc[0] = '\0';
+ }
+ }
+
+ if (subtype) {
+ if (flags & BIT(MC_CMD_NVRAM_METADATA_OUT_SUBTYPE_VALID_LBN))
+ *subtype = MCDI_DWORD(outbuf, NVRAM_METADATA_OUT_SUBTYPE);
+ else
+ *subtype = 0;
+ }
+
+ if (version) {
+ if (flags & BIT(MC_CMD_NVRAM_METADATA_OUT_VERSION_VALID_LBN)) {
+ version[0] = MCDI_WORD(outbuf, NVRAM_METADATA_OUT_VERSION_W);
+ version[1] = MCDI_WORD(outbuf, NVRAM_METADATA_OUT_VERSION_X);
+ version[2] = MCDI_WORD(outbuf, NVRAM_METADATA_OUT_VERSION_Y);
+ version[3] = MCDI_WORD(outbuf, NVRAM_METADATA_OUT_VERSION_Z);
+ } else {
+ version[0] = 0;
+ version[1] = 0;
+ version[2] = 0;
+ version[3] = 0;
+ }
+ }
+
+out_free:
+ kfree(outbuf);
+ return rc;
+}
+
#ifdef CONFIG_SFC_MTD
#define EFX_MCDI_NVRAM_LEN_MAX 128
diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h
index 7e35fec9da35..b139b76febff 100644
--- a/drivers/net/ethernet/sfc/mcdi.h
+++ b/drivers/net/ethernet/sfc/mcdi.h
@@ -229,6 +229,9 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
#define MCDI_WORD(_buf, _field) \
((u16)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \
le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field)))
+#define MCDI_STRUCT_WORD(_buf, _field) \
+ ((void)BUILD_BUG_ON_ZERO(_field ## _LEN != 2), \
+ le16_to_cpu(*(__force const __le16 *)MCDI_STRUCT_PTR(_buf, _field)))
/* Write a 16-bit field defined in the protocol as being big-endian. */
#define MCDI_STRUCT_SET_WORD_BE(_buf, _field, _value) do { \
BUILD_BUG_ON(_field ## _LEN != 2); \
@@ -241,6 +244,8 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
EFX_POPULATE_DWORD_1(*_MCDI_STRUCT_DWORD(_buf, _field), EFX_DWORD_0, _value)
#define MCDI_DWORD(_buf, _field) \
EFX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0)
+#define MCDI_STRUCT_DWORD(_buf, _field) \
+ EFX_DWORD_FIELD(*_MCDI_STRUCT_DWORD(_buf, _field), EFX_DWORD_0)
/* Write a 32-bit field defined in the protocol as being big-endian. */
#define MCDI_STRUCT_SET_DWORD_BE(_buf, _field, _value) do { \
BUILD_BUG_ON(_field ## _LEN != 4); \
@@ -378,6 +383,9 @@ int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
size_t *size_out, size_t *erase_size_out,
bool *protected_out);
int efx_new_mcdi_nvram_test_all(struct efx_nic *efx);
+int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
+ u32 *subtype, u16 version[4], char *desc,
+ size_t descsize);
int efx_mcdi_nvram_test_all(struct efx_nic *efx);
int efx_mcdi_handle_assertion(struct efx_nic *efx);
int efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index 3b49e216768b..fcd51d3992fa 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -845,6 +845,8 @@ enum efx_xdp_tx_queues_mode {
EFX_XDP_TX_QUEUES_BORROWED /* queues borrowed from net stack */
};
+struct efx_mae;
+
/**
* struct efx_nic - an Efx NIC
* @name: Device name (net device name or bus id before net device registered)
@@ -881,6 +883,7 @@ enum efx_xdp_tx_queues_mode {
* @msi_context: Context for each MSI
* @extra_channel_types: Types of extra (non-traffic) channels that
* should be allocated for this NIC
+ * @mae: Details of the Match Action Engine
* @xdp_tx_queue_count: Number of entries in %xdp_tx_queues.
* @xdp_tx_queues: Array of pointers to tx queues used for XDP transmit.
* @xdp_txq_queues_mode: XDP TX queues sharing strategy.
@@ -994,6 +997,8 @@ enum efx_xdp_tx_queues_mode {
* xdp_rxq_info structures?
* @netdev_notifier: Netdevice notifier.
* @tc: state for TC offload (EF100).
+ * @devlink: reference to devlink structure owned by this device
+ * @dl_port: devlink port associated with the PF
* @mem_bar: The BAR that is mapped into membase.
* @reg_base: Offset from the start of the bar to the function control window.
* @monitor_work: Hardware monitor workitem
@@ -1043,6 +1048,7 @@ struct efx_nic {
struct efx_msi_context msi_context[EFX_MAX_CHANNELS];
const struct efx_channel_type *
extra_channel_type[EFX_MAX_EXTRA_CHANNELS];
+ struct efx_mae *mae;
unsigned int xdp_tx_queue_count;
struct efx_tx_queue **xdp_tx_queues;
@@ -1179,6 +1185,8 @@ struct efx_nic {
struct notifier_block netdev_notifier;
struct efx_tc_state *tc;
+ struct devlink *devlink;
+ struct devlink_port *dl_port;
unsigned int mem_bar;
u32 reg_base;
diff --git a/drivers/net/ethernet/sfc/siena/efx.c b/drivers/net/ethernet/sfc/siena/efx.c
index 60e5b7c8ccf9..ef52ec71d197 100644
--- a/drivers/net/ethernet/sfc/siena/efx.c
+++ b/drivers/net/ethernet/sfc/siena/efx.c
@@ -1007,6 +1007,10 @@ static int efx_pci_probe_post_io(struct efx_nic *efx)
net_dev->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER;
net_dev->features |= efx->fixed_features;
+ net_dev->xdp_features = NETDEV_XDP_ACT_BASIC |
+ NETDEV_XDP_ACT_REDIRECT |
+ NETDEV_XDP_ACT_NDO_XMIT;
+
rc = efx_register_netdev(efx);
if (!rc)
return 0;