Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_BNGE) += bng_en.o
bng_en-y := bnge_core.o \
bnge_devlink.o \
bnge_hwrm.o \
bnge_hwrm_lib.o \
bnge_rmem.o \
bnge_resc.o \
bnge_netdev.o \
bnge_ethtool.o \
bnge_auxr.o \
bnge_txrx.o \
bnge_link.o
@@ -0,0 +1,266 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_H_
#define _BNGE_H_
#define DRV_NAME "bng_en"
#define DRV_SUMMARY "Broadcom ThorUltra NIC Ethernet Driver"
#include <linux/etherdevice.h>
#include <linux/bnge/hsi.h>
#include "bnge_rmem.h"
#include "bnge_resc.h"
#include "bnge_auxr.h"
#define DRV_VER_MAJ 1
#define DRV_VER_MIN 15
#define DRV_VER_UPD 1
extern char bnge_driver_name[];
enum board_idx {
BCM57708,
};
struct bnge_auxr_priv {
struct auxiliary_device aux_dev;
struct bnge_auxr_dev *auxr_dev;
int id;
};
struct bnge_pf_info {
u16 fw_fid;
u16 port_id;
u8 mac_addr[ETH_ALEN];
};
#define INVALID_HW_RING_ID ((u16)-1)
#define INVALID_HW_RING_ID_32BIT (U32_MAX)
enum {
BNGE_FW_CAP_SHORT_CMD = BIT_ULL(0),
BNGE_FW_CAP_LLDP_AGENT = BIT_ULL(1),
BNGE_FW_CAP_DCBX_AGENT = BIT_ULL(2),
BNGE_FW_CAP_IF_CHANGE = BIT_ULL(3),
BNGE_FW_CAP_KONG_MB_CHNL = BIT_ULL(4),
BNGE_FW_CAP_ERROR_RECOVERY = BIT_ULL(5),
BNGE_FW_CAP_PKG_VER = BIT_ULL(6),
BNGE_FW_CAP_CFA_ADV_FLOW = BIT_ULL(7),
BNGE_FW_CAP_CFA_RFS_RING_TBL_IDX_V2 = BIT_ULL(8),
BNGE_FW_CAP_PCIE_STATS_SUPPORTED = BIT_ULL(9),
BNGE_FW_CAP_EXT_STATS_SUPPORTED = BIT_ULL(10),
BNGE_FW_CAP_ERR_RECOVER_RELOAD = BIT_ULL(11),
BNGE_FW_CAP_HOT_RESET = BIT_ULL(12),
BNGE_FW_CAP_RX_ALL_PKT_TS = BIT_ULL(13),
BNGE_FW_CAP_VLAN_RX_STRIP = BIT_ULL(14),
BNGE_FW_CAP_VLAN_TX_INSERT = BIT_ULL(15),
BNGE_FW_CAP_EXT_HW_STATS_SUPPORTED = BIT_ULL(16),
BNGE_FW_CAP_LIVEPATCH = BIT_ULL(17),
BNGE_FW_CAP_HOT_RESET_IF = BIT_ULL(18),
BNGE_FW_CAP_RING_MONITOR = BIT_ULL(19),
BNGE_FW_CAP_DBG_QCAPS = BIT_ULL(20),
BNGE_FW_CAP_THRESHOLD_TEMP_SUPPORTED = BIT_ULL(21),
BNGE_FW_CAP_DFLT_VLAN_TPID_PCP = BIT_ULL(22),
BNGE_FW_CAP_VNIC_TUNNEL_TPA = BIT_ULL(23),
BNGE_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO = BIT_ULL(24),
BNGE_FW_CAP_CFA_RFS_RING_TBL_IDX_V3 = BIT_ULL(25),
BNGE_FW_CAP_VNIC_RE_FLUSH = BIT_ULL(26),
};
enum {
BNGE_EN_ROCE_V1 = BIT_ULL(0),
BNGE_EN_ROCE_V2 = BIT_ULL(1),
BNGE_EN_STRIP_VLAN = BIT_ULL(2),
BNGE_EN_SHARED_CHNL = BIT_ULL(3),
BNGE_EN_UDP_GSO_SUPP = BIT_ULL(4),
};
#define BNGE_EN_ROCE (BNGE_EN_ROCE_V1 | BNGE_EN_ROCE_V2)
enum {
BNGE_RSS_CAP_RSS_HASH_TYPE_DELTA = BIT(0),
BNGE_RSS_CAP_UDP_RSS_CAP = BIT(1),
BNGE_RSS_CAP_NEW_RSS_CAP = BIT(2),
BNGE_RSS_CAP_RSS_TCAM = BIT(3),
BNGE_RSS_CAP_AH_V4_RSS_CAP = BIT(4),
BNGE_RSS_CAP_AH_V6_RSS_CAP = BIT(5),
BNGE_RSS_CAP_ESP_V4_RSS_CAP = BIT(6),
BNGE_RSS_CAP_ESP_V6_RSS_CAP = BIT(7),
};
#define BNGE_MAX_QUEUE 8
struct bnge_queue_info {
u8 queue_id;
u8 queue_profile;
};
#define BNGE_PHY_FLAGS2_SHIFT 8
#define BNGE_PHY_FL_NO_FCS PORT_PHY_QCAPS_RESP_FLAGS_NO_FCS
#define BNGE_PHY_FL_NO_PAUSE \
(PORT_PHY_QCAPS_RESP_FLAGS2_PAUSE_UNSUPPORTED << 8)
struct bnge_dev {
struct device *dev;
struct pci_dev *pdev;
struct net_device *netdev;
u64 dsn;
#define BNGE_VPD_FLD_LEN 32
char board_partno[BNGE_VPD_FLD_LEN];
char board_serialno[BNGE_VPD_FLD_LEN];
void __iomem *bar0;
void __iomem *bar1;
u16 chip_num;
u8 chip_rev;
#if BITS_PER_LONG == 32
/* ensure atomic 64-bit doorbell writes on 32-bit systems. */
spinlock_t db_lock;
#endif
int db_offset; /* db_offset within db_size */
int db_size;
/* HWRM members */
u16 hwrm_cmd_seq;
u16 hwrm_cmd_kong_seq;
struct dma_pool *hwrm_dma_pool;
struct hlist_head hwrm_pending_list;
u16 hwrm_max_req_len;
u16 hwrm_max_ext_req_len;
unsigned int hwrm_cmd_timeout;
unsigned int hwrm_cmd_max_timeout;
struct mutex hwrm_cmd_lock; /* serialize hwrm messages */
struct hwrm_ver_get_output ver_resp;
#define FW_VER_STR_LEN 32
char fw_ver_str[FW_VER_STR_LEN];
char hwrm_ver_supp[FW_VER_STR_LEN];
char nvm_cfg_ver[FW_VER_STR_LEN];
u64 fw_ver_code;
#define BNGE_FW_VER_CODE(maj, min, bld, rsv) \
((u64)(maj) << 48 | (u64)(min) << 32 | (u64)(bld) << 16 | (rsv))
struct bnge_pf_info pf;
unsigned long state;
#define BNGE_STATE_DRV_REGISTERED 0
#define BNGE_STATE_OPEN 1
u64 fw_cap;
/* Backing stores */
struct bnge_ctx_mem_info *ctx;
u64 flags;
struct bnge_hw_resc hw_resc;
u16 tso_max_segs;
int max_fltr;
#define BNGE_L2_FLTR_MAX_FLTR 1024
u32 *rss_indir_tbl;
#define BNGE_RSS_TABLE_ENTRIES 64
#define BNGE_RSS_TABLE_SIZE (BNGE_RSS_TABLE_ENTRIES * 4)
#define BNGE_RSS_TABLE_MAX_TBL 8
#define BNGE_MAX_RSS_TABLE_SIZE \
(BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)
#define BNGE_MAX_RSS_TABLE_ENTRIES \
(BNGE_RSS_TABLE_ENTRIES * BNGE_RSS_TABLE_MAX_TBL)
u16 rss_indir_tbl_entries;
u32 rss_cap;
u32 rss_hash_cfg;
u16 rx_nr_rings;
u16 tx_nr_rings;
u16 tx_nr_rings_per_tc;
/* Number of NQs */
u16 nq_nr_rings;
/* Aux device resources */
u16 aux_num_msix;
u16 aux_num_stat_ctxs;
u16 max_mtu;
#define BNGE_MAX_MTU 9500
u16 hw_ring_stats_size;
#define BNGE_NUM_RX_RING_STATS 8
#define BNGE_NUM_TX_RING_STATS 8
#define BNGE_NUM_TPA_RING_STATS 6
#define BNGE_RING_STATS_SIZE \
((BNGE_NUM_RX_RING_STATS + BNGE_NUM_TX_RING_STATS + \
BNGE_NUM_TPA_RING_STATS) * 8)
u16 max_tpa_v2;
#define BNGE_SUPPORTS_TPA(bd) ((bd)->max_tpa_v2)
u8 num_tc;
u8 max_tc;
u8 max_lltc; /* lossless TCs */
struct bnge_queue_info q_info[BNGE_MAX_QUEUE];
u8 tc_to_qidx[BNGE_MAX_QUEUE];
u8 q_ids[BNGE_MAX_QUEUE];
u8 max_q;
u8 port_count;
struct bnge_irq *irq_tbl;
u16 irqs_acquired;
struct bnge_auxr_priv *aux_priv;
struct bnge_auxr_dev *auxr_dev;
struct bnge_link_info link_info;
/* Copied from flags and flags2 in hwrm_port_phy_qcaps_output */
u32 phy_flags;
};
static inline bool bnge_is_roce_en(struct bnge_dev *bd)
{
return bd->flags & BNGE_EN_ROCE;
}
static inline bool bnge_is_agg_reqd(struct bnge_dev *bd)
{
if (bd->netdev) {
struct bnge_net *bn = netdev_priv(bd->netdev);
if (bn->priv_flags & BNGE_NET_EN_TPA ||
bn->priv_flags & BNGE_NET_EN_JUMBO)
return true;
else
return false;
}
return true;
}
static inline void bnge_writeq(struct bnge_dev *bd, u64 val,
void __iomem *addr)
{
#if BITS_PER_LONG == 32
spin_lock(&bd->db_lock);
lo_hi_writeq(val, addr);
spin_unlock(&bd->db_lock);
#else
writeq(val, addr);
#endif
}
/* For TX and RX ring doorbells */
static inline void bnge_db_write(struct bnge_dev *bd, struct bnge_db_info *db,
u32 idx)
{
bnge_writeq(bd, db->db_key64 | DB_RING_IDX(db, idx),
db->doorbell);
}
bool bnge_aux_registered(struct bnge_dev *bd);
u16 bnge_aux_get_msix(struct bnge_dev *bd);
#endif /* _BNGE_H_ */
@@ -0,0 +1,262 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Broadcom.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <asm/byteorder.h>
#include <linux/bitmap.h>
#include <linux/auxiliary_bus.h>
#include <linux/bnge/hsi.h>
#include "bnge.h"
#include "bnge_hwrm.h"
#include "bnge_auxr.h"
static DEFINE_IDA(bnge_aux_dev_ids);
static void bnge_fill_msix_vecs(struct bnge_dev *bd,
struct bnge_msix_info *info)
{
struct bnge_auxr_dev *auxr_dev = bd->auxr_dev;
int num_msix, i;
if (!auxr_dev->auxr_info->msix_requested) {
dev_warn(bd->dev, "Requested MSI-X vectors not allocated\n");
return;
}
num_msix = auxr_dev->auxr_info->msix_requested;
for (i = 0; i < num_msix; i++) {
info[i].vector = bd->irq_tbl[i].vector;
info[i].db_offset = bd->db_offset;
info[i].ring_idx = i;
}
}
int bnge_register_dev(struct bnge_auxr_dev *auxr_dev,
void *handle)
{
struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev);
struct bnge_auxr_info *auxr_info;
int rc = 0;
netdev_lock(bd->netdev);
mutex_lock(&auxr_dev->auxr_dev_lock);
if (!bd->irq_tbl) {
rc = -ENODEV;
goto exit;
}
if (!bnge_aux_has_enough_resources(bd)) {
rc = -ENOMEM;
goto exit;
}
auxr_info = auxr_dev->auxr_info;
auxr_info->handle = handle;
auxr_info->msix_requested = bd->aux_num_msix;
bnge_fill_msix_vecs(bd, bd->auxr_dev->msix_info);
auxr_dev->flags |= BNGE_ARDEV_MSIX_ALLOC;
exit:
mutex_unlock(&auxr_dev->auxr_dev_lock);
netdev_unlock(bd->netdev);
return rc;
}
EXPORT_SYMBOL(bnge_register_dev);
void bnge_unregister_dev(struct bnge_auxr_dev *auxr_dev)
{
struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev);
struct bnge_auxr_info *auxr_info;
auxr_info = auxr_dev->auxr_info;
netdev_lock(bd->netdev);
mutex_lock(&auxr_dev->auxr_dev_lock);
if (auxr_info->msix_requested)
auxr_dev->flags &= ~BNGE_ARDEV_MSIX_ALLOC;
auxr_info->msix_requested = 0;
mutex_unlock(&auxr_dev->auxr_dev_lock);
netdev_unlock(bd->netdev);
}
EXPORT_SYMBOL(bnge_unregister_dev);
int bnge_send_msg(struct bnge_auxr_dev *auxr_dev, struct bnge_fw_msg *fw_msg)
{
struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev);
struct output *resp;
struct input *req;
u32 resp_len;
int rc;
rc = bnge_hwrm_req_init(bd, req, 0 /* don't care */);
if (rc)
return rc;
rc = bnge_hwrm_req_replace(bd, req, fw_msg->msg, fw_msg->msg_len);
if (rc)
goto drop_req;
bnge_hwrm_req_timeout(bd, req, fw_msg->timeout);
resp = bnge_hwrm_req_hold(bd, req);
rc = bnge_hwrm_req_send(bd, req);
resp_len = le16_to_cpu(resp->resp_len);
if (resp_len) {
if (fw_msg->resp_max_len < resp_len)
resp_len = fw_msg->resp_max_len;
memcpy(fw_msg->resp, resp, resp_len);
}
drop_req:
bnge_hwrm_req_drop(bd, req);
return rc;
}
EXPORT_SYMBOL(bnge_send_msg);
void bnge_rdma_aux_device_uninit(struct bnge_dev *bd)
{
struct bnge_auxr_priv *aux_priv;
struct auxiliary_device *adev;
/* Skip if no auxiliary device init was done. */
if (!bd->aux_priv)
return;
aux_priv = bd->aux_priv;
adev = &aux_priv->aux_dev;
auxiliary_device_uninit(adev);
}
static void bnge_aux_dev_release(struct device *dev)
{
struct bnge_auxr_priv *aux_priv =
container_of(dev, struct bnge_auxr_priv, aux_dev.dev);
struct bnge_auxr_dev *auxr_dev = aux_priv->auxr_dev;
struct bnge_dev *bd = pci_get_drvdata(to_pci_dev(dev->parent));
ida_free(&bnge_aux_dev_ids, aux_priv->id);
if (auxr_dev) {
kfree(auxr_dev->auxr_info);
kfree(auxr_dev);
}
bd->auxr_dev = NULL;
kfree(aux_priv);
bd->aux_priv = NULL;
}
void bnge_rdma_aux_device_del(struct bnge_dev *bd)
{
if (!bd->auxr_dev)
return;
auxiliary_device_delete(&bd->aux_priv->aux_dev);
}
static void bnge_set_auxr_dev_info(struct bnge_auxr_dev *auxr_dev,
struct bnge_dev *bd)
{
auxr_dev->pdev = bd->pdev;
auxr_dev->l2_db_size = bd->db_size;
auxr_dev->l2_db_size_nc = bd->db_size;
auxr_dev->l2_db_offset = bd->db_offset;
mutex_init(&auxr_dev->auxr_dev_lock);
if (bd->flags & BNGE_EN_ROCE_V1)
auxr_dev->flags |= BNGE_ARDEV_ROCEV1_SUPP;
if (bd->flags & BNGE_EN_ROCE_V2)
auxr_dev->flags |= BNGE_ARDEV_ROCEV2_SUPP;
auxr_dev->chip_num = bd->chip_num;
auxr_dev->hw_ring_stats_size = bd->hw_ring_stats_size;
auxr_dev->pf_port_id = bd->pf.port_id;
auxr_dev->en_state = bd->state;
auxr_dev->bar0 = bd->bar0;
}
void bnge_rdma_aux_device_add(struct bnge_dev *bd)
{
struct auxiliary_device *aux_dev;
int rc;
if (!bd->auxr_dev)
return;
aux_dev = &bd->aux_priv->aux_dev;
rc = auxiliary_device_add(aux_dev);
if (rc) {
dev_warn(bd->dev, "Failed to add auxiliary device for ROCE\n");
auxiliary_device_uninit(aux_dev);
bd->flags &= ~BNGE_EN_ROCE;
return;
}
bd->auxr_dev->net = bd->netdev;
}
void bnge_rdma_aux_device_init(struct bnge_dev *bd)
{
struct auxiliary_device *aux_dev;
struct bnge_auxr_info *auxr_info;
struct bnge_auxr_priv *aux_priv;
struct bnge_auxr_dev *auxr_dev;
int rc;
if (!bnge_is_roce_en(bd))
return;
aux_priv = kzalloc_obj(*aux_priv);
if (!aux_priv)
goto exit;
aux_priv->id = ida_alloc(&bnge_aux_dev_ids, GFP_KERNEL);
if (aux_priv->id < 0) {
dev_warn(bd->dev, "ida alloc failed for aux device\n");
kfree(aux_priv);
goto exit;
}
aux_dev = &aux_priv->aux_dev;
aux_dev->id = aux_priv->id;
aux_dev->name = "rdma";
aux_dev->dev.parent = &bd->pdev->dev;
aux_dev->dev.release = bnge_aux_dev_release;
rc = auxiliary_device_init(aux_dev);
if (rc) {
ida_free(&bnge_aux_dev_ids, aux_priv->id);
kfree(aux_priv);
goto exit;
}
bd->aux_priv = aux_priv;
auxr_dev = kzalloc_obj(*auxr_dev);
if (!auxr_dev)
goto aux_dev_uninit;
aux_priv->auxr_dev = auxr_dev;
auxr_info = kzalloc_obj(*auxr_info);
if (!auxr_info)
goto aux_dev_uninit;
auxr_dev->auxr_info = auxr_info;
bd->auxr_dev = auxr_dev;
bnge_set_auxr_dev_info(auxr_dev, bd);
return;
aux_dev_uninit:
auxiliary_device_uninit(aux_dev);
exit:
bd->flags &= ~BNGE_EN_ROCE;
}
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_AUXR_H_
#define _BNGE_AUXR_H_
#include <linux/auxiliary_bus.h>
#define BNGE_MIN_ROCE_CP_RINGS 2
#define BNGE_MIN_ROCE_STAT_CTXS 1
#define BNGE_MAX_ROCE_MSIX 64
struct hwrm_async_event_cmpl;
struct bnge;
struct bnge_msix_info {
u32 vector;
u32 ring_idx;
u32 db_offset;
};
struct bnge_fw_msg {
void *msg;
int msg_len;
void *resp;
int resp_max_len;
int timeout;
};
struct bnge_auxr_info {
void *handle;
u16 msix_requested;
};
enum {
BNGE_ARDEV_ROCEV1_SUPP = BIT(0),
BNGE_ARDEV_ROCEV2_SUPP = BIT(1),
BNGE_ARDEV_MSIX_ALLOC = BIT(2),
};
#define BNGE_ARDEV_ROCE_SUPP (BNGE_ARDEV_ROCEV1_SUPP | \
BNGE_ARDEV_ROCEV2_SUPP)
struct bnge_auxr_dev {
struct net_device *net;
struct pci_dev *pdev;
void __iomem *bar0;
struct bnge_msix_info msix_info[BNGE_MAX_ROCE_MSIX];
u32 flags;
struct bnge_auxr_info *auxr_info;
/* Doorbell BAR size in bytes mapped by L2 driver. */
int l2_db_size;
/* Doorbell BAR size in bytes mapped as non-cacheable. */
int l2_db_size_nc;
/* Doorbell offset in bytes within l2_db_size_nc. */
int l2_db_offset;
u16 chip_num;
u16 hw_ring_stats_size;
u16 pf_port_id;
unsigned long en_state;
u16 auxr_num_msix_vec;
u16 auxr_num_ctxs;
/* serialize auxr operations */
struct mutex auxr_dev_lock;
};
void bnge_rdma_aux_device_uninit(struct bnge_dev *bdev);
void bnge_rdma_aux_device_del(struct bnge_dev *bdev);
void bnge_rdma_aux_device_add(struct bnge_dev *bdev);
void bnge_rdma_aux_device_init(struct bnge_dev *bdev);
int bnge_register_dev(struct bnge_auxr_dev *adev,
void *handle);
void bnge_unregister_dev(struct bnge_auxr_dev *adev);
int bnge_send_msg(struct bnge_auxr_dev *adev, struct bnge_fw_msg *fw_msg);
#endif /* _BNGE_AUXR_H_ */
@@ -0,0 +1,435 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Broadcom.
#include <linux/init.h>
#include <linux/crash_dump.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "bnge.h"
#include "bnge_devlink.h"
#include "bnge_hwrm.h"
#include "bnge_hwrm_lib.h"
#include "bnge_link.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRV_SUMMARY);
char bnge_driver_name[] = DRV_NAME;
static const struct {
char *name;
} board_info[] = {
[BCM57708] = { "Broadcom BCM57708 ThorUltra 50Gb/100Gb/200Gb/400Gb/800Gb Ethernet" },
};
static const struct pci_device_id bnge_pci_tbl[] = {
{ PCI_VDEVICE(BROADCOM, 0x1780), .driver_data = BCM57708 },
/* Required last entry */
{0, }
};
MODULE_DEVICE_TABLE(pci, bnge_pci_tbl);
static void bnge_print_device_info(struct pci_dev *pdev, enum board_idx idx)
{
struct device *dev = &pdev->dev;
dev_info(dev, "%s found at mem %lx\n", board_info[idx].name,
(long)pci_resource_start(pdev, 0));
pcie_print_link_status(pdev);
}
bool bnge_aux_registered(struct bnge_dev *bd)
{
struct bnge_auxr_dev *ba_dev = bd->auxr_dev;
if (ba_dev && ba_dev->auxr_info->msix_requested)
return true;
return false;
}
static void bnge_nvm_cfg_ver_get(struct bnge_dev *bd)
{
struct hwrm_nvm_get_dev_info_output nvm_info;
if (!bnge_hwrm_nvm_dev_info(bd, &nvm_info))
snprintf(bd->nvm_cfg_ver, FW_VER_STR_LEN, "%d.%d.%d",
nvm_info.nvm_cfg_ver_maj, nvm_info.nvm_cfg_ver_min,
nvm_info.nvm_cfg_ver_upd);
}
static int bnge_func_qcaps(struct bnge_dev *bd)
{
int rc;
rc = bnge_hwrm_func_qcaps(bd);
if (rc)
return rc;
rc = bnge_hwrm_queue_qportcfg(bd);
if (rc) {
dev_err(bd->dev, "query qportcfg failure rc: %d\n", rc);
return rc;
}
return 0;
}
static int bnge_func_qrcaps_qcfg(struct bnge_dev *bd)
{
int rc;
rc = bnge_hwrm_func_resc_qcaps(bd);
if (rc) {
dev_err(bd->dev, "query resc caps failure rc: %d\n", rc);
return rc;
}
rc = bnge_hwrm_func_qcfg(bd);
if (rc) {
dev_err(bd->dev, "query config failure rc: %d\n", rc);
return rc;
}
rc = bnge_hwrm_vnic_qcaps(bd);
if (rc) {
dev_err(bd->dev, "vnic caps failure rc: %d\n", rc);
return rc;
}
return 0;
}
static void bnge_fw_unregister_dev(struct bnge_dev *bd)
{
/* ctx mem free after unrgtr only */
bnge_hwrm_func_drv_unrgtr(bd);
bnge_free_ctx_mem(bd);
}
static void bnge_set_dflt_rss_hash_type(struct bnge_dev *bd)
{
bd->rss_hash_cfg = VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4 |
VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4 |
VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6 |
VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6 |
VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4 |
VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6;
}
static int bnge_fw_register_dev(struct bnge_dev *bd)
{
int rc;
bd->fw_cap = 0;
rc = bnge_hwrm_ver_get(bd);
if (rc) {
dev_err(bd->dev, "Get Version command failed rc: %d\n", rc);
return rc;
}
bnge_nvm_cfg_ver_get(bd);
rc = bnge_hwrm_func_reset(bd);
if (rc) {
dev_err(bd->dev, "Failed to reset function rc: %d\n", rc);
return rc;
}
bnge_hwrm_fw_set_time(bd);
/* Get the resources and configuration from firmware */
rc = bnge_func_qcaps(bd);
if (rc) {
dev_err(bd->dev, "Failed querying caps rc: %d\n", rc);
return rc;
}
rc = bnge_alloc_ctx_mem(bd);
if (rc) {
dev_err(bd->dev, "Failed to allocate ctx mem rc: %d\n", rc);
goto err_free_ctx_mem;
}
rc = bnge_hwrm_func_drv_rgtr(bd);
if (rc) {
dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
goto err_free_ctx_mem;
}
rc = bnge_func_qrcaps_qcfg(bd);
if (rc) {
dev_err(bd->dev, "Failed querying resources rc: %d\n", rc);
goto err_func_unrgtr;
}
bnge_set_dflt_rss_hash_type(bd);
return 0;
err_func_unrgtr:
bnge_hwrm_func_drv_unrgtr(bd);
err_free_ctx_mem:
bnge_free_ctx_mem(bd);
return rc;
}
static void bnge_pci_disable(struct pci_dev *pdev)
{
pci_release_regions(pdev);
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
}
static int bnge_pci_enable(struct pci_dev *pdev)
{
int rc;
rc = pci_enable_device(pdev);
if (rc) {
dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
return rc;
}
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
dev_err(&pdev->dev,
"Cannot find PCI device base address, aborting\n");
rc = -ENODEV;
goto err_pci_disable;
}
rc = pci_request_regions(pdev, bnge_driver_name);
if (rc) {
dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n");
goto err_pci_disable;
}
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
pci_set_master(pdev);
return 0;
err_pci_disable:
pci_disable_device(pdev);
return rc;
}
static void bnge_unmap_bars(struct pci_dev *pdev)
{
struct bnge_dev *bd = pci_get_drvdata(pdev);
if (bd->bar1) {
pci_iounmap(pdev, bd->bar1);
bd->bar1 = NULL;
}
if (bd->bar0) {
pci_iounmap(pdev, bd->bar0);
bd->bar0 = NULL;
}
}
static void bnge_set_max_func_irqs(struct bnge_dev *bd,
unsigned int max_irqs)
{
bd->hw_resc.max_irqs = max_irqs;
}
static int bnge_get_max_irq(struct pci_dev *pdev)
{
u16 ctrl;
pci_read_config_word(pdev, pdev->msix_cap + PCI_MSIX_FLAGS, &ctrl);
return (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
}
static int bnge_map_db_bar(struct bnge_dev *bd)
{
if (!bd->db_size)
return -ENODEV;
bd->bar1 = pci_iomap(bd->pdev, 2, bd->db_size);
if (!bd->bar1)
return -ENOMEM;
return 0;
}
static int bnge_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
unsigned int max_irqs;
struct bnge_dev *bd;
int rc;
if (pci_is_bridge(pdev))
return -ENODEV;
if (!pdev->msix_cap) {
dev_err(&pdev->dev, "MSIX capability missing, aborting\n");
return -ENODEV;
}
if (is_kdump_kernel()) {
pci_clear_master(pdev);
pcie_flr(pdev);
}
rc = bnge_pci_enable(pdev);
if (rc)
return rc;
bnge_print_device_info(pdev, ent->driver_data);
bd = bnge_devlink_alloc(pdev);
if (!bd) {
dev_err(&pdev->dev, "Devlink allocation failed\n");
rc = -ENOMEM;
goto err_pci_disable;
}
bd->bar0 = pci_ioremap_bar(pdev, 0);
if (!bd->bar0) {
dev_err(&pdev->dev, "Failed mapping BAR-0, aborting\n");
rc = -ENOMEM;
goto err_devl_free;
}
rc = bnge_init_hwrm_resources(bd);
if (rc)
goto err_bar_unmap;
rc = bnge_fw_register_dev(bd);
if (rc) {
dev_err(&pdev->dev, "Failed to register with firmware rc = %d\n", rc);
goto err_hwrm_cleanup;
}
bnge_devlink_register(bd);
max_irqs = bnge_get_max_irq(pdev);
bnge_set_max_func_irqs(bd, max_irqs);
bnge_aux_init_dflt_config(bd);
rc = bnge_net_init_dflt_config(bd);
if (rc) {
dev_err(&pdev->dev, "Error setting up default cfg to netdev rc = %d\n",
rc);
goto err_fw_reg;
}
rc = bnge_map_db_bar(bd);
if (rc) {
dev_err(&pdev->dev, "Failed mapping doorbell BAR rc = %d, aborting\n",
rc);
goto err_config_uninit;
}
#if BITS_PER_LONG == 32
spin_lock_init(&bd->db_lock);
#endif
bnge_rdma_aux_device_init(bd);
rc = bnge_alloc_irqs(bd);
if (rc) {
dev_err(&pdev->dev, "Error IRQ allocation rc = %d\n", rc);
goto err_uninit_auxr;
}
rc = bnge_netdev_alloc(bd, max_irqs);
if (rc)
goto err_free_irq;
bnge_rdma_aux_device_add(bd);
pci_save_state(pdev);
return 0;
err_free_irq:
bnge_free_irqs(bd);
err_uninit_auxr:
bnge_rdma_aux_device_uninit(bd);
err_config_uninit:
bnge_net_uninit_dflt_config(bd);
err_fw_reg:
bnge_devlink_unregister(bd);
bnge_fw_unregister_dev(bd);
err_hwrm_cleanup:
bnge_cleanup_hwrm_resources(bd);
err_bar_unmap:
bnge_unmap_bars(pdev);
err_devl_free:
bnge_devlink_free(bd);
err_pci_disable:
bnge_pci_disable(pdev);
return rc;
}
static void bnge_remove_one(struct pci_dev *pdev)
{
struct bnge_dev *bd = pci_get_drvdata(pdev);
bnge_rdma_aux_device_del(bd);
bnge_netdev_free(bd);
bnge_free_irqs(bd);
bnge_rdma_aux_device_uninit(bd);
bnge_net_uninit_dflt_config(bd);
bnge_devlink_unregister(bd);
bnge_fw_unregister_dev(bd);
bnge_cleanup_hwrm_resources(bd);
bnge_unmap_bars(pdev);
bnge_devlink_free(bd);
bnge_pci_disable(pdev);
}
static void bnge_shutdown(struct pci_dev *pdev)
{
pci_disable_device(pdev);
if (system_state == SYSTEM_POWER_OFF) {
pci_wake_from_d3(pdev, 0);
pci_set_power_state(pdev, PCI_D3hot);
}
}
static struct pci_driver bnge_driver = {
.name = bnge_driver_name,
.id_table = bnge_pci_tbl,
.probe = bnge_probe_one,
.remove = bnge_remove_one,
.shutdown = bnge_shutdown,
};
static int __init bnge_init_module(void)
{
return pci_register_driver(&bnge_driver);
}
module_init(bnge_init_module);
static void __exit bnge_exit_module(void)
{
pci_unregister_driver(&bnge_driver);
}
module_exit(bnge_exit_module);
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_DB_H_
#define _BNGE_DB_H_
/* 64-bit doorbell */
#define DBR_EPOCH_SFT 24
#define DBR_TOGGLE_SFT 25
#define DBR_XID_SFT 32
#define DBR_PATH_L2 (0x1ULL << 56)
#define DBR_VALID (0x1ULL << 58)
#define DBR_TYPE_SQ (0x0ULL << 60)
#define DBR_TYPE_SRQ (0x2ULL << 60)
#define DBR_TYPE_CQ (0x4ULL << 60)
#define DBR_TYPE_CQ_ARMALL (0x6ULL << 60)
#define DBR_TYPE_NQ (0xaULL << 60)
#define DBR_TYPE_NQ_ARM (0xbULL << 60)
#define DBR_TYPE_NQ_MASK (0xeULL << 60)
struct bnge_db_info {
void __iomem *doorbell;
u64 db_key64;
u32 db_ring_mask;
u32 db_epoch_mask;
u8 db_epoch_shift;
};
#define DB_EPOCH(db, idx) (((idx) & (db)->db_epoch_mask) << \
((db)->db_epoch_shift))
#define DB_RING_IDX(db, idx) (((idx) & (db)->db_ring_mask) | \
DB_EPOCH(db, idx))
#endif /* _BNGE_DB_H_ */
@@ -0,0 +1,306 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Broadcom.
#include <linux/unaligned.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <net/devlink.h>
#include "bnge.h"
#include "bnge_devlink.h"
#include "bnge_hwrm_lib.h"
static int bnge_dl_info_put(struct bnge_dev *bd, struct devlink_info_req *req,
enum bnge_dl_version_type type, const char *key,
char *buf)
{
if (!strlen(buf))
return 0;
if (!strcmp(key, DEVLINK_INFO_VERSION_GENERIC_FW_NCSI) ||
!strcmp(key, DEVLINK_INFO_VERSION_GENERIC_FW_ROCE))
return 0;
switch (type) {
case BNGE_VERSION_FIXED:
return devlink_info_version_fixed_put(req, key, buf);
case BNGE_VERSION_RUNNING:
return devlink_info_version_running_put(req, key, buf);
case BNGE_VERSION_STORED:
return devlink_info_version_stored_put(req, key, buf);
}
return 0;
}
static void bnge_vpd_read_info(struct bnge_dev *bd)
{
struct pci_dev *pdev = bd->pdev;
unsigned int vpd_size, kw_len;
int pos, size;
u8 *vpd_data;
vpd_data = pci_vpd_alloc(pdev, &vpd_size);
if (IS_ERR(vpd_data)) {
pci_warn(pdev, "Unable to read VPD\n");
return;
}
pos = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
PCI_VPD_RO_KEYWORD_PARTNO, &kw_len);
if (pos < 0)
goto read_sn;
size = min_t(int, kw_len, BNGE_VPD_FLD_LEN - 1);
memcpy(bd->board_partno, &vpd_data[pos], size);
read_sn:
pos = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
PCI_VPD_RO_KEYWORD_SERIALNO,
&kw_len);
if (pos < 0)
goto exit;
size = min_t(int, kw_len, BNGE_VPD_FLD_LEN - 1);
memcpy(bd->board_serialno, &vpd_data[pos], size);
exit:
kfree(vpd_data);
}
#define HWRM_FW_VER_STR_LEN 16
static int bnge_devlink_info_get(struct devlink *devlink,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct hwrm_nvm_get_dev_info_output nvm_dev_info;
struct bnge_dev *bd = devlink_priv(devlink);
struct hwrm_ver_get_output *ver_resp;
char mgmt_ver[FW_VER_STR_LEN];
char roce_ver[FW_VER_STR_LEN];
char ncsi_ver[FW_VER_STR_LEN];
char buf[32];
int rc;
if (bd->dsn) {
char buf[32];
u8 dsn[8];
int rc;
put_unaligned_le64(bd->dsn, dsn);
sprintf(buf, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
dsn[7], dsn[6], dsn[5], dsn[4],
dsn[3], dsn[2], dsn[1], dsn[0]);
rc = devlink_info_serial_number_put(req, buf);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to set dsn");
return rc;
}
}
if (strlen(bd->board_serialno)) {
rc = devlink_info_board_serial_number_put(req,
bd->board_serialno);
if (rc) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to set board serial number");
return rc;
}
}
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_FIXED,
DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
bd->board_partno);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to set board part number");
return rc;
}
/* More information from HWRM ver get command */
sprintf(buf, "%X", bd->chip_num);
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_FIXED,
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, buf);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to set asic id");
return rc;
}
ver_resp = &bd->ver_resp;
sprintf(buf, "%c%d", 'A' + ver_resp->chip_rev, ver_resp->chip_metal);
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_FIXED,
DEVLINK_INFO_VERSION_GENERIC_ASIC_REV, buf);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to set asic info");
return rc;
}
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_PSID,
bd->nvm_cfg_ver);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to set firmware version");
return rc;
}
buf[0] = 0;
strncat(buf, ver_resp->active_pkg_name, HWRM_FW_VER_STR_LEN);
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW, buf);
if (rc) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to set firmware generic version");
return rc;
}
if (ver_resp->flags & VER_GET_RESP_FLAGS_EXT_VER_AVAIL) {
snprintf(mgmt_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->hwrm_fw_major, ver_resp->hwrm_fw_minor,
ver_resp->hwrm_fw_build, ver_resp->hwrm_fw_patch);
snprintf(ncsi_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->mgmt_fw_major, ver_resp->mgmt_fw_minor,
ver_resp->mgmt_fw_build, ver_resp->mgmt_fw_patch);
snprintf(roce_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->roce_fw_major, ver_resp->roce_fw_minor,
ver_resp->roce_fw_build, ver_resp->roce_fw_patch);
} else {
snprintf(mgmt_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->hwrm_fw_maj_8b, ver_resp->hwrm_fw_min_8b,
ver_resp->hwrm_fw_bld_8b, ver_resp->hwrm_fw_rsvd_8b);
snprintf(ncsi_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->mgmt_fw_maj_8b, ver_resp->mgmt_fw_min_8b,
ver_resp->mgmt_fw_bld_8b, ver_resp->mgmt_fw_rsvd_8b);
snprintf(roce_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->roce_fw_maj_8b, ver_resp->roce_fw_min_8b,
ver_resp->roce_fw_bld_8b, ver_resp->roce_fw_rsvd_8b);
}
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, mgmt_ver);
if (rc) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to set firmware mgmt version");
return rc;
}
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT_API,
bd->hwrm_ver_supp);
if (rc) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to set firmware mgmt api version");
return rc;
}
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, ncsi_ver);
if (rc) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to set ncsi firmware version");
return rc;
}
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_ROCE, roce_ver);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to set roce firmware version");
return rc;
}
rc = bnge_hwrm_nvm_dev_info(bd, &nvm_dev_info);
if (!(nvm_dev_info.flags & NVM_GET_DEV_INFO_RESP_FLAGS_FW_VER_VALID))
return 0;
buf[0] = 0;
strncat(buf, nvm_dev_info.pkg_name, HWRM_FW_VER_STR_LEN);
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW, buf);
if (rc) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to set firmware version");
return rc;
}
snprintf(mgmt_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
nvm_dev_info.hwrm_fw_major, nvm_dev_info.hwrm_fw_minor,
nvm_dev_info.hwrm_fw_build, nvm_dev_info.hwrm_fw_patch);
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, mgmt_ver);
if (rc) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to set stored firmware version");
return rc;
}
snprintf(ncsi_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
nvm_dev_info.mgmt_fw_major, nvm_dev_info.mgmt_fw_minor,
nvm_dev_info.mgmt_fw_build, nvm_dev_info.mgmt_fw_patch);
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, ncsi_ver);
if (rc) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to set stored ncsi firmware version");
return rc;
}
snprintf(roce_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
nvm_dev_info.roce_fw_major, nvm_dev_info.roce_fw_minor,
nvm_dev_info.roce_fw_build, nvm_dev_info.roce_fw_patch);
rc = bnge_dl_info_put(bd, req, BNGE_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW_ROCE, roce_ver);
if (rc)
NL_SET_ERR_MSG_MOD(extack,
"Failed to set stored roce firmware version");
return rc;
}
static const struct devlink_ops bnge_devlink_ops = {
.info_get = bnge_devlink_info_get,
};
void bnge_devlink_free(struct bnge_dev *bd)
{
struct devlink *devlink = priv_to_devlink(bd);
devlink_free(devlink);
}
struct bnge_dev *bnge_devlink_alloc(struct pci_dev *pdev)
{
struct devlink *devlink;
struct bnge_dev *bd;
devlink = devlink_alloc(&bnge_devlink_ops, sizeof(*bd), &pdev->dev);
if (!devlink)
return NULL;
bd = devlink_priv(devlink);
pci_set_drvdata(pdev, bd);
bd->dev = &pdev->dev;
bd->pdev = pdev;
bd->dsn = pci_get_dsn(pdev);
if (!bd->dsn)
pci_warn(pdev, "Failed to get DSN\n");
bnge_vpd_read_info(bd);
return bd;
}
void bnge_devlink_register(struct bnge_dev *bd)
{
struct devlink *devlink = priv_to_devlink(bd);
devlink_register(devlink);
}
void bnge_devlink_unregister(struct bnge_dev *bd)
{
struct devlink *devlink = priv_to_devlink(bd);
devlink_unregister(devlink);
}
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_DEVLINK_H_
#define _BNGE_DEVLINK_H_
enum bnge_dl_version_type {
BNGE_VERSION_FIXED,
BNGE_VERSION_RUNNING,
BNGE_VERSION_STORED,
};
void bnge_devlink_free(struct bnge_dev *bd);
struct bnge_dev *bnge_devlink_alloc(struct pci_dev *pdev);
void bnge_devlink_register(struct bnge_dev *bd);
void bnge_devlink_unregister(struct bnge_dev *bd);
#endif /* _BNGE_DEVLINK_H_ */
@@ -0,0 +1,765 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Broadcom.
#include <linux/unaligned.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <net/devlink.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/ethtool_netlink.h>
#include "bnge.h"
#include "bnge_ethtool.h"
#include "bnge_hwrm_lib.h"
static int bnge_nway_reset(struct net_device *dev)
{
struct bnge_net *bn = netdev_priv(dev);
struct bnge_dev *bd = bn->bd;
bool set_pause = false;
int rc = 0;
if (!BNGE_PHY_CFG_ABLE(bd))
return -EOPNOTSUPP;
if (!(bn->eth_link_info.autoneg & BNGE_AUTONEG_SPEED))
return -EINVAL;
if (!(bd->phy_flags & BNGE_PHY_FL_NO_PAUSE))
set_pause = true;
if (netif_running(dev))
rc = bnge_hwrm_set_link_setting(bn, set_pause);
return rc;
}
static const char * const bnge_ring_q_stats_str[] = {
"ucast_packets",
"mcast_packets",
"bcast_packets",
"ucast_bytes",
"mcast_bytes",
"bcast_bytes",
};
static const char * const bnge_ring_tpa2_stats_str[] = {
"tpa_eligible_pkt",
"tpa_eligible_bytes",
"tpa_pkt",
"tpa_bytes",
"tpa_errors",
"tpa_events",
};
#define BNGE_RX_PORT_STATS_ENTRY(suffix) \
{ BNGE_RX_STATS_OFFSET(rx_##suffix), "rxp_" __stringify(suffix) }
#define BNGE_TX_PORT_STATS_ENTRY(suffix) \
{ BNGE_TX_STATS_OFFSET(tx_##suffix), "txp_" __stringify(suffix) }
#define BNGE_RX_STATS_EXT_ENTRY(counter) \
{ BNGE_RX_STATS_EXT_OFFSET(counter), __stringify(counter) }
#define BNGE_TX_STATS_EXT_ENTRY(counter) \
{ BNGE_TX_STATS_EXT_OFFSET(counter), __stringify(counter) }
#define BNGE_RX_STATS_EXT_PFC_ENTRY(n) \
BNGE_RX_STATS_EXT_ENTRY(pfc_pri##n##_rx_duration_us), \
BNGE_RX_STATS_EXT_ENTRY(pfc_pri##n##_rx_transitions)
#define BNGE_TX_STATS_EXT_PFC_ENTRY(n) \
BNGE_TX_STATS_EXT_ENTRY(pfc_pri##n##_tx_duration_us), \
BNGE_TX_STATS_EXT_ENTRY(pfc_pri##n##_tx_transitions)
#define BNGE_RX_STATS_EXT_PFC_ENTRIES \
BNGE_RX_STATS_EXT_PFC_ENTRY(0), \
BNGE_RX_STATS_EXT_PFC_ENTRY(1), \
BNGE_RX_STATS_EXT_PFC_ENTRY(2), \
BNGE_RX_STATS_EXT_PFC_ENTRY(3), \
BNGE_RX_STATS_EXT_PFC_ENTRY(4), \
BNGE_RX_STATS_EXT_PFC_ENTRY(5), \
BNGE_RX_STATS_EXT_PFC_ENTRY(6), \
BNGE_RX_STATS_EXT_PFC_ENTRY(7)
#define BNGE_TX_STATS_EXT_PFC_ENTRIES \
BNGE_TX_STATS_EXT_PFC_ENTRY(0), \
BNGE_TX_STATS_EXT_PFC_ENTRY(1), \
BNGE_TX_STATS_EXT_PFC_ENTRY(2), \
BNGE_TX_STATS_EXT_PFC_ENTRY(3), \
BNGE_TX_STATS_EXT_PFC_ENTRY(4), \
BNGE_TX_STATS_EXT_PFC_ENTRY(5), \
BNGE_TX_STATS_EXT_PFC_ENTRY(6), \
BNGE_TX_STATS_EXT_PFC_ENTRY(7)
#define BNGE_RX_STATS_EXT_COS_ENTRY(n) \
BNGE_RX_STATS_EXT_ENTRY(rx_bytes_cos##n), \
BNGE_RX_STATS_EXT_ENTRY(rx_packets_cos##n)
#define BNGE_TX_STATS_EXT_COS_ENTRY(n) \
BNGE_TX_STATS_EXT_ENTRY(tx_bytes_cos##n), \
BNGE_TX_STATS_EXT_ENTRY(tx_packets_cos##n)
#define BNGE_RX_STATS_EXT_COS_ENTRIES \
BNGE_RX_STATS_EXT_COS_ENTRY(0), \
BNGE_RX_STATS_EXT_COS_ENTRY(1), \
BNGE_RX_STATS_EXT_COS_ENTRY(2), \
BNGE_RX_STATS_EXT_COS_ENTRY(3), \
BNGE_RX_STATS_EXT_COS_ENTRY(4), \
BNGE_RX_STATS_EXT_COS_ENTRY(5), \
BNGE_RX_STATS_EXT_COS_ENTRY(6), \
BNGE_RX_STATS_EXT_COS_ENTRY(7) \
#define BNGE_TX_STATS_EXT_COS_ENTRIES \
BNGE_TX_STATS_EXT_COS_ENTRY(0), \
BNGE_TX_STATS_EXT_COS_ENTRY(1), \
BNGE_TX_STATS_EXT_COS_ENTRY(2), \
BNGE_TX_STATS_EXT_COS_ENTRY(3), \
BNGE_TX_STATS_EXT_COS_ENTRY(4), \
BNGE_TX_STATS_EXT_COS_ENTRY(5), \
BNGE_TX_STATS_EXT_COS_ENTRY(6), \
BNGE_TX_STATS_EXT_COS_ENTRY(7) \
#define BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(n) \
BNGE_RX_STATS_EXT_ENTRY(rx_discard_bytes_cos##n), \
BNGE_RX_STATS_EXT_ENTRY(rx_discard_packets_cos##n)
#define BNGE_RX_STATS_EXT_DISCARD_COS_ENTRIES \
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(0), \
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(1), \
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(2), \
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(3), \
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(4), \
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(5), \
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(6), \
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(7)
#define BNGE_RX_STATS_PRI_ENTRY(counter, n) \
{ BNGE_RX_STATS_EXT_OFFSET(counter##_cos0), \
__stringify(counter##_pri##n) }
#define BNGE_TX_STATS_PRI_ENTRY(counter, n) \
{ BNGE_TX_STATS_EXT_OFFSET(counter##_cos0), \
__stringify(counter##_pri##n) }
#define BNGE_RX_STATS_PRI_ENTRIES(counter) \
BNGE_RX_STATS_PRI_ENTRY(counter, 0), \
BNGE_RX_STATS_PRI_ENTRY(counter, 1), \
BNGE_RX_STATS_PRI_ENTRY(counter, 2), \
BNGE_RX_STATS_PRI_ENTRY(counter, 3), \
BNGE_RX_STATS_PRI_ENTRY(counter, 4), \
BNGE_RX_STATS_PRI_ENTRY(counter, 5), \
BNGE_RX_STATS_PRI_ENTRY(counter, 6), \
BNGE_RX_STATS_PRI_ENTRY(counter, 7)
#define BNGE_TX_STATS_PRI_ENTRIES(counter) \
BNGE_TX_STATS_PRI_ENTRY(counter, 0), \
BNGE_TX_STATS_PRI_ENTRY(counter, 1), \
BNGE_TX_STATS_PRI_ENTRY(counter, 2), \
BNGE_TX_STATS_PRI_ENTRY(counter, 3), \
BNGE_TX_STATS_PRI_ENTRY(counter, 4), \
BNGE_TX_STATS_PRI_ENTRY(counter, 5), \
BNGE_TX_STATS_PRI_ENTRY(counter, 6), \
BNGE_TX_STATS_PRI_ENTRY(counter, 7)
#define NUM_RING_Q_HW_STATS ARRAY_SIZE(bnge_ring_q_stats_str)
static const struct {
long offset;
char string[ETH_GSTRING_LEN];
} bnge_tx_port_stats_ext_arr[] = {
BNGE_TX_STATS_EXT_COS_ENTRIES,
BNGE_TX_STATS_EXT_PFC_ENTRIES,
};
static const struct {
long base_off;
char string[ETH_GSTRING_LEN];
} bnge_rx_bytes_pri_arr[] = {
BNGE_RX_STATS_PRI_ENTRIES(rx_bytes),
};
static const struct {
long base_off;
char string[ETH_GSTRING_LEN];
} bnge_rx_pkts_pri_arr[] = {
BNGE_RX_STATS_PRI_ENTRIES(rx_packets),
};
static const struct {
long base_off;
char string[ETH_GSTRING_LEN];
} bnge_tx_bytes_pri_arr[] = {
BNGE_TX_STATS_PRI_ENTRIES(tx_bytes),
};
static const struct {
long base_off;
char string[ETH_GSTRING_LEN];
} bnge_tx_pkts_pri_arr[] = {
BNGE_TX_STATS_PRI_ENTRIES(tx_packets),
};
static const struct {
long offset;
char string[ETH_GSTRING_LEN];
} bnge_port_stats_arr[] = {
BNGE_RX_PORT_STATS_ENTRY(good_vlan_frames),
BNGE_RX_PORT_STATS_ENTRY(mtu_err_frames),
BNGE_RX_PORT_STATS_ENTRY(tagged_frames),
BNGE_RX_PORT_STATS_ENTRY(double_tagged_frames),
BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri0),
BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri1),
BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri2),
BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri3),
BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri4),
BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri5),
BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri6),
BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri7),
BNGE_RX_PORT_STATS_ENTRY(eee_lpi_events),
BNGE_RX_PORT_STATS_ENTRY(eee_lpi_duration),
BNGE_RX_PORT_STATS_ENTRY(runt_bytes),
BNGE_RX_PORT_STATS_ENTRY(runt_frames),
BNGE_TX_PORT_STATS_ENTRY(good_vlan_frames),
BNGE_TX_PORT_STATS_ENTRY(jabber_frames),
BNGE_TX_PORT_STATS_ENTRY(fcs_err_frames),
BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri0),
BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri1),
BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri2),
BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri3),
BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri4),
BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri5),
BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri6),
BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri7),
BNGE_TX_PORT_STATS_ENTRY(eee_lpi_events),
BNGE_TX_PORT_STATS_ENTRY(eee_lpi_duration),
BNGE_TX_PORT_STATS_ENTRY(xthol_frames),
};
static const struct {
long offset;
char string[ETH_GSTRING_LEN];
} bnge_port_stats_ext_arr[] = {
BNGE_RX_STATS_EXT_ENTRY(continuous_pause_events),
BNGE_RX_STATS_EXT_ENTRY(resume_pause_events),
BNGE_RX_STATS_EXT_ENTRY(continuous_roce_pause_events),
BNGE_RX_STATS_EXT_ENTRY(resume_roce_pause_events),
BNGE_RX_STATS_EXT_COS_ENTRIES,
BNGE_RX_STATS_EXT_PFC_ENTRIES,
BNGE_RX_STATS_EXT_ENTRY(rx_bits),
BNGE_RX_STATS_EXT_ENTRY(rx_buffer_passed_threshold),
BNGE_RX_STATS_EXT_DISCARD_COS_ENTRIES,
BNGE_RX_STATS_EXT_ENTRY(rx_filter_miss),
};
static int bnge_get_num_tpa_ring_stats(struct bnge_dev *bd)
{
if (BNGE_SUPPORTS_TPA(bd))
return BNGE_NUM_TPA_RING_STATS;
return 0;
}
#define BNGE_NUM_PORT_STATS ARRAY_SIZE(bnge_port_stats_arr)
#define BNGE_NUM_STATS_PRI \
(ARRAY_SIZE(bnge_rx_bytes_pri_arr) + \
ARRAY_SIZE(bnge_rx_pkts_pri_arr) + \
ARRAY_SIZE(bnge_tx_bytes_pri_arr) + \
ARRAY_SIZE(bnge_tx_pkts_pri_arr))
static int bnge_get_num_ring_stats(struct bnge_dev *bd)
{
int rx, tx;
rx = NUM_RING_Q_HW_STATS + bnge_get_num_tpa_ring_stats(bd);
tx = NUM_RING_Q_HW_STATS;
return rx * bd->rx_nr_rings +
tx * bd->tx_nr_rings_per_tc;
}
static u32 bnge_get_num_stats(struct bnge_net *bn)
{
u32 num_stats = bnge_get_num_ring_stats(bn->bd);
u32 len;
if (bn->flags & BNGE_FLAG_PORT_STATS)
num_stats += BNGE_NUM_PORT_STATS;
if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
len = min_t(u32, bn->fw_rx_stats_ext_size,
ARRAY_SIZE(bnge_port_stats_ext_arr));
num_stats += len;
len = min_t(u32, bn->fw_tx_stats_ext_size,
ARRAY_SIZE(bnge_tx_port_stats_ext_arr));
num_stats += len;
if (bn->pri2cos_valid)
num_stats += BNGE_NUM_STATS_PRI;
}
return num_stats;
}
static void bnge_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct bnge_net *bn = netdev_priv(dev);
struct bnge_dev *bd = bn->bd;
strscpy(info->driver, DRV_NAME, sizeof(info->driver));
strscpy(info->fw_version, bd->fw_ver_str, sizeof(info->fw_version));
strscpy(info->bus_info, pci_name(bd->pdev), sizeof(info->bus_info));
}
static int bnge_get_sset_count(struct net_device *dev, int sset)
{
struct bnge_net *bn = netdev_priv(dev);
switch (sset) {
case ETH_SS_STATS:
return bnge_get_num_stats(bn);
default:
return -EOPNOTSUPP;
}
}
static bool is_rx_ring(struct bnge_dev *bd, u16 ring_num)
{
return ring_num < bd->rx_nr_rings;
}
static bool is_tx_ring(struct bnge_dev *bd, u16 ring_num)
{
u16 tx_base = 0;
if (!(bd->flags & BNGE_EN_SHARED_CHNL))
tx_base = bd->rx_nr_rings;
return ring_num >= tx_base && ring_num < (tx_base + bd->tx_nr_rings_per_tc);
}
static void bnge_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *buf)
{
struct bnge_net *bn = netdev_priv(dev);
struct bnge_dev *bd = bn->bd;
u32 tpa_stats;
u32 i, j = 0;
if (!bn->bnapi) {
j += bnge_get_num_ring_stats(bd);
goto skip_ring_stats;
}
tpa_stats = bnge_get_num_tpa_ring_stats(bd);
for (i = 0; i < bd->nq_nr_rings; i++) {
struct bnge_napi *bnapi = bn->bnapi[i];
struct bnge_nq_ring_info *nqr;
u64 *sw_stats;
int k;
nqr = &bnapi->nq_ring;
sw_stats = nqr->stats.sw_stats;
if (is_rx_ring(bd, i)) {
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_ucast_pkts);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_mcast_pkts);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_bcast_pkts);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_ucast_bytes);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_mcast_bytes);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_bcast_bytes);
}
if (is_tx_ring(bd, i)) {
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_ucast_pkts);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_mcast_pkts);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_bcast_pkts);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_ucast_bytes);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_mcast_bytes);
buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_bcast_bytes);
}
if (!tpa_stats || !is_rx_ring(bd, i))
continue;
k = BNGE_NUM_RX_RING_STATS + BNGE_NUM_TX_RING_STATS;
for (; k < BNGE_NUM_RX_RING_STATS + BNGE_NUM_TX_RING_STATS +
tpa_stats; j++, k++)
buf[j] = sw_stats[k];
}
skip_ring_stats:
if (bn->flags & BNGE_FLAG_PORT_STATS) {
u64 *port_stats = bn->port_stats.sw_stats;
for (i = 0; i < BNGE_NUM_PORT_STATS; i++, j++)
buf[j] = *(port_stats + bnge_port_stats_arr[i].offset);
}
if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
u64 *rx_port_stats_ext = bn->rx_port_stats_ext.sw_stats;
u64 *tx_port_stats_ext = bn->tx_port_stats_ext.sw_stats;
u32 len;
len = min_t(u32, bn->fw_rx_stats_ext_size,
ARRAY_SIZE(bnge_port_stats_ext_arr));
for (i = 0; i < len; i++, j++) {
buf[j] = *(rx_port_stats_ext +
bnge_port_stats_ext_arr[i].offset);
}
len = min_t(u32, bn->fw_tx_stats_ext_size,
ARRAY_SIZE(bnge_tx_port_stats_ext_arr));
for (i = 0; i < len; i++, j++) {
buf[j] = *(tx_port_stats_ext +
bnge_tx_port_stats_ext_arr[i].offset);
}
if (bn->pri2cos_valid) {
for (i = 0; i < 8; i++, j++) {
long n = bnge_rx_bytes_pri_arr[i].base_off +
bn->pri2cos_idx[i];
buf[j] = *(rx_port_stats_ext + n);
}
for (i = 0; i < 8; i++, j++) {
long n = bnge_rx_pkts_pri_arr[i].base_off +
bn->pri2cos_idx[i];
buf[j] = *(rx_port_stats_ext + n);
}
for (i = 0; i < 8; i++, j++) {
long n = bnge_tx_bytes_pri_arr[i].base_off +
bn->pri2cos_idx[i];
buf[j] = *(tx_port_stats_ext + n);
}
for (i = 0; i < 8; i++, j++) {
long n = bnge_tx_pkts_pri_arr[i].base_off +
bn->pri2cos_idx[i];
buf[j] = *(tx_port_stats_ext + n);
}
}
}
}
static void bnge_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
{
struct bnge_net *bn = netdev_priv(dev);
struct bnge_dev *bd = bn->bd;
u32 i, j, num_str;
const char *str;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < bd->nq_nr_rings; i++) {
if (is_rx_ring(bd, i))
for (j = 0; j < NUM_RING_Q_HW_STATS; j++) {
str = bnge_ring_q_stats_str[j];
ethtool_sprintf(&buf, "rxq%d_%s", i,
str);
}
if (is_tx_ring(bd, i))
for (j = 0; j < NUM_RING_Q_HW_STATS; j++) {
str = bnge_ring_q_stats_str[j];
ethtool_sprintf(&buf, "txq%d_%s", i,
str);
}
num_str = bnge_get_num_tpa_ring_stats(bd);
if (!num_str || !is_rx_ring(bd, i))
continue;
for (j = 0; j < num_str; j++) {
str = bnge_ring_tpa2_stats_str[j];
ethtool_sprintf(&buf, "rxq%d_%s", i, str);
}
}
if (bn->flags & BNGE_FLAG_PORT_STATS)
for (i = 0; i < BNGE_NUM_PORT_STATS; i++) {
str = bnge_port_stats_arr[i].string;
ethtool_puts(&buf, str);
}
if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
u32 len;
len = min_t(u32, bn->fw_rx_stats_ext_size,
ARRAY_SIZE(bnge_port_stats_ext_arr));
for (i = 0; i < len; i++) {
str = bnge_port_stats_ext_arr[i].string;
ethtool_puts(&buf, str);
}
len = min_t(u32, bn->fw_tx_stats_ext_size,
ARRAY_SIZE(bnge_tx_port_stats_ext_arr));
for (i = 0; i < len; i++) {
str = bnge_tx_port_stats_ext_arr[i].string;
ethtool_puts(&buf, str);
}
if (bn->pri2cos_valid) {
for (i = 0; i < 8; i++) {
str = bnge_rx_bytes_pri_arr[i].string;
ethtool_puts(&buf, str);
}
for (i = 0; i < 8; i++) {
str = bnge_rx_pkts_pri_arr[i].string;
ethtool_puts(&buf, str);
}
for (i = 0; i < 8; i++) {
str = bnge_tx_bytes_pri_arr[i].string;
ethtool_puts(&buf, str);
}
for (i = 0; i < 8; i++) {
str = bnge_tx_pkts_pri_arr[i].string;
ethtool_puts(&buf, str);
}
}
}
break;
default:
netdev_err(bd->netdev, "%s invalid request %x\n",
__func__, stringset);
break;
}
}
static void bnge_get_eth_phy_stats(struct net_device *dev,
struct ethtool_eth_phy_stats *phy_stats)
{
struct bnge_net *bn = netdev_priv(dev);
u64 *rx;
if (!(bn->flags & BNGE_FLAG_PORT_STATS_EXT))
return;
rx = bn->rx_port_stats_ext.sw_stats;
phy_stats->SymbolErrorDuringCarrier =
*(rx + BNGE_RX_STATS_EXT_OFFSET(rx_pcs_symbol_err));
}
static void bnge_get_eth_mac_stats(struct net_device *dev,
struct ethtool_eth_mac_stats *mac_stats)
{
struct bnge_net *bn = netdev_priv(dev);
u64 *rx, *tx;
if (!(bn->flags & BNGE_FLAG_PORT_STATS))
return;
rx = bn->port_stats.sw_stats;
tx = bn->port_stats.sw_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
mac_stats->FramesReceivedOK =
BNGE_GET_RX_PORT_STATS64(rx, rx_good_frames);
mac_stats->FramesTransmittedOK =
BNGE_GET_TX_PORT_STATS64(tx, tx_good_frames);
mac_stats->FrameCheckSequenceErrors =
BNGE_GET_RX_PORT_STATS64(rx, rx_fcs_err_frames);
mac_stats->AlignmentErrors =
BNGE_GET_RX_PORT_STATS64(rx, rx_align_err_frames);
mac_stats->OutOfRangeLengthField =
BNGE_GET_RX_PORT_STATS64(rx, rx_oor_len_frames);
mac_stats->OctetsReceivedOK = BNGE_GET_RX_PORT_STATS64(rx, rx_bytes);
mac_stats->OctetsTransmittedOK = BNGE_GET_TX_PORT_STATS64(tx, tx_bytes);
mac_stats->MulticastFramesReceivedOK =
BNGE_GET_RX_PORT_STATS64(rx, rx_mcast_frames);
mac_stats->BroadcastFramesReceivedOK =
BNGE_GET_RX_PORT_STATS64(rx, rx_bcast_frames);
mac_stats->MulticastFramesXmittedOK =
BNGE_GET_TX_PORT_STATS64(tx, tx_mcast_frames);
mac_stats->BroadcastFramesXmittedOK =
BNGE_GET_TX_PORT_STATS64(tx, tx_bcast_frames);
mac_stats->FrameTooLongErrors =
BNGE_GET_RX_PORT_STATS64(rx, rx_ovrsz_frames);
}
static void bnge_get_eth_ctrl_stats(struct net_device *dev,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
struct bnge_net *bn = netdev_priv(dev);
u64 *rx;
if (!(bn->flags & BNGE_FLAG_PORT_STATS))
return;
rx = bn->port_stats.sw_stats;
ctrl_stats->MACControlFramesReceived =
BNGE_GET_RX_PORT_STATS64(rx, rx_ctrl_frames);
}
static void bnge_get_pause_stats(struct net_device *dev,
struct ethtool_pause_stats *pause_stats)
{
struct bnge_net *bn = netdev_priv(dev);
u64 *rx, *tx;
if (!(bn->flags & BNGE_FLAG_PORT_STATS))
return;
rx = bn->port_stats.sw_stats;
tx = bn->port_stats.sw_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
pause_stats->rx_pause_frames =
BNGE_GET_RX_PORT_STATS64(rx, rx_pause_frames);
pause_stats->tx_pause_frames =
BNGE_GET_TX_PORT_STATS64(tx, tx_pause_frames);
}
static const struct ethtool_rmon_hist_range bnge_rmon_ranges[] = {
{ 0, 64 },
{ 65, 127 },
{ 128, 255 },
{ 256, 511 },
{ 512, 1023 },
{ 1024, 1518 },
{ 1519, 2047 },
{ 2048, 4095 },
{ 4096, 9216 },
{ 9217, 16383 },
{}
};
static void bnge_get_rmon_stats(struct net_device *dev,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges)
{
struct bnge_net *bn = netdev_priv(dev);
u64 *rx, *tx;
if (!(bn->flags & BNGE_FLAG_PORT_STATS))
return;
rx = bn->port_stats.sw_stats;
tx = bn->port_stats.sw_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
rmon_stats->jabbers = BNGE_GET_RX_PORT_STATS64(rx, rx_jbr_frames);
rmon_stats->oversize_pkts =
BNGE_GET_RX_PORT_STATS64(rx, rx_ovrsz_frames);
rmon_stats->undersize_pkts =
BNGE_GET_RX_PORT_STATS64(rx, rx_undrsz_frames);
rmon_stats->hist[0] = BNGE_GET_RX_PORT_STATS64(rx, rx_64b_frames);
rmon_stats->hist[1] = BNGE_GET_RX_PORT_STATS64(rx, rx_65b_127b_frames);
rmon_stats->hist[2] = BNGE_GET_RX_PORT_STATS64(rx, rx_128b_255b_frames);
rmon_stats->hist[3] = BNGE_GET_RX_PORT_STATS64(rx, rx_256b_511b_frames);
rmon_stats->hist[4] =
BNGE_GET_RX_PORT_STATS64(rx, rx_512b_1023b_frames);
rmon_stats->hist[5] =
BNGE_GET_RX_PORT_STATS64(rx, rx_1024b_1518b_frames);
rmon_stats->hist[6] =
BNGE_GET_RX_PORT_STATS64(rx, rx_1519b_2047b_frames);
rmon_stats->hist[7] =
BNGE_GET_RX_PORT_STATS64(rx, rx_2048b_4095b_frames);
rmon_stats->hist[8] =
BNGE_GET_RX_PORT_STATS64(rx, rx_4096b_9216b_frames);
rmon_stats->hist[9] =
BNGE_GET_RX_PORT_STATS64(rx, rx_9217b_16383b_frames);
rmon_stats->hist_tx[0] = BNGE_GET_TX_PORT_STATS64(tx, tx_64b_frames);
rmon_stats->hist_tx[1] =
BNGE_GET_TX_PORT_STATS64(tx, tx_65b_127b_frames);
rmon_stats->hist_tx[2] =
BNGE_GET_TX_PORT_STATS64(tx, tx_128b_255b_frames);
rmon_stats->hist_tx[3] =
BNGE_GET_TX_PORT_STATS64(tx, tx_256b_511b_frames);
rmon_stats->hist_tx[4] =
BNGE_GET_TX_PORT_STATS64(tx, tx_512b_1023b_frames);
rmon_stats->hist_tx[5] =
BNGE_GET_TX_PORT_STATS64(tx, tx_1024b_1518b_frames);
rmon_stats->hist_tx[6] =
BNGE_GET_TX_PORT_STATS64(tx, tx_1519b_2047b_frames);
rmon_stats->hist_tx[7] =
BNGE_GET_TX_PORT_STATS64(tx, tx_2048b_4095b_frames);
rmon_stats->hist_tx[8] =
BNGE_GET_TX_PORT_STATS64(tx, tx_4096b_9216b_frames);
rmon_stats->hist_tx[9] =
BNGE_GET_TX_PORT_STATS64(tx, tx_9217b_16383b_frames);
*ranges = bnge_rmon_ranges;
}
static void bnge_get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct bnge_net *bn = netdev_priv(dev);
struct bnge_dev *bd = bn->bd;
if (bd->phy_flags & BNGE_PHY_FL_NO_PAUSE) {
epause->autoneg = 0;
epause->rx_pause = 0;
epause->tx_pause = 0;
return;
}
epause->autoneg = !!(bn->eth_link_info.autoneg &
BNGE_AUTONEG_FLOW_CTRL);
epause->rx_pause = !!(bn->eth_link_info.req_flow_ctrl &
BNGE_LINK_PAUSE_RX);
epause->tx_pause = !!(bn->eth_link_info.req_flow_ctrl &
BNGE_LINK_PAUSE_TX);
}
static int bnge_set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct bnge_ethtool_link_info old_elink_info, *elink_info;
struct bnge_net *bn = netdev_priv(dev);
struct bnge_dev *bd = bn->bd;
int rc = 0;
if (!BNGE_PHY_CFG_ABLE(bd) || (bd->phy_flags & BNGE_PHY_FL_NO_PAUSE))
return -EOPNOTSUPP;
elink_info = &bn->eth_link_info;
old_elink_info = *elink_info;
if (epause->autoneg) {
if (!(elink_info->autoneg & BNGE_AUTONEG_SPEED))
return -EINVAL;
elink_info->autoneg |= BNGE_AUTONEG_FLOW_CTRL;
} else {
if (elink_info->autoneg & BNGE_AUTONEG_FLOW_CTRL)
elink_info->force_link_chng = true;
elink_info->autoneg &= ~BNGE_AUTONEG_FLOW_CTRL;
}
elink_info->req_flow_ctrl = 0;
if (epause->rx_pause)
elink_info->req_flow_ctrl |= BNGE_LINK_PAUSE_RX;
if (epause->tx_pause)
elink_info->req_flow_ctrl |= BNGE_LINK_PAUSE_TX;
if (netif_running(dev)) {
rc = bnge_hwrm_set_pause(bn);
if (rc)
*elink_info = old_elink_info;
}
return rc;
}
static const struct ethtool_ops bnge_ethtool_ops = {
.cap_link_lanes_supported = 1,
.get_link_ksettings = bnge_get_link_ksettings,
.set_link_ksettings = bnge_set_link_ksettings,
.get_drvinfo = bnge_get_drvinfo,
.get_link = bnge_get_link,
.nway_reset = bnge_nway_reset,
.get_pauseparam = bnge_get_pauseparam,
.set_pauseparam = bnge_set_pauseparam,
.get_sset_count = bnge_get_sset_count,
.get_strings = bnge_get_strings,
.get_ethtool_stats = bnge_get_ethtool_stats,
.get_eth_phy_stats = bnge_get_eth_phy_stats,
.get_eth_mac_stats = bnge_get_eth_mac_stats,
.get_eth_ctrl_stats = bnge_get_eth_ctrl_stats,
.get_pause_stats = bnge_get_pause_stats,
.get_rmon_stats = bnge_get_rmon_stats,
};
void bnge_set_ethtool_ops(struct net_device *dev)
{
dev->ethtool_ops = &bnge_ethtool_ops;
}
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_ETHTOOL_H_
#define _BNGE_ETHTOOL_H_
void bnge_set_ethtool_ops(struct net_device *dev);
#endif /* _BNGE_ETHTOOL_H_ */
@@ -0,0 +1,446 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_HW_DEF_H_
#define _BNGE_HW_DEF_H_
#define TX_BD_FLAGS_TCP_UDP_CHKSUM BIT(0)
#define TX_BD_FLAGS_IP_CKSUM BIT(1)
#define TX_BD_FLAGS_NO_CRC BIT(2)
#define TX_BD_FLAGS_STAMP BIT(3)
#define TX_BD_FLAGS_T_IP_CHKSUM BIT(4)
#define TX_BD_FLAGS_LSO BIT(5)
#define TX_BD_FLAGS_IPID_FMT BIT(6)
#define TX_BD_FLAGS_T_IPID BIT(7)
#define TX_BD_HSIZE GENMASK(23, 16)
#define TX_BD_HSIZE_SHIFT 16
#define TX_BD_CFA_ACTION GENMASK(31, 16)
#define TX_BD_CFA_ACTION_SHIFT 16
#define TX_BD_CFA_META_MASK 0xfffffff
#define TX_BD_CFA_META_VID_MASK 0xfff
#define TX_BD_CFA_META_PRI_MASK GENMASK(15, 12)
#define TX_BD_CFA_META_PRI_SHIFT 12
#define TX_BD_CFA_META_TPID_MASK GENMASK(17, 16)
#define TX_BD_CFA_META_TPID_SHIFT 16
#define TX_BD_CFA_META_KEY GENMASK(31, 28)
#define TX_BD_CFA_META_KEY_SHIFT 28
#define TX_BD_CFA_META_KEY_VLAN BIT(28)
struct tx_bd_ext {
__le32 tx_bd_hsize_lflags;
__le32 tx_bd_mss;
__le32 tx_bd_cfa_action;
__le32 tx_bd_cfa_meta;
};
#define TX_CMP_SQ_CONS_IDX(txcmp) \
(le32_to_cpu((txcmp)->sq_cons_idx) & TX_CMP_SQ_CONS_IDX_MASK)
#define RX_CMP_CMP_TYPE GENMASK(5, 0)
#define RX_CMP_FLAGS_ERROR BIT(6)
#define RX_CMP_FLAGS_PLACEMENT GENMASK(9, 7)
#define RX_CMP_FLAGS_RSS_VALID BIT(10)
#define RX_CMP_FLAGS_PKT_METADATA_PRESENT BIT(11)
#define RX_CMP_FLAGS_ITYPES_SHIFT 12
#define RX_CMP_FLAGS_ITYPES_MASK 0xf000
#define RX_CMP_FLAGS_ITYPE_UNKNOWN (0 << 12)
#define RX_CMP_FLAGS_ITYPE_IP (1 << 12)
#define RX_CMP_FLAGS_ITYPE_TCP (2 << 12)
#define RX_CMP_FLAGS_ITYPE_UDP (3 << 12)
#define RX_CMP_FLAGS_ITYPE_FCOE (4 << 12)
#define RX_CMP_FLAGS_ITYPE_ROCE (5 << 12)
#define RX_CMP_FLAGS_ITYPE_PTP_WO_TS (8 << 12)
#define RX_CMP_FLAGS_ITYPE_PTP_W_TS (9 << 12)
#define RX_CMP_LEN GENMASK(31, 16)
#define RX_CMP_LEN_SHIFT 16
#define RX_CMP_V1 BIT(0)
#define RX_CMP_AGG_BUFS GENMASK(5, 1)
#define RX_CMP_AGG_BUFS_SHIFT 1
#define RX_CMP_RSS_HASH_TYPE GENMASK(15, 9)
#define RX_CMP_RSS_HASH_TYPE_SHIFT 9
#define RX_CMP_V3_RSS_EXT_OP_LEGACY GENMASK(15, 12)
#define RX_CMP_V3_RSS_EXT_OP_LEGACY_SHIFT 12
#define RX_CMP_V3_RSS_EXT_OP_NEW GENMASK(11, 8)
#define RX_CMP_V3_RSS_EXT_OP_NEW_SHIFT 8
#define RX_CMP_PAYLOAD_OFFSET GENMASK(23, 16)
#define RX_CMP_PAYLOAD_OFFSET_SHIFT 16
#define RX_CMP_SUB_NS_TS GENMASK(19, 16)
#define RX_CMP_SUB_NS_TS_SHIFT 16
#define RX_CMP_METADATA1 GENMASK(31, 28)
#define RX_CMP_METADATA1_SHIFT 28
#define RX_CMP_METADATA1_TPID_SEL GENMASK(30, 28)
#define RX_CMP_METADATA1_TPID_8021Q BIT(28)
#define RX_CMP_METADATA1_TPID_8021AD (0x0 << 28)
#define RX_CMP_METADATA1_VALID BIT(31)
struct rx_cmp {
__le32 rx_cmp_len_flags_type;
u32 rx_cmp_opaque;
__le32 rx_cmp_misc_v1;
__le32 rx_cmp_rss_hash;
};
#define RX_CMP_FLAGS2_IP_CS_CALC BIT(0)
#define RX_CMP_FLAGS2_L4_CS_CALC BIT(1)
#define RX_CMP_FLAGS2_T_IP_CS_CALC BIT(2)
#define RX_CMP_FLAGS2_T_L4_CS_CALC BIT(3)
#define RX_CMP_FLAGS2_META_FORMAT_VLAN BIT(4)
#define RX_CMP_FLAGS2_METADATA_TCI_MASK GENMASK(15, 0)
#define RX_CMP_FLAGS2_METADATA_VID_MASK GENMASK(11, 0)
#define RX_CMP_FLAGS2_METADATA_TPID_MASK GENMASK(31, 16)
#define RX_CMP_FLAGS2_METADATA_TPID_SFT 16
#define RX_CMP_V BIT(0)
#define RX_CMPL_ERRORS_MASK GENMASK(15, 1)
#define RX_CMPL_ERRORS_SFT 1
#define RX_CMPL_ERRORS_BUFFER_ERROR_MASK GENMASK(3, 1)
#define RX_CMPL_ERRORS_BUFFER_ERROR_NO_BUFFER (0x0 << 1)
#define RX_CMPL_ERRORS_BUFFER_ERROR_DID_NOT_FIT (0x1 << 1)
#define RX_CMPL_ERRORS_BUFFER_ERROR_NOT_ON_CHIP (0x2 << 1)
#define RX_CMPL_ERRORS_BUFFER_ERROR_BAD_FORMAT (0x3 << 1)
#define RX_CMPL_ERRORS_IP_CS_ERROR BIT(4)
#define RX_CMPL_ERRORS_L4_CS_ERROR BIT(5)
#define RX_CMPL_ERRORS_T_IP_CS_ERROR BIT(6)
#define RX_CMPL_ERRORS_T_L4_CS_ERROR BIT(7)
#define RX_CMPL_ERRORS_CRC_ERROR BIT(8)
#define RX_CMPL_ERRORS_T_PKT_ERROR_MASK GENMASK(11, 9)
#define RX_CMPL_ERRORS_T_PKT_ERROR_NO_ERROR (0x0 << 9)
#define RX_CMPL_ERRORS_T_PKT_ERROR_T_L3_BAD_VERSION (0x1 << 9)
#define RX_CMPL_ERRORS_T_PKT_ERROR_T_L3_BAD_HDR_LEN (0x2 << 9)
#define RX_CMPL_ERRORS_T_PKT_ERROR_TUNNEL_TOTAL_ERROR (0x3 << 9)
#define RX_CMPL_ERRORS_T_PKT_ERROR_T_IP_TOTAL_ERROR (0x4 << 9)
#define RX_CMPL_ERRORS_T_PKT_ERROR_T_UDP_TOTAL_ERROR (0x5 << 9)
#define RX_CMPL_ERRORS_T_PKT_ERROR_T_L3_BAD_TTL (0x6 << 9)
#define RX_CMPL_ERRORS_PKT_ERROR_MASK GENMASK(15, 12)
#define RX_CMPL_ERRORS_PKT_ERROR_NO_ERROR (0x0 << 12)
#define RX_CMPL_ERRORS_PKT_ERROR_L3_BAD_VERSION (0x1 << 12)
#define RX_CMPL_ERRORS_PKT_ERROR_L3_BAD_HDR_LEN (0x2 << 12)
#define RX_CMPL_ERRORS_PKT_ERROR_L3_BAD_TTL (0x3 << 12)
#define RX_CMPL_ERRORS_PKT_ERROR_IP_TOTAL_ERROR (0x4 << 12)
#define RX_CMPL_ERRORS_PKT_ERROR_UDP_TOTAL_ERROR (0x5 << 12)
#define RX_CMPL_ERRORS_PKT_ERROR_L4_BAD_HDR_LEN (0x6 << 12)
#define RX_CMPL_ERRORS_PKT_ERROR_L4_BAD_HDR_LEN_TOO_SMALL (0x7 << 12)
#define RX_CMPL_ERRORS_PKT_ERROR_L4_BAD_OPT_LEN (0x8 << 12)
#define RX_CMPL_CFA_CODE_MASK GENMASK(31, 16)
#define RX_CMPL_CFA_CODE_SFT 16
#define RX_CMPL_METADATA0_TCI_MASK GENMASK(31, 16)
#define RX_CMPL_METADATA0_VID_MASK GENMASK(27, 16)
#define RX_CMPL_METADATA0_SFT 16
struct rx_cmp_ext {
__le32 rx_cmp_flags2;
__le32 rx_cmp_meta_data;
__le32 rx_cmp_cfa_code_errors_v2;
__le32 rx_cmp_timestamp;
};
#define RX_AGG_CMP_TYPE GENMASK(5, 0)
#define RX_AGG_CMP_LEN GENMASK(31, 16)
#define RX_AGG_CMP_LEN_SHIFT 16
#define RX_AGG_CMP_V BIT(0)
#define RX_AGG_CMP_AGG_ID GENMASK(25, 16)
#define RX_AGG_CMP_AGG_ID_SHIFT 16
struct rx_agg_cmp {
__le32 rx_agg_cmp_len_flags_type;
u32 rx_agg_cmp_opaque;
__le32 rx_agg_cmp_v;
__le32 rx_agg_cmp_unused;
};
#define RX_CMP_L2_ERRORS \
cpu_to_le32(RX_CMPL_ERRORS_BUFFER_ERROR_MASK | RX_CMPL_ERRORS_CRC_ERROR)
#define RX_CMP_L4_CS_BITS \
(cpu_to_le32(RX_CMP_FLAGS2_L4_CS_CALC | RX_CMP_FLAGS2_T_L4_CS_CALC))
#define RX_CMP_L4_CS_ERR_BITS \
(cpu_to_le32(RX_CMPL_ERRORS_L4_CS_ERROR | RX_CMPL_ERRORS_T_L4_CS_ERROR))
#define RX_CMP_L4_CS_OK(rxcmp1) \
(((rxcmp1)->rx_cmp_flags2 & RX_CMP_L4_CS_BITS) && \
!((rxcmp1)->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS))
#define RX_CMP_METADATA0_TCI(rxcmp1) \
((le32_to_cpu((rxcmp1)->rx_cmp_cfa_code_errors_v2) & \
RX_CMPL_METADATA0_TCI_MASK) >> RX_CMPL_METADATA0_SFT)
#define RX_CMP_ENCAP(rxcmp1) \
((le32_to_cpu((rxcmp1)->rx_cmp_flags2) & \
RX_CMP_FLAGS2_T_L4_CS_CALC) >> 3)
#define RX_CMP_V3_HASH_TYPE_LEGACY(rxcmp) \
((le32_to_cpu((rxcmp)->rx_cmp_misc_v1) & \
RX_CMP_V3_RSS_EXT_OP_LEGACY) >> RX_CMP_V3_RSS_EXT_OP_LEGACY_SHIFT)
#define RX_CMP_V3_HASH_TYPE_NEW(rxcmp) \
((le32_to_cpu((rxcmp)->rx_cmp_misc_v1) & RX_CMP_V3_RSS_EXT_OP_NEW) >>\
RX_CMP_V3_RSS_EXT_OP_NEW_SHIFT)
#define RX_CMP_V3_HASH_TYPE(bd, rxcmp) \
(((bd)->rss_cap & BNGE_RSS_CAP_RSS_TCAM) ? \
RX_CMP_V3_HASH_TYPE_NEW(rxcmp) : \
RX_CMP_V3_HASH_TYPE_LEGACY(rxcmp))
#define EXT_OP_INNER_4 0x0
#define EXT_OP_OUTER_4 0x2
#define EXT_OP_INNFL_3 0x8
#define EXT_OP_OUTFL_3 0xa
#define RX_CMP_VLAN_VALID(rxcmp) \
((rxcmp)->rx_cmp_misc_v1 & cpu_to_le32(RX_CMP_METADATA1_VALID))
#define RX_CMP_VLAN_TPID_SEL(rxcmp) \
(le32_to_cpu((rxcmp)->rx_cmp_misc_v1) & RX_CMP_METADATA1_TPID_SEL)
#define RSS_PROFILE_ID_MASK GENMASK(4, 0)
#define RX_CMP_HASH_TYPE(rxcmp) \
(((le32_to_cpu((rxcmp)->rx_cmp_misc_v1) & RX_CMP_RSS_HASH_TYPE) >>\
RX_CMP_RSS_HASH_TYPE_SHIFT) & RSS_PROFILE_ID_MASK)
#define RX_CMP_HASH_VALID(rxcmp) \
((rxcmp)->rx_cmp_len_flags_type & cpu_to_le32(RX_CMP_FLAGS_RSS_VALID))
#define TPA_AGG_AGG_ID(rx_agg) \
((le32_to_cpu((rx_agg)->rx_agg_cmp_v) & \
RX_AGG_CMP_AGG_ID) >> RX_AGG_CMP_AGG_ID_SHIFT)
#define RX_TPA_START_CMP_TYPE GENMASK(5, 0)
#define RX_TPA_START_CMP_FLAGS GENMASK(15, 6)
#define RX_TPA_START_CMP_FLAGS_SHIFT 6
#define RX_TPA_START_CMP_FLAGS_ERROR BIT(6)
#define RX_TPA_START_CMP_FLAGS_PLACEMENT GENMASK(9, 7)
#define RX_TPA_START_CMP_FLAGS_PLACEMENT_SHIFT 7
#define RX_TPA_START_CMP_FLAGS_PLACEMENT_JUMBO BIT(7)
#define RX_TPA_START_CMP_FLAGS_PLACEMENT_HDS (0x2 << 7)
#define RX_TPA_START_CMP_FLAGS_PLACEMENT_GRO_JUMBO (0x5 << 7)
#define RX_TPA_START_CMP_FLAGS_PLACEMENT_GRO_HDS (0x6 << 7)
#define RX_TPA_START_CMP_FLAGS_RSS_VALID BIT(10)
#define RX_TPA_START_CMP_FLAGS_TIMESTAMP BIT(11)
#define RX_TPA_START_CMP_FLAGS_ITYPES GENMASK(15, 12)
#define RX_TPA_START_CMP_FLAGS_ITYPES_SHIFT 12
#define RX_TPA_START_CMP_FLAGS_ITYPE_TCP (0x2 << 12)
#define RX_TPA_START_CMP_LEN GENMASK(31, 16)
#define RX_TPA_START_CMP_LEN_SHIFT 16
#define RX_TPA_START_CMP_V1 BIT(0)
#define RX_TPA_START_CMP_RSS_HASH_TYPE GENMASK(15, 9)
#define RX_TPA_START_CMP_RSS_HASH_TYPE_SHIFT 9
#define RX_TPA_START_CMP_V3_RSS_HASH_TYPE GENMASK(15, 7)
#define RX_TPA_START_CMP_V3_RSS_HASH_TYPE_SHIFT 7
#define RX_TPA_START_CMP_AGG_ID GENMASK(25, 16)
#define RX_TPA_START_CMP_AGG_ID_SHIFT 16
#define RX_TPA_START_CMP_METADATA1 GENMASK(31, 28)
#define RX_TPA_START_CMP_METADATA1_SHIFT 28
#define RX_TPA_START_METADATA1_TPID_SEL GENMASK(30, 28)
#define RX_TPA_START_METADATA1_TPID_8021Q BIT(28)
#define RX_TPA_START_METADATA1_TPID_8021AD (0x0 << 28)
#define RX_TPA_START_METADATA1_VALID BIT(31)
struct rx_tpa_start_cmp {
__le32 rx_tpa_start_cmp_len_flags_type;
u32 rx_tpa_start_cmp_opaque;
__le32 rx_tpa_start_cmp_misc_v1;
__le32 rx_tpa_start_cmp_rss_hash;
};
#define TPA_START_HASH_VALID(rx_tpa_start) \
((rx_tpa_start)->rx_tpa_start_cmp_len_flags_type & \
cpu_to_le32(RX_TPA_START_CMP_FLAGS_RSS_VALID))
#define TPA_START_HASH_TYPE(rx_tpa_start) \
(((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_misc_v1) & \
RX_TPA_START_CMP_RSS_HASH_TYPE) >> \
RX_TPA_START_CMP_RSS_HASH_TYPE_SHIFT) & RSS_PROFILE_ID_MASK)
#define TPA_START_V3_HASH_TYPE(rx_tpa_start) \
(((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_misc_v1) & \
RX_TPA_START_CMP_V3_RSS_HASH_TYPE) >> \
RX_TPA_START_CMP_V3_RSS_HASH_TYPE_SHIFT) & RSS_PROFILE_ID_MASK)
#define TPA_START_AGG_ID(rx_tpa_start) \
((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_misc_v1) & \
RX_TPA_START_CMP_AGG_ID) >> RX_TPA_START_CMP_AGG_ID_SHIFT)
#define TPA_START_ERROR(rx_tpa_start) \
((rx_tpa_start)->rx_tpa_start_cmp_len_flags_type & \
cpu_to_le32(RX_TPA_START_CMP_FLAGS_ERROR))
#define TPA_START_VLAN_VALID(rx_tpa_start) \
((rx_tpa_start)->rx_tpa_start_cmp_misc_v1 & \
cpu_to_le32(RX_TPA_START_METADATA1_VALID))
#define TPA_START_VLAN_TPID_SEL(rx_tpa_start) \
(le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_misc_v1) & \
RX_TPA_START_METADATA1_TPID_SEL)
#define RX_TPA_START_CMP_FLAGS2_IP_CS_CALC BIT(0)
#define RX_TPA_START_CMP_FLAGS2_L4_CS_CALC BIT(1)
#define RX_TPA_START_CMP_FLAGS2_T_IP_CS_CALC BIT(2)
#define RX_TPA_START_CMP_FLAGS2_T_L4_CS_CALC BIT(3)
#define RX_TPA_START_CMP_FLAGS2_IP_TYPE BIT(8)
#define RX_TPA_START_CMP_FLAGS2_CSUM_CMPL_VALID BIT(9)
#define RX_TPA_START_CMP_FLAGS2_EXT_META_FORMAT GENMASK(11, 10)
#define RX_TPA_START_CMP_FLAGS2_EXT_META_FORMAT_SHIFT 10
#define RX_TPA_START_CMP_V3_FLAGS2_T_IP_TYPE BIT(10)
#define RX_TPA_START_CMP_V3_FLAGS2_AGG_GRO BIT(11)
#define RX_TPA_START_CMP_FLAGS2_CSUM_CMPL GENMASK(31, 16)
#define RX_TPA_START_CMP_FLAGS2_CSUM_CMPL_SHIFT 16
#define RX_TPA_START_CMP_V2 BIT(0)
#define RX_TPA_START_CMP_ERRORS_BUFFER_ERROR_MASK GENMASK(3, 1)
#define RX_TPA_START_CMP_ERRORS_BUFFER_ERROR_SHIFT 1
#define RX_TPA_START_CMP_ERRORS_BUFFER_ERROR_NO_BUFFER (0x0 << 1)
#define RX_TPA_START_CMP_ERRORS_BUFFER_ERROR_BAD_FORMAT (0x3 << 1)
#define RX_TPA_START_CMP_ERRORS_BUFFER_ERROR_FLUSH (0x5 << 1)
#define RX_TPA_START_CMP_CFA_CODE GENMASK(31, 16)
#define RX_TPA_START_CMPL_CFA_CODE_SHIFT 16
#define RX_TPA_START_CMP_METADATA0_TCI_MASK GENMASK(31, 16)
#define RX_TPA_START_CMP_METADATA0_VID_MASK GENMASK(27, 16)
#define RX_TPA_START_CMP_METADATA0_SFT 16
struct rx_tpa_start_cmp_ext {
__le32 rx_tpa_start_cmp_flags2;
__le32 rx_tpa_start_cmp_metadata;
__le32 rx_tpa_start_cmp_cfa_code_v2;
__le32 rx_tpa_start_cmp_hdr_info;
};
#define TPA_START_CFA_CODE(rx_tpa_start) \
((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_cfa_code_v2) & \
RX_TPA_START_CMP_CFA_CODE) >> RX_TPA_START_CMPL_CFA_CODE_SHIFT)
#define TPA_START_IS_IPV6(rx_tpa_start) \
(!!((rx_tpa_start)->rx_tpa_start_cmp_flags2 & \
cpu_to_le32(RX_TPA_START_CMP_FLAGS2_IP_TYPE)))
#define TPA_START_ERROR_CODE(rx_tpa_start) \
((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_cfa_code_v2) & \
RX_TPA_START_CMP_ERRORS_BUFFER_ERROR_MASK) >> \
RX_TPA_START_CMP_ERRORS_BUFFER_ERROR_SHIFT)
#define TPA_START_METADATA0_TCI(rx_tpa_start) \
((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_cfa_code_v2) & \
RX_TPA_START_CMP_METADATA0_TCI_MASK) >> \
RX_TPA_START_CMP_METADATA0_SFT)
#define RX_TPA_END_CMP_TYPE GENMASK(5, 0)
#define RX_TPA_END_CMP_FLAGS GENMASK(15, 6)
#define RX_TPA_END_CMP_FLAGS_SHIFT 6
#define RX_TPA_END_CMP_FLAGS_PLACEMENT GENMASK(9, 7)
#define RX_TPA_END_CMP_FLAGS_PLACEMENT_SHIFT 7
#define RX_TPA_END_CMP_FLAGS_PLACEMENT_JUMBO BIT(7)
#define RX_TPA_END_CMP_FLAGS_PLACEMENT_HDS (0x2 << 7)
#define RX_TPA_END_CMP_FLAGS_PLACEMENT_GRO_JUMBO (0x5 << 7)
#define RX_TPA_END_CMP_FLAGS_PLACEMENT_GRO_HDS (0x6 << 7)
#define RX_TPA_END_CMP_FLAGS_RSS_VALID BIT(10)
#define RX_TPA_END_CMP_FLAGS_ITYPES GENMASK(15, 12)
#define RX_TPA_END_CMP_FLAGS_ITYPES_SHIFT 12
#define RX_TPA_END_CMP_FLAGS_ITYPE_TCP (0x2 << 12)
#define RX_TPA_END_CMP_LEN GENMASK(31, 16)
#define RX_TPA_END_CMP_LEN_SHIFT 16
#define RX_TPA_END_CMP_V1 BIT(0)
#define RX_TPA_END_CMP_TPA_SEGS GENMASK(15, 8)
#define RX_TPA_END_CMP_TPA_SEGS_SHIFT 8
#define RX_TPA_END_CMP_AGG_ID GENMASK(25, 16)
#define RX_TPA_END_CMP_AGG_ID_SHIFT 16
#define RX_TPA_END_GRO_TS BIT(31)
struct rx_tpa_end_cmp {
__le32 rx_tpa_end_cmp_len_flags_type;
u32 rx_tpa_end_cmp_opaque;
__le32 rx_tpa_end_cmp_misc_v1;
__le32 rx_tpa_end_cmp_tsdelta;
};
#define TPA_END_AGG_ID(rx_tpa_end) \
((le32_to_cpu((rx_tpa_end)->rx_tpa_end_cmp_misc_v1) & \
RX_TPA_END_CMP_AGG_ID) >> RX_TPA_END_CMP_AGG_ID_SHIFT)
#define TPA_END_TPA_SEGS(rx_tpa_end) \
((le32_to_cpu((rx_tpa_end)->rx_tpa_end_cmp_misc_v1) & \
RX_TPA_END_CMP_TPA_SEGS) >> RX_TPA_END_CMP_TPA_SEGS_SHIFT)
#define RX_TPA_END_CMP_FLAGS_PLACEMENT_ANY_GRO \
cpu_to_le32(RX_TPA_END_CMP_FLAGS_PLACEMENT_GRO_JUMBO & \
RX_TPA_END_CMP_FLAGS_PLACEMENT_GRO_HDS)
#define TPA_END_GRO(rx_tpa_end) \
((rx_tpa_end)->rx_tpa_end_cmp_len_flags_type & \
RX_TPA_END_CMP_FLAGS_PLACEMENT_ANY_GRO)
#define TPA_END_GRO_TS(rx_tpa_end) \
(!!((rx_tpa_end)->rx_tpa_end_cmp_tsdelta & \
cpu_to_le32(RX_TPA_END_GRO_TS)))
#define RX_TPA_END_CMP_TPA_DUP_ACKS GENMASK(3, 0)
#define RX_TPA_END_CMP_PAYLOAD_OFFSET GENMASK(23, 16)
#define RX_TPA_END_CMP_PAYLOAD_OFFSET_SHIFT 16
#define RX_TPA_END_CMP_AGG_BUFS GENMASK(31, 24)
#define RX_TPA_END_CMP_AGG_BUFS_SHIFT 24
#define RX_TPA_END_CMP_TPA_SEG_LEN GENMASK(15, 0)
#define RX_TPA_END_CMP_V2 BIT(0)
#define RX_TPA_END_CMP_ERRORS GENMASK(2, 1)
#define RX_TPA_END_CMPL_ERRORS_SHIFT 1
#define RX_TPA_END_CMP_ERRORS_BUFFER_ERROR_NO_BUFFER (0x0 << 1)
#define RX_TPA_END_CMP_ERRORS_BUFFER_ERROR_NOT_ON_CHIP (0x2 << 1)
#define RX_TPA_END_CMP_ERRORS_BUFFER_ERROR_BAD_FORMAT (0x3 << 1)
#define RX_TPA_END_CMP_ERRORS_BUFFER_ERROR_RSV_ERROR (0x4 << 1)
#define RX_TPA_END_CMP_ERRORS_BUFFER_ERROR_FLUSH (0x5 << 1)
struct rx_tpa_end_cmp_ext {
__le32 rx_tpa_end_cmp_dup_acks;
__le32 rx_tpa_end_cmp_seg_len;
__le32 rx_tpa_end_cmp_errors_v2;
u32 rx_tpa_end_cmp_start_opaque;
};
#define TPA_END_ERRORS(rx_tpa_end_ext) \
((rx_tpa_end_ext)->rx_tpa_end_cmp_errors_v2 & \
cpu_to_le32(RX_TPA_END_CMP_ERRORS))
#define TPA_END_PAYLOAD_OFF(rx_tpa_end_ext) \
((le32_to_cpu((rx_tpa_end_ext)->rx_tpa_end_cmp_dup_acks) & \
RX_TPA_END_CMP_PAYLOAD_OFFSET) >> \
RX_TPA_END_CMP_PAYLOAD_OFFSET_SHIFT)
#define TPA_END_AGG_BUFS(rx_tpa_end_ext) \
((le32_to_cpu((rx_tpa_end_ext)->rx_tpa_end_cmp_dup_acks) & \
RX_TPA_END_CMP_AGG_BUFS) >> RX_TPA_END_CMP_AGG_BUFS_SHIFT)
#define EVENT_DATA1_RESET_NOTIFY_FATAL(data1) \
(((data1) & \
ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_MASK) ==\
ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_FATAL)
#define EVENT_DATA1_RESET_NOTIFY_FW_ACTIVATION(data1) \
(((data1) & \
ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_MASK) ==\
ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_ACTIVATION)
#define EVENT_DATA2_RESET_NOTIFY_FW_STATUS_CODE(data2) \
((data2) & \
ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA2_FW_STATUS_CODE_MASK)
#define EVENT_DATA1_RECOVERY_MASTER_FUNC(data1) \
(!!((data1) & \
ASYNC_EVENT_CMPL_ERROR_RECOVERY_EVENT_DATA1_FLAGS_MASTER_FUNC))
#define EVENT_DATA1_RECOVERY_ENABLED(data1) \
(!!((data1) & \
ASYNC_EVENT_CMPL_ERROR_RECOVERY_EVENT_DATA1_FLAGS_RECOVERY_ENABLED))
#define BNGE_EVENT_ERROR_REPORT_TYPE(data1) \
(((data1) & \
ASYNC_EVENT_CMPL_ERROR_REPORT_BASE_EVENT_DATA1_ERROR_TYPE_MASK) >>\
ASYNC_EVENT_CMPL_ERROR_REPORT_BASE_EVENT_DATA1_ERROR_TYPE_SFT)
#define BNGE_EVENT_INVALID_SIGNAL_DATA(data2) \
(((data2) & \
ASYNC_EVENT_CMPL_ERROR_REPORT_INVALID_SIGNAL_EVENT_DATA2_PIN_ID_MASK) >>\
ASYNC_EVENT_CMPL_ERROR_REPORT_INVALID_SIGNAL_EVENT_DATA2_PIN_ID_SFT)
#endif /* _BNGE_HW_DEF_H_ */
@@ -0,0 +1,548 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Broadcom.
#include <asm/byteorder.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pci.h>
#include "bnge.h"
#include "bnge_hwrm.h"
static u64 bnge_cal_sentinel(struct bnge_hwrm_ctx *ctx, u16 req_type)
{
return (((uintptr_t)ctx) + req_type) ^ BNGE_HWRM_SENTINEL;
}
int bnge_hwrm_req_create(struct bnge_dev *bd, void **req, u16 req_type,
u32 req_len)
{
struct bnge_hwrm_ctx *ctx;
dma_addr_t dma_handle;
u8 *req_addr;
if (req_len > BNGE_HWRM_CTX_OFFSET)
return -E2BIG;
req_addr = dma_pool_alloc(bd->hwrm_dma_pool, GFP_KERNEL | __GFP_ZERO,
&dma_handle);
if (!req_addr)
return -ENOMEM;
ctx = (struct bnge_hwrm_ctx *)(req_addr + BNGE_HWRM_CTX_OFFSET);
/* safety first, sentinel used to check for invalid requests */
ctx->sentinel = bnge_cal_sentinel(ctx, req_type);
ctx->req_len = req_len;
ctx->req = (struct input *)req_addr;
ctx->resp = (struct output *)(req_addr + BNGE_HWRM_RESP_OFFSET);
ctx->dma_handle = dma_handle;
ctx->flags = 0; /* __GFP_ZERO, but be explicit regarding ownership */
ctx->timeout = bd->hwrm_cmd_timeout ?: BNGE_DFLT_HWRM_CMD_TIMEOUT;
ctx->allocated = BNGE_HWRM_DMA_SIZE - BNGE_HWRM_CTX_OFFSET;
ctx->gfp = GFP_KERNEL;
ctx->slice_addr = NULL;
/* initialize common request fields */
ctx->req->req_type = cpu_to_le16(req_type);
ctx->req->resp_addr = cpu_to_le64(dma_handle + BNGE_HWRM_RESP_OFFSET);
ctx->req->cmpl_ring = cpu_to_le16(BNGE_HWRM_NO_CMPL_RING);
ctx->req->target_id = cpu_to_le16(BNGE_HWRM_TARGET);
*req = ctx->req;
return 0;
}
static struct bnge_hwrm_ctx *__hwrm_ctx_get(struct bnge_dev *bd, u8 *req_addr)
{
void *ctx_addr = req_addr + BNGE_HWRM_CTX_OFFSET;
struct input *req = (struct input *)req_addr;
struct bnge_hwrm_ctx *ctx = ctx_addr;
u64 sentinel;
if (!req) {
dev_err(bd->dev, "null HWRM request");
dump_stack();
return NULL;
}
/* HWRM API has no type safety, verify sentinel to validate address */
sentinel = bnge_cal_sentinel(ctx, le16_to_cpu(req->req_type));
if (ctx->sentinel != sentinel) {
dev_err(bd->dev, "HWRM sentinel mismatch, req_type = %u\n",
(u32)le16_to_cpu(req->req_type));
dump_stack();
return NULL;
}
return ctx;
}
void bnge_hwrm_req_timeout(struct bnge_dev *bd,
void *req, unsigned int timeout)
{
struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
if (ctx)
ctx->timeout = timeout;
}
void bnge_hwrm_req_alloc_flags(struct bnge_dev *bd, void *req, gfp_t gfp)
{
struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
if (ctx)
ctx->gfp = gfp;
}
int bnge_hwrm_req_replace(struct bnge_dev *bd, void *req, void *new_req,
u32 len)
{
struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
struct input *internal_req = req;
u16 req_type;
if (!ctx)
return -EINVAL;
if (len > BNGE_HWRM_CTX_OFFSET)
return -E2BIG;
/* free any existing slices */
ctx->allocated = BNGE_HWRM_DMA_SIZE - BNGE_HWRM_CTX_OFFSET;
if (ctx->slice_addr) {
dma_free_coherent(bd->dev, ctx->slice_size,
ctx->slice_addr, ctx->slice_handle);
ctx->slice_addr = NULL;
}
ctx->gfp = GFP_KERNEL;
if ((bd->fw_cap & BNGE_FW_CAP_SHORT_CMD) || len > BNGE_HWRM_MAX_REQ_LEN) {
memcpy(internal_req, new_req, len);
} else {
internal_req->req_type = ((struct input *)new_req)->req_type;
ctx->req = new_req;
}
ctx->req_len = len;
ctx->req->resp_addr = cpu_to_le64(ctx->dma_handle +
BNGE_HWRM_RESP_OFFSET);
/* update sentinel for potentially new request type */
req_type = le16_to_cpu(internal_req->req_type);
ctx->sentinel = bnge_cal_sentinel(ctx, req_type);
return 0;
}
void bnge_hwrm_req_flags(struct bnge_dev *bd, void *req,
enum bnge_hwrm_ctx_flags flags)
{
struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
if (ctx)
ctx->flags |= (flags & BNGE_HWRM_API_FLAGS);
}
void *bnge_hwrm_req_hold(struct bnge_dev *bd, void *req)
{
struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
struct input *input = (struct input *)req;
if (!ctx)
return NULL;
if (ctx->flags & BNGE_HWRM_INTERNAL_CTX_OWNED) {
dev_err(bd->dev, "HWRM context already owned, req_type = %u\n",
(u32)le16_to_cpu(input->req_type));
dump_stack();
return NULL;
}
ctx->flags |= BNGE_HWRM_INTERNAL_CTX_OWNED;
return ((u8 *)req) + BNGE_HWRM_RESP_OFFSET;
}
static void __hwrm_ctx_invalidate(struct bnge_dev *bd,
struct bnge_hwrm_ctx *ctx)
{
void *addr = ((u8 *)ctx) - BNGE_HWRM_CTX_OFFSET;
dma_addr_t dma_handle = ctx->dma_handle; /* save before invalidate */
/* unmap any auxiliary DMA slice */
if (ctx->slice_addr)
dma_free_coherent(bd->dev, ctx->slice_size,
ctx->slice_addr, ctx->slice_handle);
/* invalidate, ensure ownership, sentinel and dma_handle are cleared */
memset(ctx, 0, sizeof(struct bnge_hwrm_ctx));
/* return the buffer to the DMA pool */
if (dma_handle)
dma_pool_free(bd->hwrm_dma_pool, addr, dma_handle);
}
void bnge_hwrm_req_drop(struct bnge_dev *bd, void *req)
{
struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
if (ctx)
__hwrm_ctx_invalidate(bd, ctx);
}
static int bnge_map_hwrm_error(u32 hwrm_err)
{
switch (hwrm_err) {
case HWRM_ERR_CODE_SUCCESS:
return 0;
case HWRM_ERR_CODE_RESOURCE_LOCKED:
return -EROFS;
case HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED:
return -EACCES;
case HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR:
return -ENOSPC;
case HWRM_ERR_CODE_INVALID_PARAMS:
case HWRM_ERR_CODE_INVALID_FLAGS:
case HWRM_ERR_CODE_INVALID_ENABLES:
case HWRM_ERR_CODE_UNSUPPORTED_TLV:
case HWRM_ERR_CODE_UNSUPPORTED_OPTION_ERR:
return -EINVAL;
case HWRM_ERR_CODE_NO_BUFFER:
return -ENOMEM;
case HWRM_ERR_CODE_HOT_RESET_PROGRESS:
case HWRM_ERR_CODE_BUSY:
return -EAGAIN;
case HWRM_ERR_CODE_CMD_NOT_SUPPORTED:
return -EOPNOTSUPP;
case HWRM_ERR_CODE_PF_UNAVAILABLE:
return -ENODEV;
default:
return -EIO;
}
}
static struct bnge_hwrm_wait_token *
bnge_hwrm_create_token(struct bnge_dev *bd, enum bnge_hwrm_chnl dst)
{
struct bnge_hwrm_wait_token *token;
token = kzalloc_obj(*token);
if (!token)
return NULL;
mutex_lock(&bd->hwrm_cmd_lock);
token->dst = dst;
token->state = BNGE_HWRM_PENDING;
if (dst == BNGE_HWRM_CHNL_CHIMP) {
token->seq_id = bd->hwrm_cmd_seq++;
hlist_add_head_rcu(&token->node, &bd->hwrm_pending_list);
} else {
token->seq_id = bd->hwrm_cmd_kong_seq++;
}
return token;
}
static void
bnge_hwrm_destroy_token(struct bnge_dev *bd, struct bnge_hwrm_wait_token *token)
{
if (token->dst == BNGE_HWRM_CHNL_CHIMP) {
hlist_del_rcu(&token->node);
kfree_rcu(token, rcu);
} else {
kfree(token);
}
mutex_unlock(&bd->hwrm_cmd_lock);
}
static void bnge_hwrm_req_dbg(struct bnge_dev *bd, struct input *req)
{
u32 ring = le16_to_cpu(req->cmpl_ring);
u32 type = le16_to_cpu(req->req_type);
u32 tgt = le16_to_cpu(req->target_id);
u32 seq = le16_to_cpu(req->seq_id);
char opt[32] = "\n";
if (unlikely(ring != (u16)BNGE_HWRM_NO_CMPL_RING))
snprintf(opt, 16, " ring %d\n", ring);
if (unlikely(tgt != BNGE_HWRM_TARGET))
snprintf(opt + strlen(opt) - 1, 16, " tgt 0x%x\n", tgt);
dev_dbg(bd->dev, "sent hwrm req_type 0x%x seq id 0x%x%s",
type, seq, opt);
}
#define bnge_hwrm_err(bd, ctx, fmt, ...) \
do { \
if ((ctx)->flags & BNGE_HWRM_CTX_SILENT) \
dev_dbg((bd)->dev, fmt, __VA_ARGS__); \
else \
dev_err((bd)->dev, fmt, __VA_ARGS__); \
} while (0)
static int __hwrm_send_ctx(struct bnge_dev *bd, struct bnge_hwrm_ctx *ctx)
{
u32 doorbell_offset = BNGE_GRCPF_REG_CHIMP_COMM_TRIGGER;
enum bnge_hwrm_chnl dst = BNGE_HWRM_CHNL_CHIMP;
u32 bar_offset = BNGE_GRCPF_REG_CHIMP_COMM;
struct bnge_hwrm_wait_token *token = NULL;
u16 max_req_len = BNGE_HWRM_MAX_REQ_LEN;
unsigned int i, timeout, tmo_count;
u32 *data = (u32 *)ctx->req;
u32 msg_len = ctx->req_len;
int rc = -EBUSY;
u32 req_type;
u16 len = 0;
u8 *valid;
if (ctx->flags & BNGE_HWRM_INTERNAL_RESP_DIRTY)
memset(ctx->resp, 0, PAGE_SIZE);
req_type = le16_to_cpu(ctx->req->req_type);
if (msg_len > BNGE_HWRM_MAX_REQ_LEN &&
msg_len > bd->hwrm_max_ext_req_len) {
dev_warn(bd->dev, "oversized hwrm request, req_type 0x%x",
req_type);
rc = -E2BIG;
goto exit;
}
token = bnge_hwrm_create_token(bd, dst);
if (!token) {
rc = -ENOMEM;
goto exit;
}
ctx->req->seq_id = cpu_to_le16(token->seq_id);
/* Ensure any associated DMA buffers are written before doorbell */
wmb();
/* Write request msg to hwrm channel */
__iowrite32_copy(bd->bar0 + bar_offset, data, msg_len / 4);
for (i = msg_len; i < max_req_len; i += 4)
writel(0, bd->bar0 + bar_offset + i);
/* Ring channel doorbell */
writel(1, bd->bar0 + doorbell_offset);
bnge_hwrm_req_dbg(bd, ctx->req);
/* Limit timeout to an upper limit */
timeout = min(ctx->timeout,
bd->hwrm_cmd_max_timeout ?: BNGE_HWRM_CMD_MAX_TIMEOUT);
/* convert timeout to usec */
timeout *= 1000;
i = 0;
/* Short timeout for the first few iterations:
* number of loops = number of loops for short timeout +
* number of loops for standard timeout.
*/
tmo_count = BNGE_HWRM_SHORT_TIMEOUT_COUNTER;
timeout = timeout - BNGE_HWRM_SHORT_MIN_TIMEOUT *
BNGE_HWRM_SHORT_TIMEOUT_COUNTER;
tmo_count += DIV_ROUND_UP(timeout, BNGE_HWRM_MIN_TIMEOUT);
if (le16_to_cpu(ctx->req->cmpl_ring) != INVALID_HW_RING_ID) {
/* Wait until hwrm response cmpl interrupt is processed */
while (READ_ONCE(token->state) < BNGE_HWRM_COMPLETE &&
i++ < tmo_count) {
/* on first few passes, just barely sleep */
if (i < BNGE_HWRM_SHORT_TIMEOUT_COUNTER) {
usleep_range(BNGE_HWRM_SHORT_MIN_TIMEOUT,
BNGE_HWRM_SHORT_MAX_TIMEOUT);
} else {
usleep_range(BNGE_HWRM_MIN_TIMEOUT,
BNGE_HWRM_MAX_TIMEOUT);
}
}
if (READ_ONCE(token->state) != BNGE_HWRM_COMPLETE) {
bnge_hwrm_err(bd, ctx, "No hwrm cmpl received: 0x%x\n",
req_type);
goto exit;
}
len = le16_to_cpu(READ_ONCE(ctx->resp->resp_len));
valid = ((u8 *)ctx->resp) + len - 1;
} else {
__le16 seen_out_of_seq = ctx->req->seq_id; /* will never see */
int j;
/* Check if response len is updated */
for (i = 0; i < tmo_count; i++) {
if (token &&
READ_ONCE(token->state) == BNGE_HWRM_DEFERRED) {
bnge_hwrm_destroy_token(bd, token);
token = NULL;
}
len = le16_to_cpu(READ_ONCE(ctx->resp->resp_len));
if (len) {
__le16 resp_seq = READ_ONCE(ctx->resp->seq_id);
if (resp_seq == ctx->req->seq_id)
break;
if (resp_seq != seen_out_of_seq) {
dev_warn(bd->dev, "Discarding out of seq response: 0x%x for msg {0x%x 0x%x}\n",
le16_to_cpu(resp_seq), req_type, le16_to_cpu(ctx->req->seq_id));
seen_out_of_seq = resp_seq;
}
}
/* on first few passes, just barely sleep */
if (i < BNGE_HWRM_SHORT_TIMEOUT_COUNTER) {
usleep_range(BNGE_HWRM_SHORT_MIN_TIMEOUT,
BNGE_HWRM_SHORT_MAX_TIMEOUT);
} else {
usleep_range(BNGE_HWRM_MIN_TIMEOUT,
BNGE_HWRM_MAX_TIMEOUT);
}
}
if (i >= tmo_count) {
bnge_hwrm_err(bd, ctx,
"Error (timeout: %u) msg {0x%x 0x%x} len:%d\n",
bnge_hwrm_timeout(i), req_type,
le16_to_cpu(ctx->req->seq_id), len);
goto exit;
}
/* Last byte of resp contains valid bit */
valid = ((u8 *)ctx->resp) + len - 1;
for (j = 0; j < BNGE_HWRM_FIN_WAIT_USEC; ) {
/* make sure we read from updated DMA memory */
dma_rmb();
if (*valid)
break;
if (j < 10) {
udelay(1);
j++;
} else {
usleep_range(20, 30);
j += 20;
}
}
if (j >= BNGE_HWRM_FIN_WAIT_USEC) {
bnge_hwrm_err(bd, ctx, "Error (timeout: %u) msg {0x%x 0x%x} len:%d v:%d\n",
bnge_hwrm_timeout(i) + j, req_type,
le16_to_cpu(ctx->req->seq_id), len, *valid);
goto exit;
}
}
/* Zero valid bit for compatibility. Valid bit in an older spec
* may become a new field in a newer spec. We must make sure that
* a new field not implemented by old spec will read zero.
*/
*valid = 0;
rc = le16_to_cpu(ctx->resp->error_code);
if (rc == HWRM_ERR_CODE_BUSY && !(ctx->flags & BNGE_HWRM_CTX_SILENT))
dev_warn(bd->dev, "FW returned busy, hwrm req_type 0x%x\n",
req_type);
else if (rc && rc != HWRM_ERR_CODE_PF_UNAVAILABLE)
bnge_hwrm_err(bd, ctx, "hwrm req_type 0x%x seq id 0x%x error %d\n",
req_type, le16_to_cpu(ctx->req->seq_id), rc);
rc = bnge_map_hwrm_error(rc);
exit:
if (token)
bnge_hwrm_destroy_token(bd, token);
if (ctx->flags & BNGE_HWRM_INTERNAL_CTX_OWNED)
ctx->flags |= BNGE_HWRM_INTERNAL_RESP_DIRTY;
else
__hwrm_ctx_invalidate(bd, ctx);
return rc;
}
int bnge_hwrm_req_send(struct bnge_dev *bd, void *req)
{
struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
if (!ctx)
return -EINVAL;
return __hwrm_send_ctx(bd, ctx);
}
int bnge_hwrm_req_send_silent(struct bnge_dev *bd, void *req)
{
bnge_hwrm_req_flags(bd, req, BNGE_HWRM_CTX_SILENT);
return bnge_hwrm_req_send(bd, req);
}
void *
bnge_hwrm_req_dma_slice(struct bnge_dev *bd, void *req, u32 size,
dma_addr_t *dma_handle)
{
struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
u8 *end = ((u8 *)req) + BNGE_HWRM_DMA_SIZE;
struct input *input = req;
u8 *addr, *req_addr = req;
u32 max_offset, offset;
if (!ctx)
return NULL;
max_offset = BNGE_HWRM_DMA_SIZE - ctx->allocated;
offset = max_offset - size;
offset = ALIGN_DOWN(offset, BNGE_HWRM_DMA_ALIGN);
addr = req_addr + offset;
if (addr < req_addr + max_offset && req_addr + ctx->req_len <= addr) {
ctx->allocated = end - addr;
*dma_handle = ctx->dma_handle + offset;
return addr;
}
if (ctx->slice_addr) {
dev_err(bd->dev, "HWRM refusing to reallocate DMA slice, req_type = %u\n",
(u32)le16_to_cpu(input->req_type));
dump_stack();
return NULL;
}
addr = dma_alloc_coherent(bd->dev, size, dma_handle, ctx->gfp);
if (!addr)
return NULL;
ctx->slice_addr = addr;
ctx->slice_size = size;
ctx->slice_handle = *dma_handle;
return addr;
}
void bnge_cleanup_hwrm_resources(struct bnge_dev *bd)
{
struct bnge_hwrm_wait_token *token;
dma_pool_destroy(bd->hwrm_dma_pool);
bd->hwrm_dma_pool = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(token, &bd->hwrm_pending_list, node)
WRITE_ONCE(token->state, BNGE_HWRM_CANCELLED);
rcu_read_unlock();
}
int bnge_init_hwrm_resources(struct bnge_dev *bd)
{
bd->hwrm_dma_pool = dma_pool_create("bnge_hwrm", bd->dev,
BNGE_HWRM_DMA_SIZE,
BNGE_HWRM_DMA_ALIGN, 0);
if (!bd->hwrm_dma_pool)
return -ENOMEM;
INIT_HLIST_HEAD(&bd->hwrm_pending_list);
mutex_init(&bd->hwrm_cmd_lock);
return 0;
}
@@ -0,0 +1,112 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_HWRM_H_
#define _BNGE_HWRM_H_
#include <linux/bnge/hsi.h>
enum bnge_hwrm_ctx_flags {
BNGE_HWRM_INTERNAL_CTX_OWNED = BIT(0),
BNGE_HWRM_INTERNAL_RESP_DIRTY = BIT(1),
BNGE_HWRM_CTX_SILENT = BIT(2),
BNGE_HWRM_FULL_WAIT = BIT(3),
};
#define BNGE_HWRM_API_FLAGS (BNGE_HWRM_CTX_SILENT | BNGE_HWRM_FULL_WAIT)
struct bnge_hwrm_ctx {
u64 sentinel;
dma_addr_t dma_handle;
struct output *resp;
struct input *req;
dma_addr_t slice_handle;
void *slice_addr;
u32 slice_size;
u32 req_len;
enum bnge_hwrm_ctx_flags flags;
unsigned int timeout;
u32 allocated;
gfp_t gfp;
};
enum bnge_hwrm_wait_state {
BNGE_HWRM_PENDING,
BNGE_HWRM_DEFERRED,
BNGE_HWRM_COMPLETE,
BNGE_HWRM_CANCELLED,
};
enum bnge_hwrm_chnl { BNGE_HWRM_CHNL_CHIMP, BNGE_HWRM_CHNL_KONG };
struct bnge_hwrm_wait_token {
struct rcu_head rcu;
struct hlist_node node;
enum bnge_hwrm_wait_state state;
enum bnge_hwrm_chnl dst;
u16 seq_id;
};
#define BNGE_DFLT_HWRM_CMD_TIMEOUT 500
#define BNGE_GRCPF_REG_CHIMP_COMM 0x0
#define BNGE_GRCPF_REG_CHIMP_COMM_TRIGGER 0x100
#define BNGE_HWRM_MAX_REQ_LEN (bd->hwrm_max_req_len)
#define BNGE_HWRM_SHORT_REQ_LEN sizeof(struct hwrm_short_input)
#define BNGE_HWRM_CMD_MAX_TIMEOUT 40000U
#define BNGE_SHORT_HWRM_CMD_TIMEOUT 20
#define BNGE_HWRM_CMD_TIMEOUT (bd->hwrm_cmd_timeout)
#define BNGE_HWRM_RESET_TIMEOUT ((BNGE_HWRM_CMD_TIMEOUT) * 4)
#define BNGE_HWRM_TARGET 0xffff
#define BNGE_HWRM_NO_CMPL_RING -1
#define BNGE_HWRM_REQ_MAX_SIZE 128
#define BNGE_HWRM_DMA_SIZE (2 * PAGE_SIZE) /* space for req+resp */
#define BNGE_HWRM_RESP_RESERVED PAGE_SIZE
#define BNGE_HWRM_RESP_OFFSET (BNGE_HWRM_DMA_SIZE - \
BNGE_HWRM_RESP_RESERVED)
#define BNGE_HWRM_CTX_OFFSET (BNGE_HWRM_RESP_OFFSET - \
sizeof(struct bnge_hwrm_ctx))
#define BNGE_HWRM_DMA_ALIGN 16
#define BNGE_HWRM_SENTINEL 0xb6e1f68a12e9a7eb /* arbitrary value */
#define BNGE_HWRM_SHORT_MIN_TIMEOUT 3
#define BNGE_HWRM_SHORT_MAX_TIMEOUT 10
#define BNGE_HWRM_SHORT_TIMEOUT_COUNTER 5
#define BNGE_HWRM_MIN_TIMEOUT 25
#define BNGE_HWRM_MAX_TIMEOUT 40
static inline unsigned int bnge_hwrm_timeout(unsigned int n)
{
return n <= BNGE_HWRM_SHORT_TIMEOUT_COUNTER ?
n * BNGE_HWRM_SHORT_MIN_TIMEOUT :
BNGE_HWRM_SHORT_TIMEOUT_COUNTER *
BNGE_HWRM_SHORT_MIN_TIMEOUT +
(n - BNGE_HWRM_SHORT_TIMEOUT_COUNTER) *
BNGE_HWRM_MIN_TIMEOUT;
}
#define BNGE_HWRM_FIN_WAIT_USEC 50000
void bnge_cleanup_hwrm_resources(struct bnge_dev *bd);
int bnge_init_hwrm_resources(struct bnge_dev *bd);
int bnge_hwrm_req_create(struct bnge_dev *bd, void **req, u16 req_type,
u32 req_len);
#define bnge_hwrm_req_init(bd, req, req_type) \
bnge_hwrm_req_create((bd), (void **)&(req), (req_type), \
sizeof(*(req)))
void *bnge_hwrm_req_hold(struct bnge_dev *bd, void *req);
void bnge_hwrm_req_drop(struct bnge_dev *bd, void *req);
void bnge_hwrm_req_flags(struct bnge_dev *bd, void *req,
enum bnge_hwrm_ctx_flags flags);
void bnge_hwrm_req_timeout(struct bnge_dev *bd, void *req,
unsigned int timeout);
int bnge_hwrm_req_send(struct bnge_dev *bd, void *req);
int bnge_hwrm_req_send_silent(struct bnge_dev *bd, void *req);
void bnge_hwrm_req_alloc_flags(struct bnge_dev *bd, void *req, gfp_t flags);
void *bnge_hwrm_req_dma_slice(struct bnge_dev *bd, void *req, u32 size,
dma_addr_t *dma);
int bnge_hwrm_req_replace(struct bnge_dev *bd, void *req, void *new_req,
u32 len);
#endif /* _BNGE_HWRM_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_HWRM_LIB_H_
#define _BNGE_HWRM_LIB_H_
#define BNGE_PLC_EN_JUMBO_THRES_VALID \
VNIC_PLCMODES_CFG_REQ_ENABLES_JUMBO_THRESH_VALID
#define BNGE_PLC_EN_HDS_THRES_VALID \
VNIC_PLCMODES_CFG_REQ_ENABLES_HDS_THRESHOLD_VALID
#define BNGE_VNIC_CFG_ROCE_DUAL_MODE \
VNIC_CFG_REQ_FLAGS_ROCE_DUAL_VNIC_MODE
int bnge_hwrm_ver_get(struct bnge_dev *bd);
int bnge_hwrm_func_reset(struct bnge_dev *bd);
int bnge_hwrm_fw_set_time(struct bnge_dev *bd);
int bnge_hwrm_func_drv_rgtr(struct bnge_dev *bd);
int bnge_hwrm_func_drv_unrgtr(struct bnge_dev *bd);
int bnge_hwrm_vnic_qcaps(struct bnge_dev *bd);
int bnge_hwrm_nvm_dev_info(struct bnge_dev *bd,
struct hwrm_nvm_get_dev_info_output *nvm_dev_info);
int bnge_hwrm_func_backing_store(struct bnge_dev *bd,
struct bnge_ctx_mem_type *ctxm,
bool last);
int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd);
int bnge_hwrm_reserve_rings(struct bnge_dev *bd,
struct bnge_hw_rings *hwr);
int bnge_hwrm_func_qcaps(struct bnge_dev *bd);
int bnge_hwrm_vnic_qcaps(struct bnge_dev *bd);
int bnge_hwrm_func_qcfg(struct bnge_dev *bd);
int bnge_hwrm_func_resc_qcaps(struct bnge_dev *bd);
int bnge_hwrm_queue_qportcfg(struct bnge_dev *bd);
int bnge_hwrm_vnic_set_hds(struct bnge_net *bn, struct bnge_vnic_info *vnic);
int bnge_hwrm_vnic_ctx_alloc(struct bnge_dev *bd,
struct bnge_vnic_info *vnic, u16 ctx_idx);
int bnge_hwrm_vnic_set_rss(struct bnge_net *bn,
struct bnge_vnic_info *vnic, bool set_rss);
int bnge_hwrm_vnic_cfg(struct bnge_net *bn, struct bnge_vnic_info *vnic);
void bnge_hwrm_update_rss_hash_cfg(struct bnge_net *bn);
int bnge_hwrm_vnic_alloc(struct bnge_dev *bd, struct bnge_vnic_info *vnic,
unsigned int nr_rings);
void bnge_hwrm_vnic_free_one(struct bnge_dev *bd, struct bnge_vnic_info *vnic);
void bnge_hwrm_vnic_ctx_free_one(struct bnge_dev *bd,
struct bnge_vnic_info *vnic, u16 ctx_idx);
int bnge_hwrm_l2_filter_free(struct bnge_dev *bd, struct bnge_l2_filter *fltr);
int bnge_hwrm_l2_filter_alloc(struct bnge_dev *bd, struct bnge_l2_filter *fltr);
int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd,
struct bnge_vnic_info *vnic);
void bnge_hwrm_stat_ctx_free(struct bnge_net *bn);
int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn);
int hwrm_ring_free_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring,
u32 ring_type, u32 cmpl_ring_id);
int hwrm_ring_alloc_send_msg(struct bnge_net *bn,
struct bnge_ring_struct *ring,
u32 ring_type, u32 map_index);
int bnge_hwrm_set_async_event_cr(struct bnge_dev *bd, int idx);
int bnge_hwrm_vnic_set_tpa(struct bnge_dev *bd, struct bnge_vnic_info *vnic,
u32 tpa_flags);
int bnge_update_link(struct bnge_net *bn, bool chng_link_state);
int bnge_hwrm_phy_qcaps(struct bnge_dev *bd);
int bnge_hwrm_set_link_setting(struct bnge_net *bn, bool set_pause);
int bnge_hwrm_set_pause(struct bnge_net *bn);
int bnge_hwrm_shutdown_link(struct bnge_dev *bd);
int bnge_hwrm_port_qstats(struct bnge_dev *bd, u8 flags);
int bnge_hwrm_port_qstats_ext(struct bnge_dev *bd, u8 flags);
int bnge_hwrm_func_qstat_ext(struct bnge_dev *bd, struct bnge_stats_mem *stats);
#endif /* _BNGE_HWRM_LIB_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,173 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2026 Broadcom */
#ifndef _BNGE_LINK_H_
#define _BNGE_LINK_H_
#include <linux/ethtool.h>
#define BNGE_PHY_CFG_ABLE(bd) \
((bd)->link_info.phy_enabled)
#define BNGE_PHY_AUTO_SPEEDS2_MASK \
PORT_PHY_CFG_REQ_ENABLES_AUTO_LINK_SPEEDS2_MASK
#define BNGE_PHY_FLAGS_RESTART_AUTO \
PORT_PHY_CFG_REQ_FLAGS_RESTART_AUTONEG
#define BNGE_PHY_FLAGS_ENA_FORCE_SPEEDS2 \
PORT_PHY_CFG_REQ_ENABLES_FORCE_LINK_SPEEDS2
#define BNGE_LINK_LINK PORT_PHY_QCFG_RESP_LINK_LINK
enum bnge_link_state {
BNGE_LINK_STATE_UNKNOWN,
BNGE_LINK_STATE_DOWN,
BNGE_LINK_STATE_UP,
};
#define BNGE_LINK_IS_UP(bd) \
((bd)->link_info.link_state == BNGE_LINK_STATE_UP)
#define BNGE_LINK_DUPLEX_FULL PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL
#define BNGE_LINK_PAUSE_TX PORT_PHY_QCFG_RESP_PAUSE_TX
#define BNGE_LINK_PAUSE_RX PORT_PHY_QCFG_RESP_PAUSE_RX
#define BNGE_LINK_PAUSE_BOTH (PORT_PHY_QCFG_RESP_PAUSE_RX | \
PORT_PHY_QCFG_RESP_PAUSE_TX)
#define BNGE_LINK_AUTO_NONE PORT_PHY_QCFG_RESP_AUTO_MODE_NONE
#define BNGE_LINK_AUTO_MSK PORT_PHY_QCFG_RESP_AUTO_MODE_SPEED_MASK
#define BNGE_AUTO_MODE(mode) ((mode) > BNGE_LINK_AUTO_NONE && \
(mode) <= BNGE_LINK_AUTO_MSK)
#define BNGE_LINK_SPEED_50GB PORT_PHY_QCFG_RESP_LINK_SPEED_50GB
#define BNGE_LINK_SPEED_100GB PORT_PHY_QCFG_RESP_LINK_SPEED_100GB
#define BNGE_LINK_SPEED_200GB PORT_PHY_QCFG_RESP_LINK_SPEED_200GB
#define BNGE_LINK_SPEED_400GB PORT_PHY_QCFG_RESP_LINK_SPEED_400GB
#define BNGE_LINK_SPEED_800GB PORT_PHY_QCFG_RESP_LINK_SPEED_800GB
#define BNGE_LINK_SPEEDS2_MSK_50GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_50GB
#define BNGE_LINK_SPEEDS2_MSK_100GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_100GB
#define BNGE_LINK_SPEEDS2_MSK_50GB_PAM4 \
PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_50GB_PAM4_56
#define BNGE_LINK_SPEEDS2_MSK_100GB_PAM4 \
PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_100GB_PAM4_56
#define BNGE_LINK_SPEEDS2_MSK_200GB_PAM4 \
PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_200GB_PAM4_56
#define BNGE_LINK_SPEEDS2_MSK_400GB_PAM4 \
PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_400GB_PAM4_56
#define BNGE_LINK_SPEEDS2_MSK_100GB_PAM4_112 \
PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_100GB_PAM4_112
#define BNGE_LINK_SPEEDS2_MSK_200GB_PAM4_112 \
PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_200GB_PAM4_112
#define BNGE_LINK_SPEEDS2_MSK_400GB_PAM4_112 \
PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_400GB_PAM4_112
#define BNGE_LINK_SPEEDS2_MSK_800GB_PAM4_112 \
PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS2_800GB_PAM4_112
#define BNGE_LINK_SPEED_50GB_PAM4 \
PORT_PHY_CFG_REQ_FORCE_LINK_SPEEDS2_50GB_PAM4_56
#define BNGE_LINK_SPEED_100GB_PAM4 \
PORT_PHY_CFG_REQ_FORCE_LINK_SPEEDS2_100GB_PAM4_56
#define BNGE_LINK_SPEED_200GB_PAM4 \
PORT_PHY_CFG_REQ_FORCE_LINK_SPEEDS2_200GB_PAM4_56
#define BNGE_LINK_SPEED_400GB_PAM4 \
PORT_PHY_CFG_REQ_FORCE_LINK_SPEEDS2_400GB_PAM4_56
#define BNGE_LINK_SPEED_100GB_PAM4_112 \
PORT_PHY_CFG_REQ_FORCE_LINK_SPEEDS2_100GB_PAM4_112
#define BNGE_LINK_SPEED_200GB_PAM4_112 \
PORT_PHY_CFG_REQ_FORCE_LINK_SPEEDS2_200GB_PAM4_112
#define BNGE_LINK_SPEED_400GB_PAM4_112 \
PORT_PHY_CFG_REQ_FORCE_LINK_SPEEDS2_400GB_PAM4_112
#define BNGE_LINK_SPEED_800GB_PAM4_112 \
PORT_PHY_CFG_REQ_FORCE_LINK_SPEEDS2_800GB_PAM4_112
#define BNGE_FEC_NONE PORT_PHY_QCFG_RESP_FEC_CFG_FEC_NONE_SUPPORTED
#define BNGE_FEC_AUTONEG PORT_PHY_QCFG_RESP_FEC_CFG_FEC_AUTONEG_ENABLED
#define BNGE_FEC_ENC_BASE_R_CAP \
PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE74_SUPPORTED
#define BNGE_FEC_ENC_BASE_R PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE74_ENABLED
#define BNGE_FEC_ENC_RS_CAP \
PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE91_SUPPORTED
#define BNGE_FEC_ENC_LLRS_CAP \
(PORT_PHY_QCFG_RESP_FEC_CFG_FEC_RS272_1XN_SUPPORTED | \
PORT_PHY_QCFG_RESP_FEC_CFG_FEC_RS272_IEEE_SUPPORTED)
#define BNGE_FEC_ENC_RS \
(PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE91_ENABLED | \
PORT_PHY_QCFG_RESP_FEC_CFG_FEC_RS544_1XN_ENABLED | \
PORT_PHY_QCFG_RESP_FEC_CFG_FEC_RS544_IEEE_ENABLED)
#define BNGE_FEC_ENC_LLRS \
(PORT_PHY_QCFG_RESP_FEC_CFG_FEC_RS272_1XN_ENABLED | \
PORT_PHY_QCFG_RESP_FEC_CFG_FEC_RS272_IEEE_ENABLED)
struct bnge_link_info {
u8 phy_type;
u8 media_type;
u8 phy_addr;
u8 phy_link_status;
bool phy_enabled;
u8 link_state;
u8 active_lanes;
u8 duplex;
u8 pause;
u8 lp_pause;
u8 auto_pause_setting;
u8 force_pause_setting;
u8 duplex_setting;
u8 auto_mode;
u16 link_speed;
u16 support_speeds2;
u16 auto_link_speeds2;
u16 support_auto_speeds2;
u16 lp_auto_link_speeds;
u16 force_link_speed2;
u8 module_status;
u8 active_fec_sig_mode;
u16 fec_cfg;
/* A copy of phy_qcfg output used to report link
* info to VF
*/
struct hwrm_port_phy_qcfg_output phy_qcfg_resp;
bool phy_retry;
unsigned long phy_retry_expires;
};
#define BNGE_AUTONEG_SPEED 1
#define BNGE_AUTONEG_FLOW_CTRL 2
#define BNGE_SIG_MODE_NRZ PORT_PHY_QCFG_RESP_SIGNAL_MODE_NRZ
#define BNGE_SIG_MODE_PAM4 PORT_PHY_QCFG_RESP_SIGNAL_MODE_PAM4
#define BNGE_SIG_MODE_PAM4_112 PORT_PHY_QCFG_RESP_SIGNAL_MODE_PAM4_112
#define BNGE_SIG_MODE_MAX (PORT_PHY_QCFG_RESP_SIGNAL_MODE_LAST + 1)
struct bnge_ethtool_link_info {
/* copy of requested setting from ethtool cmd */
u8 autoneg;
u8 req_signal_mode;
u8 req_duplex;
u8 req_flow_ctrl;
u16 req_link_speed;
u16 advertising; /* user adv setting */
bool force_link_chng;
};
void bnge_hwrm_set_link_common(struct bnge_net *bn,
struct hwrm_port_phy_cfg_input *req);
void bnge_hwrm_set_pause_common(struct bnge_net *bn,
struct hwrm_port_phy_cfg_input *req);
int bnge_update_phy_setting(struct bnge_net *bn);
void bnge_get_port_module_status(struct bnge_net *bn);
void bnge_report_link(struct bnge_dev *bd);
bool bnge_support_speed_dropped(struct bnge_net *bn);
void bnge_init_ethtool_link_settings(struct bnge_net *bn);
int bnge_probe_phy(struct bnge_net *bn, bool fw_dflt);
int bnge_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *lk_ksettings);
int bnge_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *lk_ksettings);
u32 bnge_get_link(struct net_device *dev);
void bnge_link_async_event_process(struct bnge_net *bn, u16 event_id);
#endif /* _BNGE_LINK_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,645 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_NETDEV_H_
#define _BNGE_NETDEV_H_
#include <linux/bnge/hsi.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/refcount.h>
#include <linux/u64_stats_sync.h>
#include <net/netdev_queues.h>
#include "bnge_db.h"
#include "bnge_hw_def.h"
#include "bnge_link.h"
struct tx_bd {
__le32 tx_bd_len_flags_type;
#define TX_BD_TYPE (0x3f << 0)
#define TX_BD_TYPE_SHORT_TX_BD (0x00 << 0)
#define TX_BD_TYPE_LONG_TX_BD (0x10 << 0)
#define TX_BD_FLAGS_PACKET_END (1 << 6)
#define TX_BD_FLAGS_NO_CMPL (1 << 7)
#define TX_BD_FLAGS_BD_CNT (0x1f << 8)
#define TX_BD_FLAGS_BD_CNT_SHIFT 8
#define TX_BD_FLAGS_LHINT (3 << 13)
#define TX_BD_FLAGS_LHINT_SHIFT 13
#define TX_BD_FLAGS_LHINT_512_AND_SMALLER (0 << 13)
#define TX_BD_FLAGS_LHINT_512_TO_1023 (1 << 13)
#define TX_BD_FLAGS_LHINT_1024_TO_2047 (2 << 13)
#define TX_BD_FLAGS_LHINT_2048_AND_LARGER (3 << 13)
#define TX_BD_FLAGS_COAL_NOW (1 << 15)
#define TX_BD_LEN (0xffff << 16)
#define TX_BD_LEN_SHIFT 16
u32 tx_bd_opaque;
__le64 tx_bd_haddr;
} __packed;
struct rx_bd {
__le32 rx_bd_len_flags_type;
#define RX_BD_TYPE (0x3f << 0)
#define RX_BD_TYPE_RX_PACKET_BD 0x4
#define RX_BD_TYPE_RX_BUFFER_BD 0x5
#define RX_BD_TYPE_RX_AGG_BD 0x6
#define RX_BD_TYPE_16B_BD_SIZE (0 << 4)
#define RX_BD_TYPE_32B_BD_SIZE (1 << 4)
#define RX_BD_TYPE_48B_BD_SIZE (2 << 4)
#define RX_BD_TYPE_64B_BD_SIZE (3 << 4)
#define RX_BD_FLAGS_SOP (1 << 6)
#define RX_BD_FLAGS_EOP (1 << 7)
#define RX_BD_FLAGS_BUFFERS (3 << 8)
#define RX_BD_FLAGS_1_BUFFER_PACKET (0 << 8)
#define RX_BD_FLAGS_2_BUFFER_PACKET (1 << 8)
#define RX_BD_FLAGS_3_BUFFER_PACKET (2 << 8)
#define RX_BD_FLAGS_4_BUFFER_PACKET (3 << 8)
#define RX_BD_LEN (0xffff << 16)
#define RX_BD_LEN_SHIFT 16
u32 rx_bd_opaque;
__le64 rx_bd_haddr;
};
struct tx_cmp {
__le32 tx_cmp_flags_type;
#define CMP_TYPE (0x3f << 0)
#define CMP_TYPE_TX_L2_CMP 0
#define CMP_TYPE_TX_L2_COAL_CMP 2
#define CMP_TYPE_TX_L2_PKT_TS_CMP 4
#define CMP_TYPE_RX_L2_CMP 17
#define CMP_TYPE_RX_AGG_CMP 18
#define CMP_TYPE_RX_L2_TPA_START_CMP 19
#define CMP_TYPE_RX_L2_TPA_END_CMP 21
#define CMP_TYPE_RX_TPA_AGG_CMP 22
#define CMP_TYPE_RX_L2_V3_CMP 23
#define CMP_TYPE_RX_L2_TPA_START_V3_CMP 25
#define CMP_TYPE_STATUS_CMP 32
#define CMP_TYPE_REMOTE_DRIVER_REQ 34
#define CMP_TYPE_REMOTE_DRIVER_RESP 36
#define CMP_TYPE_ERROR_STATUS 48
#define CMPL_BASE_TYPE_STAT_EJECT 0x1aUL
#define CMPL_BASE_TYPE_HWRM_DONE 0x20UL
#define CMPL_BASE_TYPE_HWRM_FWD_REQ 0x22UL
#define CMPL_BASE_TYPE_HWRM_FWD_RESP 0x24UL
#define CMPL_BASE_TYPE_HWRM_ASYNC_EVENT 0x2eUL
#define CMPL_BA_TY_HWRM_ASY_EVT CMPL_BASE_TYPE_HWRM_ASYNC_EVENT
#define TX_CMP_FLAGS_ERROR (1 << 6)
#define TX_CMP_FLAGS_PUSH (1 << 7)
u32 tx_cmp_opaque;
__le32 tx_cmp_errors_v;
#define TX_CMP_V (1 << 0)
#define TX_CMP_ERRORS_BUFFER_ERROR (7 << 1)
#define TX_CMP_ERRORS_BUFFER_ERROR_NO_ERROR 0
#define TX_CMP_ERRORS_BUFFER_ERROR_BAD_FORMAT 2
#define TX_CMP_ERRORS_BUFFER_ERROR_INVALID_STAG 4
#define TX_CMP_ERRORS_BUFFER_ERROR_STAG_BOUNDS 5
#define TX_CMP_ERRORS_ZERO_LENGTH_PKT (1 << 4)
#define TX_CMP_ERRORS_EXCESSIVE_BD_LEN (1 << 5)
#define TX_CMP_ERRORS_DMA_ERROR (1 << 6)
#define TX_CMP_ERRORS_HINT_TOO_SHORT (1 << 7)
__le32 sq_cons_idx;
#define TX_CMP_SQ_CONS_IDX_MASK 0x00ffffff
};
struct bnge_sw_tx_bd {
struct sk_buff *skb;
DEFINE_DMA_UNMAP_ADDR(mapping);
DEFINE_DMA_UNMAP_LEN(len);
struct page *page;
u8 is_ts_pkt;
u8 is_push;
u8 action;
unsigned short nr_frags;
union {
u16 rx_prod;
u16 txts_prod;
};
};
struct bnge_sw_rx_bd {
void *data;
u8 *data_ptr;
dma_addr_t mapping;
};
struct bnge_sw_rx_agg_bd {
netmem_ref netmem;
unsigned int offset;
dma_addr_t mapping;
};
#define HWRM_RING_ALLOC_TX 0x1
#define HWRM_RING_ALLOC_RX 0x2
#define HWRM_RING_ALLOC_AGG 0x4
#define HWRM_RING_ALLOC_CMPL 0x8
#define HWRM_RING_ALLOC_NQ 0x10
struct bnge_ring_grp_info {
u16 fw_stats_ctx;
u16 fw_grp_id;
u16 rx_fw_ring_id;
u16 agg_fw_ring_id;
u16 nq_fw_ring_id;
};
#define BNGE_DEFAULT_RX_COPYBREAK 256
#define BNGE_MAX_RX_COPYBREAK 1024
#define BNGE_HW_FEATURE_VLAN_ALL_RX \
(NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX)
#define BNGE_HW_FEATURE_VLAN_ALL_TX \
(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX)
enum {
BNGE_NET_EN_GRO = BIT(0),
BNGE_NET_EN_LRO = BIT(1),
BNGE_NET_EN_JUMBO = BIT(2),
};
#define BNGE_NET_EN_TPA (BNGE_NET_EN_GRO | BNGE_NET_EN_LRO)
#define BNGE_NO_FW_ACCESS(bd) (pci_channel_offline((bd)->pdev))
#define MAX_TPA 256
#define MAX_TPA_MASK (MAX_TPA - 1)
#define MAX_TPA_SEGS 0x3f
#define BNGE_TPA_INNER_L3_OFF(hdr_info) \
(((hdr_info) >> 18) & 0x1ff)
#define BNGE_TPA_INNER_L2_OFF(hdr_info) \
(((hdr_info) >> 9) & 0x1ff)
#define BNGE_TPA_OUTER_L3_OFF(hdr_info) \
((hdr_info) & 0x1ff)
struct bnge_tpa_idx_map {
u16 agg_id_tbl[1024];
DECLARE_BITMAP(agg_idx_bmap, MAX_TPA);
};
struct bnge_tpa_info {
void *data;
u8 *data_ptr;
dma_addr_t mapping;
u16 len;
unsigned short gso_type;
u32 flags2;
u32 metadata;
enum pkt_hash_types hash_type;
u32 rss_hash;
u32 hdr_info;
u16 cfa_code; /* cfa_code in TPA start compl */
u8 agg_count;
bool vlan_valid;
bool cfa_code_valid;
struct rx_agg_cmp *agg_arr;
};
/* Minimum TX BDs for a TX packet with MAX_SKB_FRAGS + 1. We need one extra
* BD because the first TX BD is always a long BD.
*/
#define BNGE_MIN_TX_DESC_CNT (MAX_SKB_FRAGS + 2)
#define RX_RING(bn, x) (((x) & (bn)->rx_ring_mask) >> (BNGE_PAGE_SHIFT - 4))
#define RX_AGG_RING(bn, x) (((x) & (bn)->rx_agg_ring_mask) >> \
(BNGE_PAGE_SHIFT - 4))
#define RX_IDX(x) ((x) & (RX_DESC_CNT - 1))
#define TX_RING(bn, x) (((x) & (bn)->tx_ring_mask) >> (BNGE_PAGE_SHIFT - 4))
#define TX_IDX(x) ((x) & (TX_DESC_CNT - 1))
#define CP_RING(x) (((x) & ~(CP_DESC_CNT - 1)) >> (BNGE_PAGE_SHIFT - 4))
#define CP_IDX(x) ((x) & (CP_DESC_CNT - 1))
#define RING_RX(bn, idx) ((idx) & (bn)->rx_ring_mask)
#define NEXT_RX(idx) ((idx) + 1)
#define RING_RX_AGG(bn, idx) ((idx) & (bn)->rx_agg_ring_mask)
#define NEXT_RX_AGG(idx) ((idx) + 1)
#define BNGE_NQ_HDL_IDX_MASK 0x00ffffff
#define BNGE_NQ_HDL_TYPE_MASK 0xff000000
#define BNGE_NQ_HDL_TYPE_SHIFT 24
#define BNGE_NQ_HDL_TYPE_RX 0x00
#define BNGE_NQ_HDL_TYPE_TX 0x01
#define BNGE_NQ_HDL_IDX(hdl) ((hdl) & BNGE_NQ_HDL_IDX_MASK)
#define BNGE_NQ_HDL_TYPE(hdl) (((hdl) & BNGE_NQ_HDL_TYPE_MASK) >> \
BNGE_NQ_HDL_TYPE_SHIFT)
#define BNGE_GET_RING_STATS64(sw, counter) \
(*((sw) + offsetof(struct ctx_hw_stats, counter) / 8))
#define BNGE_GET_RX_PORT_STATS64(sw, counter) \
(*((sw) + offsetof(struct rx_port_stats, counter) / 8))
#define BNGE_GET_TX_PORT_STATS64(sw, counter) \
(*((sw) + offsetof(struct tx_port_stats, counter) / 8))
#define BNGE_PORT_STATS_SIZE \
(sizeof(struct rx_port_stats) + sizeof(struct tx_port_stats) + 1024)
#define BNGE_TX_PORT_STATS_BYTE_OFFSET \
(sizeof(struct rx_port_stats) + 512)
#define BNGE_RX_STATS_OFFSET(counter) \
(offsetof(struct rx_port_stats, counter) / 8)
#define BNGE_TX_STATS_OFFSET(counter) \
((offsetof(struct tx_port_stats, counter) + \
BNGE_TX_PORT_STATS_BYTE_OFFSET) / 8)
#define BNGE_RX_STATS_EXT_OFFSET(counter) \
(offsetof(struct rx_port_stats_ext, counter) / 8)
#define BNGE_TX_STATS_EXT_OFFSET(counter) \
(offsetof(struct tx_port_stats_ext, counter) / 8)
struct bnge_stats_mem {
u64 *sw_stats;
u64 *hw_masks;
void *hw_stats;
dma_addr_t hw_stats_map;
u32 len;
struct u64_stats_sync syncp;
};
enum bnge_net_state {
BNGE_STATE_NAPI_DISABLED,
BNGE_STATE_STATS_ENABLE,
};
#define BNGE_TIMER_INTERVAL HZ
enum bnge_net_flag {
BNGE_FLAG_PORT_STATS = BIT(0),
BNGE_FLAG_PORT_STATS_EXT = BIT(1),
};
enum bnge_sp_event {
BNGE_LINK_CHNG_SP_EVENT,
BNGE_LINK_SPEED_CHNG_SP_EVENT,
BNGE_LINK_CFG_CHANGE_SP_EVENT,
BNGE_UPDATE_PHY_SP_EVENT,
BNGE_PERIODIC_STATS_SP_EVENT,
};
struct bnge_net {
struct bnge_dev *bd;
struct net_device *netdev;
u32 priv_flags;
u32 rx_ring_size;
u32 rx_buf_size;
u32 rx_buf_use_size; /* usable size */
u32 rx_agg_ring_size;
u32 rx_copybreak;
u32 rx_ring_mask;
u32 rx_agg_ring_mask;
u16 rx_nr_pages;
u16 rx_agg_nr_pages;
u32 tx_ring_size;
u32 tx_ring_mask;
u16 tx_nr_pages;
/* NQs and Completion rings */
u32 cp_ring_size;
u32 cp_ring_mask;
u32 cp_bit;
u16 cp_nr_pages;
#define BNGE_L2_FLTR_HASH_SIZE 32
#define BNGE_L2_FLTR_HASH_MASK (BNGE_L2_FLTR_HASH_SIZE - 1)
struct hlist_head l2_fltr_hash_tbl[BNGE_L2_FLTR_HASH_SIZE];
u32 hash_seed;
u64 toeplitz_prefix;
struct bnge_napi **bnapi;
struct bnge_rx_ring_info *rx_ring;
struct bnge_tx_ring_info *tx_ring;
u16 *tx_ring_map;
enum dma_data_direction rx_dir;
/* grp_info indexed by napi/nq index */
struct bnge_ring_grp_info *grp_info;
struct bnge_vnic_info *vnic_info;
int nr_vnics;
int total_irqs;
u32 tx_wake_thresh;
u16 rx_offset;
u16 rx_dma_offset;
u8 rss_hash_key[HW_HASH_KEY_SIZE];
u8 rss_hash_key_valid:1;
u8 rss_hash_key_updated:1;
int rsscos_nr_ctxs;
u32 stats_coal_ticks;
unsigned long state;
u32 msg_enable;
u16 max_tpa;
__be16 vxlan_port;
__be16 nge_port;
__be16 vxlan_gpe_port;
unsigned int current_interval;
struct timer_list timer;
struct workqueue_struct *bnge_pf_wq;
struct work_struct sp_task;
unsigned long sp_event;
struct bnge_ethtool_link_info eth_link_info;
u64 flags;
struct bnge_stats_mem port_stats;
struct bnge_stats_mem rx_port_stats_ext;
struct bnge_stats_mem tx_port_stats_ext;
u16 fw_rx_stats_ext_size;
u16 fw_tx_stats_ext_size;
struct netdev_queue_stats_rx rxq_prv_stats;
struct netdev_queue_stats_tx txq_prv_stats;
struct rtnl_link_stats64 prv_stats64;
spinlock_t stats_lock;
u8 pri2cos_idx[8];
bool pri2cos_valid;
};
#define BNGE_DEFAULT_RX_RING_SIZE 511
#define BNGE_DEFAULT_TX_RING_SIZE 511
int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs);
void bnge_netdev_free(struct bnge_dev *bd);
void bnge_set_ring_params(struct bnge_dev *bd);
#if (BNGE_PAGE_SHIFT == 16)
#define MAX_RX_PAGES_AGG_ENA 1
#define MAX_RX_PAGES 4
#define MAX_RX_AGG_PAGES 4
#define MAX_TX_PAGES 1
#define MAX_CP_PAGES 16
#else
#define MAX_RX_PAGES_AGG_ENA 8
#define MAX_RX_PAGES 32
#define MAX_RX_AGG_PAGES 32
#define MAX_TX_PAGES 8
#define MAX_CP_PAGES 128
#endif
#define BNGE_RX_PAGE_SIZE (1 << BNGE_RX_PAGE_SHIFT)
#define RX_DESC_CNT (BNGE_PAGE_SIZE / sizeof(struct rx_bd))
#define TX_DESC_CNT (BNGE_PAGE_SIZE / sizeof(struct tx_bd))
#define CP_DESC_CNT (BNGE_PAGE_SIZE / sizeof(struct tx_cmp))
#define SW_RXBD_RING_SIZE (sizeof(struct bnge_sw_rx_bd) * RX_DESC_CNT)
#define HW_RXBD_RING_SIZE (sizeof(struct rx_bd) * RX_DESC_CNT)
#define SW_RXBD_AGG_RING_SIZE (sizeof(struct bnge_sw_rx_agg_bd) * RX_DESC_CNT)
#define SW_TXBD_RING_SIZE (sizeof(struct bnge_sw_tx_bd) * TX_DESC_CNT)
#define HW_TXBD_RING_SIZE (sizeof(struct tx_bd) * TX_DESC_CNT)
#define HW_CMPD_RING_SIZE (sizeof(struct tx_cmp) * CP_DESC_CNT)
#define BNGE_MAX_RX_DESC_CNT (RX_DESC_CNT * MAX_RX_PAGES - 1)
#define BNGE_MAX_RX_DESC_CNT_JUM_ENA (RX_DESC_CNT * MAX_RX_PAGES_AGG_ENA - 1)
#define BNGE_MAX_RX_JUM_DESC_CNT (RX_DESC_CNT * MAX_RX_AGG_PAGES - 1)
#define BNGE_MAX_TX_DESC_CNT (TX_DESC_CNT * MAX_TX_PAGES - 1)
#define BNGE_MAX_TXR_PER_NAPI 8
#define bnge_for_each_napi_tx(iter, bnapi, txr) \
for (iter = 0, txr = (bnapi)->tx_ring[0]; txr; \
txr = (iter < BNGE_MAX_TXR_PER_NAPI - 1) ? \
(bnapi)->tx_ring[++iter] : NULL)
#define DB_EPOCH(db, idx) (((idx) & (db)->db_epoch_mask) << \
((db)->db_epoch_shift))
#define DB_TOGGLE(tgl) ((tgl) << DBR_TOGGLE_SFT)
#define DB_RING_IDX(db, idx) (((idx) & (db)->db_ring_mask) | \
DB_EPOCH(db, idx))
#define BNGE_SET_NQ_HDL(cpr) \
(((cpr)->cp_ring_type << BNGE_NQ_HDL_TYPE_SHIFT) | (cpr)->cp_idx)
#define BNGE_DB_NQ(bd, db, idx) \
bnge_writeq(bd, (db)->db_key64 | DBR_TYPE_NQ | DB_RING_IDX(db, idx),\
(db)->doorbell)
#define BNGE_DB_NQ_ARM(bd, db, idx) \
bnge_writeq(bd, (db)->db_key64 | DBR_TYPE_NQ_ARM | \
DB_RING_IDX(db, idx), (db)->doorbell)
struct nqe_cn {
__le16 type;
#define NQ_CN_TYPE_MASK 0x3fUL
#define NQ_CN_TYPE_SFT 0
#define NQ_CN_TYPE_CQ_NOTIFICATION 0x30UL
#define NQ_CN_TYPE_LAST NQ_CN_TYPE_CQ_NOTIFICATION
#define NQ_CN_TOGGLE_MASK 0xc0UL
#define NQ_CN_TOGGLE_SFT 6
__le16 reserved16;
__le32 cq_handle_low;
__le32 v;
#define NQ_CN_V 0x1UL
__le32 cq_handle_high;
};
#define NQE_CN_TYPE(type) ((type) & NQ_CN_TYPE_MASK)
#define NQE_CN_TOGGLE(type) (((type) & NQ_CN_TOGGLE_MASK) >> \
NQ_CN_TOGGLE_SFT)
struct bnge_cp_ring_info {
struct bnge_napi *bnapi;
dma_addr_t *desc_mapping;
struct tx_cmp **desc_ring;
struct bnge_ring_struct ring_struct;
u8 cp_ring_type;
u8 cp_idx;
u32 cp_raw_cons;
struct bnge_db_info cp_db;
bool had_work_done;
bool has_more_work;
bool had_nqe_notify;
u8 toggle;
};
struct bnge_nq_ring_info {
struct bnge_napi *bnapi;
dma_addr_t *desc_mapping;
struct nqe_cn **desc_ring;
struct bnge_ring_struct ring_struct;
u32 nq_raw_cons;
struct bnge_db_info nq_db;
struct bnge_stats_mem stats;
u32 hw_stats_ctx_id;
bool has_more_work;
u16 cp_ring_count;
struct bnge_cp_ring_info *cp_ring_arr;
};
struct bnge_rx_ring_info {
struct bnge_napi *bnapi;
struct bnge_cp_ring_info *rx_cpr;
u16 rx_prod;
u16 rx_agg_prod;
u16 rx_sw_agg_prod;
u16 rx_next_cons;
struct bnge_db_info rx_db;
struct bnge_db_info rx_agg_db;
struct rx_bd *rx_desc_ring[MAX_RX_PAGES];
struct bnge_sw_rx_bd *rx_buf_ring;
struct rx_bd *rx_agg_desc_ring[MAX_RX_AGG_PAGES];
struct bnge_sw_rx_agg_bd *rx_agg_buf_ring;
unsigned long *rx_agg_bmap;
u16 rx_agg_bmap_size;
dma_addr_t rx_desc_mapping[MAX_RX_PAGES];
dma_addr_t rx_agg_desc_mapping[MAX_RX_AGG_PAGES];
struct bnge_tpa_info *rx_tpa;
struct bnge_tpa_idx_map *rx_tpa_idx_map;
struct bnge_ring_struct rx_ring_struct;
struct bnge_ring_struct rx_agg_ring_struct;
struct page_pool *page_pool;
struct page_pool *head_pool;
bool need_head_pool;
};
struct bnge_tx_ring_info {
struct bnge_napi *bnapi;
struct bnge_cp_ring_info *tx_cpr;
u16 tx_prod;
u16 tx_cons;
u16 tx_hw_cons;
u16 txq_index;
u8 tx_napi_idx;
u8 kick_pending;
struct bnge_db_info tx_db;
struct tx_bd *tx_desc_ring[MAX_TX_PAGES];
struct bnge_sw_tx_bd *tx_buf_ring;
dma_addr_t tx_desc_mapping[MAX_TX_PAGES];
u32 dev_state;
#define BNGE_DEV_STATE_CLOSING 0x1
struct bnge_ring_struct tx_ring_struct;
};
struct bnge_napi {
struct napi_struct napi;
struct bnge_net *bn;
int index;
struct bnge_nq_ring_info nq_ring;
struct bnge_rx_ring_info *rx_ring;
struct bnge_tx_ring_info *tx_ring[BNGE_MAX_TXR_PER_NAPI];
u8 events;
#define BNGE_RX_EVENT 1
#define BNGE_AGG_EVENT 2
#define BNGE_TX_EVENT 4
#define BNGE_REDIRECT_EVENT 8
#define BNGE_TX_CMP_EVENT 0x10
bool in_reset;
bool tx_fault;
};
#define INVALID_STATS_CTX_ID -1
#define BNGE_VNIC_DEFAULT 0
#define BNGE_MAX_UC_ADDRS 4
struct bnge_vnic_info {
u16 fw_vnic_id;
#define BNGE_MAX_CTX_PER_VNIC 8
u16 fw_rss_cos_lb_ctx[BNGE_MAX_CTX_PER_VNIC];
u16 mru;
/* index 0 always dev_addr */
struct bnge_l2_filter *l2_filters[BNGE_MAX_UC_ADDRS];
u16 uc_filter_count;
u8 *uc_list;
dma_addr_t rss_table_dma_addr;
__le16 *rss_table;
dma_addr_t rss_hash_key_dma_addr;
u64 *rss_hash_key;
int rss_table_size;
#define BNGE_RSS_TABLE_ENTRIES 64
#define BNGE_RSS_TABLE_SIZE (BNGE_RSS_TABLE_ENTRIES * 4)
#define BNGE_RSS_TABLE_MAX_TBL 8
#define BNGE_MAX_RSS_TABLE_SIZE \
(BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)
u32 rx_mask;
u8 *mc_list;
int mc_list_size;
int mc_list_count;
dma_addr_t mc_list_mapping;
#define BNGE_MAX_MC_ADDRS 16
u32 flags;
#define BNGE_VNIC_RSS_FLAG 1
#define BNGE_VNIC_MCAST_FLAG 4
#define BNGE_VNIC_UCAST_FLAG 8
u32 vnic_id;
};
struct bnge_filter_base {
struct hlist_node hash;
struct list_head list;
__le64 filter_id;
u8 type;
#define BNGE_FLTR_TYPE_L2 2
u8 flags;
u16 rxq;
u16 fw_vnic_id;
u16 vf_idx;
unsigned long state;
#define BNGE_FLTR_VALID 0
#define BNGE_FLTR_FW_DELETED 2
struct rcu_head rcu;
};
struct bnge_l2_key {
union {
struct {
u8 dst_mac_addr[ETH_ALEN];
u16 vlan;
};
u32 filter_key;
};
};
#define BNGE_L2_KEY_SIZE (sizeof(struct bnge_l2_key) / 4)
struct bnge_l2_filter {
/* base filter must be the first member */
struct bnge_filter_base base;
struct bnge_l2_key l2_key;
refcount_t refcnt;
};
u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr);
u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr);
void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic);
int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,
u16 prod, gfp_t gfp);
u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx);
u8 *__bnge_alloc_rx_frag(struct bnge_net *bn, dma_addr_t *mapping,
struct bnge_rx_ring_info *rxr, gfp_t gfp);
int bnge_alloc_rx_netmem(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,
u16 prod, gfp_t gfp);
void __bnge_queue_sp_work(struct bnge_net *bn);
void bnge_copy_hw_masks(u64 *mask_arr, __le64 *hw_mask_arr, int count);
#endif /* _BNGE_NETDEV_H_ */
@@ -0,0 +1,618 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Broadcom.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include "bnge.h"
#include "bnge_hwrm.h"
#include "bnge_hwrm_lib.h"
#include "bnge_resc.h"
static u16 bnge_num_tx_to_cp(struct bnge_dev *bd, u16 tx)
{
u16 tcs = bd->num_tc;
if (!tcs)
tcs = 1;
return tx / tcs;
}
static u16 bnge_get_max_func_irqs(struct bnge_dev *bd)
{
struct bnge_hw_resc *hw_resc = &bd->hw_resc;
return min_t(u16, hw_resc->max_irqs, hw_resc->max_nqs);
}
static unsigned int bnge_get_max_func_stat_ctxs(struct bnge_dev *bd)
{
return bd->hw_resc.max_stat_ctxs;
}
bool bnge_aux_has_enough_resources(struct bnge_dev *bd)
{
unsigned int max_stat_ctxs;
max_stat_ctxs = bnge_get_max_func_stat_ctxs(bd);
if (max_stat_ctxs <= BNGE_MIN_ROCE_STAT_CTXS ||
bd->nq_nr_rings == max_stat_ctxs)
return false;
return true;
}
static unsigned int bnge_get_max_func_cp_rings(struct bnge_dev *bd)
{
return bd->hw_resc.max_cp_rings;
}
static int bnge_aux_get_dflt_msix(struct bnge_dev *bd)
{
int roce_msix = BNGE_MAX_ROCE_MSIX;
return min_t(int, roce_msix, num_online_cpus() + 1);
}
u16 bnge_aux_get_msix(struct bnge_dev *bd)
{
if (bnge_is_roce_en(bd))
return bd->aux_num_msix;
return 0;
}
static void bnge_aux_set_msix_num(struct bnge_dev *bd, u16 num)
{
if (bnge_is_roce_en(bd))
bd->aux_num_msix = num;
}
static u16 bnge_aux_get_stat_ctxs(struct bnge_dev *bd)
{
if (bnge_is_roce_en(bd))
return bd->aux_num_stat_ctxs;
return 0;
}
static void bnge_aux_set_stat_ctxs(struct bnge_dev *bd, u16 num_aux_ctx)
{
if (bnge_is_roce_en(bd))
bd->aux_num_stat_ctxs = num_aux_ctx;
}
static u16 bnge_func_stat_ctxs_demand(struct bnge_dev *bd)
{
return bd->nq_nr_rings + bnge_aux_get_stat_ctxs(bd);
}
static int bnge_get_dflt_aux_stat_ctxs(struct bnge_dev *bd)
{
int stat_ctx = 0;
if (bnge_is_roce_en(bd)) {
stat_ctx = BNGE_MIN_ROCE_STAT_CTXS;
if (!bd->pf.port_id && bd->port_count > 1)
stat_ctx++;
}
return stat_ctx;
}
static u16 bnge_nqs_demand(struct bnge_dev *bd)
{
return bd->nq_nr_rings + bnge_aux_get_msix(bd);
}
static u16 bnge_cprs_demand(struct bnge_dev *bd)
{
return bd->tx_nr_rings + bd->rx_nr_rings;
}
static u16 bnge_get_avail_msix(struct bnge_dev *bd, int num)
{
u16 max_irq = bnge_get_max_func_irqs(bd);
u16 total_demand = bd->nq_nr_rings + num;
if (max_irq < total_demand) {
num = max_irq - bd->nq_nr_rings;
if (num <= 0)
return 0;
}
return num;
}
static u16 bnge_num_cp_to_tx(struct bnge_dev *bd, u16 tx_chunks)
{
return tx_chunks * bd->num_tc;
}
int bnge_fix_rings_count(u16 *rx, u16 *tx, u16 max, bool shared)
{
u16 _rx = *rx, _tx = *tx;
if (shared) {
*rx = min_t(u16, _rx, max);
*tx = min_t(u16, _tx, max);
} else {
if (max < 2)
return -ENOMEM;
while (_rx + _tx > max) {
if (_rx > _tx && _rx > 1)
_rx--;
else if (_tx > 1)
_tx--;
}
*rx = _rx;
*tx = _tx;
}
return 0;
}
static int bnge_adjust_rings(struct bnge_dev *bd, u16 *rx,
u16 *tx, u16 max_nq, bool sh)
{
u16 tx_chunks = bnge_num_tx_to_cp(bd, *tx);
if (tx_chunks != *tx) {
u16 tx_saved = tx_chunks;
int rc;
rc = bnge_fix_rings_count(rx, &tx_chunks, max_nq, sh);
if (rc)
return rc;
if (tx_chunks != tx_saved)
*tx = bnge_num_cp_to_tx(bd, tx_chunks);
return 0;
}
return bnge_fix_rings_count(rx, tx, max_nq, sh);
}
int bnge_cal_nr_rss_ctxs(u16 rx_rings)
{
if (!rx_rings)
return 0;
return bnge_adjust_pow_two(rx_rings - 1,
BNGE_RSS_TABLE_ENTRIES);
}
static u16 bnge_rss_ctxs_in_use(struct bnge_dev *bd,
struct bnge_hw_rings *hwr)
{
return bnge_cal_nr_rss_ctxs(hwr->grp);
}
static u16 bnge_get_total_vnics(struct bnge_dev *bd, u16 rx_rings)
{
return 1;
}
u32 bnge_get_rxfh_indir_size(struct bnge_dev *bd)
{
return bnge_cal_nr_rss_ctxs(bd->rx_nr_rings) *
BNGE_RSS_TABLE_ENTRIES;
}
static void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd)
{
u16 max_entries, pad;
u32 *rss_indir_tbl;
int i;
max_entries = bnge_get_rxfh_indir_size(bd);
rss_indir_tbl = &bd->rss_indir_tbl[0];
for (i = 0; i < max_entries; i++)
rss_indir_tbl[i] = ethtool_rxfh_indir_default(i,
bd->rx_nr_rings);
pad = bd->rss_indir_tbl_entries - max_entries;
if (pad)
memset(&rss_indir_tbl[i], 0, pad * sizeof(*rss_indir_tbl));
}
static void bnge_copy_reserved_rings(struct bnge_dev *bd,
struct bnge_hw_rings *hwr)
{
struct bnge_hw_resc *hw_resc = &bd->hw_resc;
hwr->tx = hw_resc->resv_tx_rings;
hwr->rx = hw_resc->resv_rx_rings;
hwr->nq = hw_resc->resv_irqs;
hwr->cmpl = hw_resc->resv_cp_rings;
hwr->grp = hw_resc->resv_hw_ring_grps;
hwr->vnic = hw_resc->resv_vnics;
hwr->stat = hw_resc->resv_stat_ctxs;
hwr->rss_ctx = hw_resc->resv_rsscos_ctxs;
}
static bool bnge_rings_ok(struct bnge_hw_rings *hwr)
{
return hwr->tx && hwr->rx && hwr->nq && hwr->grp && hwr->vnic &&
hwr->stat && hwr->cmpl;
}
static bool bnge_need_reserve_rings(struct bnge_dev *bd)
{
struct bnge_hw_resc *hw_resc = &bd->hw_resc;
u16 cprs = bnge_cprs_demand(bd);
u16 rx = bd->rx_nr_rings, stat;
u16 nqs = bnge_nqs_demand(bd);
u16 vnic;
if (hw_resc->resv_tx_rings != bd->tx_nr_rings)
return true;
vnic = bnge_get_total_vnics(bd, rx);
if (bnge_is_agg_reqd(bd))
rx <<= 1;
stat = bnge_func_stat_ctxs_demand(bd);
if (hw_resc->resv_rx_rings != rx || hw_resc->resv_cp_rings != cprs ||
hw_resc->resv_vnics != vnic || hw_resc->resv_stat_ctxs != stat)
return true;
if (hw_resc->resv_irqs != nqs)
return true;
return false;
}
int bnge_reserve_rings(struct bnge_dev *bd)
{
u16 aux_dflt_msix = bnge_aux_get_dflt_msix(bd);
struct bnge_hw_rings hwr = {0};
u16 rx_rings, old_rx_rings;
u16 nq = bd->nq_nr_rings;
u16 aux_msix = 0;
bool sh = false;
u16 tx_cp;
int rc;
if (!bnge_need_reserve_rings(bd))
return 0;
if (!bnge_aux_registered(bd)) {
aux_msix = bnge_get_avail_msix(bd, aux_dflt_msix);
if (!aux_msix)
bnge_aux_set_stat_ctxs(bd, 0);
if (aux_msix > aux_dflt_msix)
aux_msix = aux_dflt_msix;
hwr.nq = nq + aux_msix;
} else {
hwr.nq = bnge_nqs_demand(bd);
}
hwr.tx = bd->tx_nr_rings;
hwr.rx = bd->rx_nr_rings;
if (bd->flags & BNGE_EN_SHARED_CHNL)
sh = true;
hwr.cmpl = hwr.rx + hwr.tx;
hwr.vnic = bnge_get_total_vnics(bd, hwr.rx);
if (bnge_is_agg_reqd(bd))
hwr.rx <<= 1;
hwr.grp = bd->rx_nr_rings;
hwr.rss_ctx = bnge_rss_ctxs_in_use(bd, &hwr);
hwr.stat = bnge_func_stat_ctxs_demand(bd);
old_rx_rings = bd->hw_resc.resv_rx_rings;
rc = bnge_hwrm_reserve_rings(bd, &hwr);
if (rc)
return rc;
bnge_copy_reserved_rings(bd, &hwr);
rx_rings = hwr.rx;
if (bnge_is_agg_reqd(bd)) {
if (hwr.rx >= 2)
rx_rings = hwr.rx >> 1;
else
return -ENOMEM;
}
rx_rings = min_t(u16, rx_rings, hwr.grp);
hwr.nq = min_t(u16, hwr.nq, bd->nq_nr_rings);
if (hwr.stat > bnge_aux_get_stat_ctxs(bd))
hwr.stat -= bnge_aux_get_stat_ctxs(bd);
hwr.nq = min_t(u16, hwr.nq, hwr.stat);
/* Adjust the rings */
rc = bnge_adjust_rings(bd, &rx_rings, &hwr.tx, hwr.nq, sh);
if (bnge_is_agg_reqd(bd))
hwr.rx = rx_rings << 1;
tx_cp = hwr.tx;
hwr.nq = sh ? max_t(u16, tx_cp, rx_rings) : tx_cp + rx_rings;
bd->tx_nr_rings = hwr.tx;
if (rx_rings != bd->rx_nr_rings)
dev_warn(bd->dev, "RX rings resv reduced to %d than earlier %d requested\n",
rx_rings, bd->rx_nr_rings);
bd->rx_nr_rings = rx_rings;
bd->nq_nr_rings = hwr.nq;
if (!bnge_rings_ok(&hwr))
return -ENOMEM;
if (old_rx_rings != bd->hw_resc.resv_rx_rings)
bnge_set_dflt_rss_indir_tbl(bd);
if (!bnge_aux_registered(bd)) {
u16 resv_msix, resv_ctx, aux_ctxs;
struct bnge_hw_resc *hw_resc;
hw_resc = &bd->hw_resc;
resv_msix = hw_resc->resv_irqs - bd->nq_nr_rings;
aux_msix = min_t(u16, resv_msix, aux_msix);
bnge_aux_set_msix_num(bd, aux_msix);
resv_ctx = hw_resc->resv_stat_ctxs - bd->nq_nr_rings;
aux_ctxs = min(resv_ctx, bnge_aux_get_stat_ctxs(bd));
bnge_aux_set_stat_ctxs(bd, aux_ctxs);
}
return rc;
}
int bnge_alloc_irqs(struct bnge_dev *bd)
{
u16 aux_msix, tx_cp, num_entries;
int i, irqs_demand, rc;
u16 max, min = 1;
irqs_demand = bnge_nqs_demand(bd);
max = bnge_get_max_func_irqs(bd);
if (irqs_demand > max)
irqs_demand = max;
if (!(bd->flags & BNGE_EN_SHARED_CHNL))
min = 2;
irqs_demand = pci_alloc_irq_vectors(bd->pdev, min, irqs_demand,
PCI_IRQ_MSIX);
aux_msix = bnge_aux_get_msix(bd);
if (irqs_demand < 0 || irqs_demand < aux_msix) {
rc = -ENODEV;
goto err_free_irqs;
}
num_entries = irqs_demand;
if (pci_msix_can_alloc_dyn(bd->pdev))
num_entries = max;
bd->irq_tbl = kzalloc_objs(*bd->irq_tbl, num_entries);
if (!bd->irq_tbl) {
rc = -ENOMEM;
goto err_free_irqs;
}
for (i = 0; i < irqs_demand; i++)
bd->irq_tbl[i].vector = pci_irq_vector(bd->pdev, i);
bd->irqs_acquired = irqs_demand;
/* Reduce rings based upon num of vectors allocated.
* We dont need to consider NQs as they have been calculated
* and must be more than irqs_demand.
*/
rc = bnge_adjust_rings(bd, &bd->rx_nr_rings,
&bd->tx_nr_rings,
irqs_demand - aux_msix, min == 1);
if (rc)
goto err_free_irqs;
tx_cp = bnge_num_tx_to_cp(bd, bd->tx_nr_rings);
bd->nq_nr_rings = (min == 1) ?
max_t(u16, tx_cp, bd->rx_nr_rings) :
tx_cp + bd->rx_nr_rings;
/* Readjust tx_nr_rings_per_tc */
if (!bd->num_tc)
bd->tx_nr_rings_per_tc = bd->tx_nr_rings;
return 0;
err_free_irqs:
dev_err(bd->dev, "Failed to allocate IRQs err = %d\n", rc);
bnge_free_irqs(bd);
return rc;
}
void bnge_free_irqs(struct bnge_dev *bd)
{
pci_free_irq_vectors(bd->pdev);
kfree(bd->irq_tbl);
bd->irq_tbl = NULL;
}
static void _bnge_get_max_rings(struct bnge_dev *bd, u16 *max_rx,
u16 *max_tx, u16 *max_nq)
{
struct bnge_hw_resc *hw_resc = &bd->hw_resc;
u16 max_ring_grps = 0, max_cp;
int rc;
*max_tx = hw_resc->max_tx_rings;
*max_rx = hw_resc->max_rx_rings;
*max_nq = min_t(int, bnge_get_max_func_irqs(bd),
hw_resc->max_stat_ctxs);
max_ring_grps = hw_resc->max_hw_ring_grps;
if (bnge_is_agg_reqd(bd))
*max_rx >>= 1;
max_cp = bnge_get_max_func_cp_rings(bd);
/* Fix RX and TX rings according to number of CPs available */
rc = bnge_fix_rings_count(max_rx, max_tx, max_cp, false);
if (rc) {
*max_rx = 0;
*max_tx = 0;
}
*max_rx = min_t(int, *max_rx, max_ring_grps);
}
static int bnge_get_max_rings(struct bnge_dev *bd, u16 *max_rx,
u16 *max_tx, bool shared)
{
u16 rx, tx, nq;
_bnge_get_max_rings(bd, &rx, &tx, &nq);
*max_rx = rx;
*max_tx = tx;
if (!rx || !tx || !nq)
return -ENOMEM;
return bnge_fix_rings_count(max_rx, max_tx, nq, shared);
}
static int bnge_get_dflt_rings(struct bnge_dev *bd, u16 *max_rx, u16 *max_tx,
bool shared)
{
int rc;
rc = bnge_get_max_rings(bd, max_rx, max_tx, shared);
if (rc) {
dev_info(bd->dev, "Not enough rings available\n");
return rc;
}
if (bnge_is_roce_en(bd)) {
int max_cp, max_stat, max_irq;
/* Reserve minimum resources for RoCE */
max_cp = bnge_get_max_func_cp_rings(bd);
max_stat = bnge_get_max_func_stat_ctxs(bd);
max_irq = bnge_get_max_func_irqs(bd);
if (max_cp <= BNGE_MIN_ROCE_CP_RINGS ||
max_irq <= BNGE_MIN_ROCE_CP_RINGS ||
max_stat <= BNGE_MIN_ROCE_STAT_CTXS)
return 0;
max_cp -= BNGE_MIN_ROCE_CP_RINGS;
max_irq -= BNGE_MIN_ROCE_CP_RINGS;
max_stat -= BNGE_MIN_ROCE_STAT_CTXS;
max_cp = min_t(u16, max_cp, max_irq);
max_cp = min_t(u16, max_cp, max_stat);
rc = bnge_adjust_rings(bd, max_rx, max_tx, max_cp, shared);
if (rc)
rc = 0;
}
return rc;
}
/* In initial default shared ring setting, each shared ring must have a
* RX/TX ring pair.
*/
static void bnge_trim_dflt_sh_rings(struct bnge_dev *bd)
{
bd->nq_nr_rings = min_t(u16, bd->tx_nr_rings_per_tc, bd->rx_nr_rings);
bd->rx_nr_rings = bd->nq_nr_rings;
bd->tx_nr_rings_per_tc = bd->nq_nr_rings;
bd->tx_nr_rings = bd->tx_nr_rings_per_tc;
}
static int bnge_net_init_dflt_rings(struct bnge_dev *bd, bool sh)
{
u16 dflt_rings, max_rx_rings, max_tx_rings;
int rc;
if (sh)
bd->flags |= BNGE_EN_SHARED_CHNL;
dflt_rings = netif_get_num_default_rss_queues();
rc = bnge_get_dflt_rings(bd, &max_rx_rings, &max_tx_rings, sh);
if (rc)
return rc;
bd->rx_nr_rings = min_t(u16, dflt_rings, max_rx_rings);
bd->tx_nr_rings_per_tc = min_t(u16, dflt_rings, max_tx_rings);
if (sh)
bnge_trim_dflt_sh_rings(bd);
else
bd->nq_nr_rings = bd->tx_nr_rings_per_tc + bd->rx_nr_rings;
bd->tx_nr_rings = bd->tx_nr_rings_per_tc;
rc = bnge_reserve_rings(bd);
if (rc && rc != -ENODEV)
dev_warn(bd->dev, "Unable to reserve tx rings\n");
bd->tx_nr_rings_per_tc = bd->tx_nr_rings;
if (sh)
bnge_trim_dflt_sh_rings(bd);
/* Rings may have been reduced, re-reserve them again */
if (bnge_need_reserve_rings(bd)) {
rc = bnge_reserve_rings(bd);
if (rc && rc != -ENODEV)
dev_warn(bd->dev, "Fewer rings reservation failed\n");
bd->tx_nr_rings_per_tc = bd->tx_nr_rings;
}
if (rc) {
bd->tx_nr_rings = 0;
bd->rx_nr_rings = 0;
}
return rc;
}
static int bnge_alloc_rss_indir_tbl(struct bnge_dev *bd)
{
u16 entries;
entries = BNGE_MAX_RSS_TABLE_ENTRIES;
bd->rss_indir_tbl_entries = entries;
bd->rss_indir_tbl =
kmalloc_array(entries, sizeof(*bd->rss_indir_tbl), GFP_KERNEL);
if (!bd->rss_indir_tbl)
return -ENOMEM;
return 0;
}
int bnge_net_init_dflt_config(struct bnge_dev *bd)
{
struct bnge_hw_resc *hw_resc;
int rc;
rc = bnge_alloc_rss_indir_tbl(bd);
if (rc)
return rc;
rc = bnge_net_init_dflt_rings(bd, true);
if (rc)
goto err_free_tbl;
hw_resc = &bd->hw_resc;
bd->max_fltr = hw_resc->max_rx_em_flows + hw_resc->max_rx_wm_flows +
BNGE_L2_FLTR_MAX_FLTR;
return 0;
err_free_tbl:
kfree(bd->rss_indir_tbl);
bd->rss_indir_tbl = NULL;
return rc;
}
void bnge_net_uninit_dflt_config(struct bnge_dev *bd)
{
kfree(bd->rss_indir_tbl);
bd->rss_indir_tbl = NULL;
}
void bnge_aux_init_dflt_config(struct bnge_dev *bd)
{
bd->aux_num_msix = bnge_aux_get_dflt_msix(bd);
bd->aux_num_stat_ctxs = bnge_get_dflt_aux_stat_ctxs(bd);
}
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_RESC_H_
#define _BNGE_RESC_H_
#include "bnge_netdev.h"
#include "bnge_rmem.h"
struct bnge_hw_resc {
u16 min_rsscos_ctxs;
u16 max_rsscos_ctxs;
u16 resv_rsscos_ctxs;
u16 min_cp_rings;
u16 max_cp_rings;
u16 resv_cp_rings;
u16 min_tx_rings;
u16 max_tx_rings;
u16 resv_tx_rings;
u16 max_tx_sch_inputs;
u16 min_rx_rings;
u16 max_rx_rings;
u16 resv_rx_rings;
u16 min_hw_ring_grps;
u16 max_hw_ring_grps;
u16 resv_hw_ring_grps;
u16 min_l2_ctxs;
u16 max_l2_ctxs;
u16 min_vnics;
u16 max_vnics;
u16 resv_vnics;
u16 min_stat_ctxs;
u16 max_stat_ctxs;
u16 resv_stat_ctxs;
u16 max_nqs;
u16 max_irqs;
u16 resv_irqs;
u32 max_encap_records;
u32 max_decap_records;
u32 max_tx_em_flows;
u32 max_tx_wm_flows;
u32 max_rx_em_flows;
u32 max_rx_wm_flows;
};
struct bnge_hw_rings {
u16 tx;
u16 rx;
u16 grp;
u16 nq;
u16 cmpl;
u16 stat;
u16 vnic;
u16 rss_ctx;
};
/* "TXRX", 2 hypens, plus maximum integer */
#define BNGE_IRQ_NAME_EXTRA 17
struct bnge_irq {
irq_handler_t handler;
unsigned int vector;
u8 requested:1;
u8 have_cpumask:1;
char name[IFNAMSIZ + BNGE_IRQ_NAME_EXTRA];
cpumask_var_t cpu_mask;
};
int bnge_reserve_rings(struct bnge_dev *bd);
int bnge_fix_rings_count(u16 *rx, u16 *tx, u16 max, bool shared);
int bnge_alloc_irqs(struct bnge_dev *bd);
void bnge_free_irqs(struct bnge_dev *bd);
int bnge_net_init_dflt_config(struct bnge_dev *bd);
void bnge_net_uninit_dflt_config(struct bnge_dev *bd);
void bnge_aux_init_dflt_config(struct bnge_dev *bd);
u32 bnge_get_rxfh_indir_size(struct bnge_dev *bd);
int bnge_cal_nr_rss_ctxs(u16 rx_rings);
bool bnge_aux_has_enough_resources(struct bnge_dev *bd);
static inline u32
bnge_adjust_pow_two(u32 total_ent, u16 ent_per_blk)
{
u32 blks = total_ent / ent_per_blk;
if (blks == 0 || blks == 1)
return ++blks;
if (!is_power_of_2(blks))
blks = roundup_pow_of_two(blks);
return blks;
}
#define BNGE_MAX_ROCE_MSIX 64
#define BNGE_MIN_ROCE_CP_RINGS 2
#define BNGE_MIN_ROCE_STAT_CTXS 1
#endif /* _BNGE_RESC_H_ */
@@ -0,0 +1,482 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Broadcom.
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/crash_dump.h>
#include <linux/bnge/hsi.h>
#include "bnge.h"
#include "bnge_hwrm_lib.h"
#include "bnge_rmem.h"
static void bnge_init_ctx_mem(struct bnge_ctx_mem_type *ctxm,
void *p, int len)
{
u8 init_val = ctxm->init_value;
u16 offset = ctxm->init_offset;
u8 *p2 = p;
int i;
if (!init_val)
return;
if (offset == BNGE_CTX_INIT_INVALID_OFFSET) {
memset(p, init_val, len);
return;
}
for (i = 0; i < len; i += ctxm->entry_size)
*(p2 + i + offset) = init_val;
}
void bnge_free_ring(struct bnge_dev *bd, struct bnge_ring_mem_info *rmem)
{
struct pci_dev *pdev = bd->pdev;
int i;
if (!rmem->pg_arr)
goto skip_pages;
for (i = 0; i < rmem->nr_pages; i++) {
if (!rmem->pg_arr[i])
continue;
dma_free_coherent(&pdev->dev, rmem->page_size,
rmem->pg_arr[i], rmem->dma_arr[i]);
rmem->pg_arr[i] = NULL;
}
skip_pages:
if (rmem->pg_tbl) {
size_t pg_tbl_size = rmem->nr_pages * 8;
if (rmem->flags & BNGE_RMEM_USE_FULL_PAGE_FLAG)
pg_tbl_size = rmem->page_size;
dma_free_coherent(&pdev->dev, pg_tbl_size,
rmem->pg_tbl, rmem->dma_pg_tbl);
rmem->pg_tbl = NULL;
}
if (rmem->vmem_size && *rmem->vmem) {
vfree(*rmem->vmem);
*rmem->vmem = NULL;
}
}
int bnge_alloc_ring(struct bnge_dev *bd, struct bnge_ring_mem_info *rmem)
{
struct pci_dev *pdev = bd->pdev;
u64 valid_bit = 0;
int i;
if (rmem->flags & (BNGE_RMEM_VALID_PTE_FLAG | BNGE_RMEM_RING_PTE_FLAG))
valid_bit = PTU_PTE_VALID;
if ((rmem->nr_pages > 1 || rmem->depth > 0) && !rmem->pg_tbl) {
size_t pg_tbl_size = rmem->nr_pages * 8;
if (rmem->flags & BNGE_RMEM_USE_FULL_PAGE_FLAG)
pg_tbl_size = rmem->page_size;
rmem->pg_tbl = dma_alloc_coherent(&pdev->dev, pg_tbl_size,
&rmem->dma_pg_tbl,
GFP_KERNEL);
if (!rmem->pg_tbl)
return -ENOMEM;
}
for (i = 0; i < rmem->nr_pages; i++) {
u64 extra_bits = valid_bit;
rmem->pg_arr[i] = dma_alloc_coherent(&pdev->dev,
rmem->page_size,
&rmem->dma_arr[i],
GFP_KERNEL);
if (!rmem->pg_arr[i])
goto err_free_ring;
if (rmem->ctx_mem)
bnge_init_ctx_mem(rmem->ctx_mem, rmem->pg_arr[i],
rmem->page_size);
if (rmem->nr_pages > 1 || rmem->depth > 0) {
if (i == rmem->nr_pages - 2 &&
(rmem->flags & BNGE_RMEM_RING_PTE_FLAG))
extra_bits |= PTU_PTE_NEXT_TO_LAST;
else if (i == rmem->nr_pages - 1 &&
(rmem->flags & BNGE_RMEM_RING_PTE_FLAG))
extra_bits |= PTU_PTE_LAST;
rmem->pg_tbl[i] =
cpu_to_le64(rmem->dma_arr[i] | extra_bits);
}
}
if (rmem->vmem_size) {
*rmem->vmem = vzalloc(rmem->vmem_size);
if (!(*rmem->vmem))
goto err_free_ring;
}
return 0;
err_free_ring:
bnge_free_ring(bd, rmem);
return -ENOMEM;
}
static int bnge_alloc_ctx_one_lvl(struct bnge_dev *bd,
struct bnge_ctx_pg_info *ctx_pg)
{
struct bnge_ring_mem_info *rmem = &ctx_pg->ring_mem;
rmem->page_size = BNGE_PAGE_SIZE;
rmem->pg_arr = ctx_pg->ctx_pg_arr;
rmem->dma_arr = ctx_pg->ctx_dma_arr;
rmem->flags = BNGE_RMEM_VALID_PTE_FLAG;
if (rmem->depth >= 1)
rmem->flags |= BNGE_RMEM_USE_FULL_PAGE_FLAG;
return bnge_alloc_ring(bd, rmem);
}
static int bnge_alloc_ctx_pg_tbls(struct bnge_dev *bd,
struct bnge_ctx_pg_info *ctx_pg, u32 mem_size,
u8 depth, struct bnge_ctx_mem_type *ctxm)
{
struct bnge_ring_mem_info *rmem = &ctx_pg->ring_mem;
int rc;
if (!mem_size)
return -EINVAL;
ctx_pg->nr_pages = DIV_ROUND_UP(mem_size, BNGE_PAGE_SIZE);
if (ctx_pg->nr_pages > MAX_CTX_TOTAL_PAGES) {
ctx_pg->nr_pages = 0;
return -EINVAL;
}
if (ctx_pg->nr_pages > MAX_CTX_PAGES || depth > 1) {
int nr_tbls, i;
rmem->depth = 2;
ctx_pg->ctx_pg_tbl = kzalloc_objs(ctx_pg, MAX_CTX_PAGES);
if (!ctx_pg->ctx_pg_tbl)
return -ENOMEM;
nr_tbls = DIV_ROUND_UP(ctx_pg->nr_pages, MAX_CTX_PAGES);
rmem->nr_pages = nr_tbls;
rc = bnge_alloc_ctx_one_lvl(bd, ctx_pg);
if (rc)
return rc;
for (i = 0; i < nr_tbls; i++) {
struct bnge_ctx_pg_info *pg_tbl;
pg_tbl = kzalloc_obj(*pg_tbl);
if (!pg_tbl)
return -ENOMEM;
ctx_pg->ctx_pg_tbl[i] = pg_tbl;
rmem = &pg_tbl->ring_mem;
rmem->pg_tbl = ctx_pg->ctx_pg_arr[i];
rmem->dma_pg_tbl = ctx_pg->ctx_dma_arr[i];
rmem->depth = 1;
rmem->nr_pages = MAX_CTX_PAGES;
rmem->ctx_mem = ctxm;
if (i == (nr_tbls - 1)) {
int rem = ctx_pg->nr_pages % MAX_CTX_PAGES;
if (rem)
rmem->nr_pages = rem;
}
rc = bnge_alloc_ctx_one_lvl(bd, pg_tbl);
if (rc)
break;
}
} else {
rmem->nr_pages = DIV_ROUND_UP(mem_size, BNGE_PAGE_SIZE);
if (rmem->nr_pages > 1 || depth)
rmem->depth = 1;
rmem->ctx_mem = ctxm;
rc = bnge_alloc_ctx_one_lvl(bd, ctx_pg);
}
return rc;
}
static void bnge_free_ctx_pg_tbls(struct bnge_dev *bd,
struct bnge_ctx_pg_info *ctx_pg)
{
struct bnge_ring_mem_info *rmem = &ctx_pg->ring_mem;
if (rmem->depth > 1 || ctx_pg->nr_pages > MAX_CTX_PAGES ||
ctx_pg->ctx_pg_tbl) {
int i, nr_tbls = rmem->nr_pages;
for (i = 0; i < nr_tbls; i++) {
struct bnge_ctx_pg_info *pg_tbl;
struct bnge_ring_mem_info *rmem2;
pg_tbl = ctx_pg->ctx_pg_tbl[i];
if (!pg_tbl)
continue;
rmem2 = &pg_tbl->ring_mem;
bnge_free_ring(bd, rmem2);
ctx_pg->ctx_pg_arr[i] = NULL;
kfree(pg_tbl);
ctx_pg->ctx_pg_tbl[i] = NULL;
}
kfree(ctx_pg->ctx_pg_tbl);
ctx_pg->ctx_pg_tbl = NULL;
}
bnge_free_ring(bd, rmem);
ctx_pg->nr_pages = 0;
}
static int bnge_setup_ctxm_pg_tbls(struct bnge_dev *bd,
struct bnge_ctx_mem_type *ctxm, u32 entries,
u8 pg_lvl)
{
struct bnge_ctx_pg_info *ctx_pg = ctxm->pg_info;
int i, rc = 0, n = 1;
u32 mem_size;
if (!ctxm->entry_size || !ctx_pg)
return -EINVAL;
if (ctxm->instance_bmap)
n = hweight32(ctxm->instance_bmap);
if (ctxm->entry_multiple)
entries = roundup(entries, ctxm->entry_multiple);
entries = clamp_t(u32, entries, ctxm->min_entries, ctxm->max_entries);
mem_size = entries * ctxm->entry_size;
for (i = 0; i < n && !rc; i++) {
ctx_pg[i].entries = entries;
rc = bnge_alloc_ctx_pg_tbls(bd, &ctx_pg[i], mem_size, pg_lvl,
ctxm->init_value ? ctxm : NULL);
}
return rc;
}
static int bnge_backing_store_cfg(struct bnge_dev *bd, u32 ena)
{
struct bnge_ctx_mem_info *ctx = bd->ctx;
struct bnge_ctx_mem_type *ctxm;
u16 last_type;
int rc = 0;
u16 type;
if (!ena)
return 0;
else if (ena & FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM)
last_type = BNGE_CTX_MAX - 1;
else
last_type = BNGE_CTX_L2_MAX - 1;
ctx->ctx_arr[last_type].last = 1;
for (type = 0 ; type < BNGE_CTX_V2_MAX; type++) {
ctxm = &ctx->ctx_arr[type];
rc = bnge_hwrm_func_backing_store(bd, ctxm, ctxm->last);
if (rc)
return rc;
}
return 0;
}
void bnge_free_ctx_mem(struct bnge_dev *bd)
{
struct bnge_ctx_mem_info *ctx = bd->ctx;
u16 type;
if (!ctx)
return;
for (type = 0; type < BNGE_CTX_V2_MAX; type++) {
struct bnge_ctx_mem_type *ctxm = &ctx->ctx_arr[type];
struct bnge_ctx_pg_info *ctx_pg = ctxm->pg_info;
int i, n = 1;
if (!ctx_pg)
continue;
if (ctxm->instance_bmap)
n = hweight32(ctxm->instance_bmap);
for (i = 0; i < n; i++)
bnge_free_ctx_pg_tbls(bd, &ctx_pg[i]);
kfree(ctx_pg);
ctxm->pg_info = NULL;
}
ctx->flags &= ~BNGE_CTX_FLAG_INITED;
kfree(ctx);
bd->ctx = NULL;
}
#define FUNC_BACKING_STORE_CFG_REQ_DFLT_ENABLES \
(FUNC_BACKING_STORE_CFG_REQ_ENABLES_QP | \
FUNC_BACKING_STORE_CFG_REQ_ENABLES_SRQ | \
FUNC_BACKING_STORE_CFG_REQ_ENABLES_CQ | \
FUNC_BACKING_STORE_CFG_REQ_ENABLES_VNIC | \
FUNC_BACKING_STORE_CFG_REQ_ENABLES_STAT)
int bnge_alloc_ctx_mem(struct bnge_dev *bd)
{
struct bnge_ctx_mem_type *ctxm;
struct bnge_ctx_mem_info *ctx;
u32 l2_qps, qp1_qps, max_qps;
u32 ena, entries_sp, entries;
u32 srqs, max_srqs, min;
u32 extra_srqs = 0;
u32 extra_qps = 0;
u32 fast_qpmd_qps;
u8 pg_lvl = 1;
int i, rc;
rc = bnge_hwrm_func_backing_store_qcaps(bd);
if (rc) {
dev_err(bd->dev, "Failed querying ctx mem caps, rc: %d\n", rc);
return rc;
}
ctx = bd->ctx;
if (!ctx || (ctx->flags & BNGE_CTX_FLAG_INITED))
return 0;
ctxm = &ctx->ctx_arr[BNGE_CTX_QP];
l2_qps = ctxm->qp_l2_entries;
qp1_qps = ctxm->qp_qp1_entries;
fast_qpmd_qps = ctxm->qp_fast_qpmd_entries;
max_qps = ctxm->max_entries;
ctxm = &ctx->ctx_arr[BNGE_CTX_SRQ];
srqs = ctxm->srq_l2_entries;
max_srqs = ctxm->max_entries;
ena = 0;
if (bnge_is_roce_en(bd) && !is_kdump_kernel()) {
pg_lvl = 2;
extra_qps = min_t(u32, 65536, max_qps - l2_qps - qp1_qps);
/* allocate extra qps if fast qp destroy feature enabled */
extra_qps += fast_qpmd_qps;
extra_srqs = min_t(u32, 8192, max_srqs - srqs);
if (fast_qpmd_qps)
ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_QP_FAST_QPMD;
}
ctxm = &ctx->ctx_arr[BNGE_CTX_QP];
rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, l2_qps + qp1_qps + extra_qps,
pg_lvl);
if (rc)
return rc;
ctxm = &ctx->ctx_arr[BNGE_CTX_SRQ];
rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, srqs + extra_srqs, pg_lvl);
if (rc)
return rc;
ctxm = &ctx->ctx_arr[BNGE_CTX_CQ];
rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, ctxm->cq_l2_entries +
extra_qps * 2, pg_lvl);
if (rc)
return rc;
ctxm = &ctx->ctx_arr[BNGE_CTX_VNIC];
rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, ctxm->max_entries, 1);
if (rc)
return rc;
ctxm = &ctx->ctx_arr[BNGE_CTX_STAT];
rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, ctxm->max_entries, 1);
if (rc)
return rc;
if (!bnge_is_roce_en(bd))
goto skip_rdma;
ctxm = &ctx->ctx_arr[BNGE_CTX_TIM];
rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, l2_qps + qp1_qps + extra_qps, 1);
if (rc)
return rc;
ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM;
skip_rdma:
ctxm = &ctx->ctx_arr[BNGE_CTX_STQM];
min = ctxm->min_entries;
entries_sp = ctx->ctx_arr[BNGE_CTX_VNIC].vnic_entries + l2_qps +
2 * (extra_qps + qp1_qps) + min;
rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, entries_sp, 2);
if (rc)
return rc;
ctxm = &ctx->ctx_arr[BNGE_CTX_FTQM];
entries = l2_qps + 2 * (extra_qps + qp1_qps);
rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, entries, 2);
if (rc)
return rc;
for (i = 0; i < ctx->tqm_fp_rings_count + 1; i++)
ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_SP << i;
ena |= FUNC_BACKING_STORE_CFG_REQ_DFLT_ENABLES;
rc = bnge_backing_store_cfg(bd, ena);
if (rc) {
dev_err(bd->dev, "Failed configuring ctx mem, rc: %d\n", rc);
return rc;
}
ctx->flags |= BNGE_CTX_FLAG_INITED;
return 0;
}
void bnge_init_ring_struct(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
int i, j;
for (i = 0; i < bd->nq_nr_rings; i++) {
struct bnge_napi *bnapi = bn->bnapi[i];
struct bnge_ring_mem_info *rmem;
struct bnge_nq_ring_info *nqr;
struct bnge_rx_ring_info *rxr;
struct bnge_tx_ring_info *txr;
struct bnge_ring_struct *ring;
nqr = &bnapi->nq_ring;
ring = &nqr->ring_struct;
rmem = &ring->ring_mem;
rmem->nr_pages = bn->cp_nr_pages;
rmem->page_size = HW_CMPD_RING_SIZE;
rmem->pg_arr = (void **)nqr->desc_ring;
rmem->dma_arr = nqr->desc_mapping;
rmem->vmem_size = 0;
rxr = bnapi->rx_ring;
if (!rxr)
goto skip_rx;
ring = &rxr->rx_ring_struct;
rmem = &ring->ring_mem;
rmem->nr_pages = bn->rx_nr_pages;
rmem->page_size = HW_RXBD_RING_SIZE;
rmem->pg_arr = (void **)rxr->rx_desc_ring;
rmem->dma_arr = rxr->rx_desc_mapping;
rmem->vmem_size = SW_RXBD_RING_SIZE * bn->rx_nr_pages;
rmem->vmem = (void **)&rxr->rx_buf_ring;
ring = &rxr->rx_agg_ring_struct;
rmem = &ring->ring_mem;
rmem->nr_pages = bn->rx_agg_nr_pages;
rmem->page_size = HW_RXBD_RING_SIZE;
rmem->pg_arr = (void **)rxr->rx_agg_desc_ring;
rmem->dma_arr = rxr->rx_agg_desc_mapping;
rmem->vmem_size = SW_RXBD_AGG_RING_SIZE * bn->rx_agg_nr_pages;
rmem->vmem = (void **)&rxr->rx_agg_buf_ring;
skip_rx:
bnge_for_each_napi_tx(j, bnapi, txr) {
ring = &txr->tx_ring_struct;
rmem = &ring->ring_mem;
rmem->nr_pages = bn->tx_nr_pages;
rmem->page_size = HW_TXBD_RING_SIZE;
rmem->pg_arr = (void **)txr->tx_desc_ring;
rmem->dma_arr = txr->tx_desc_mapping;
rmem->vmem_size = SW_TXBD_RING_SIZE * bn->tx_nr_pages;
rmem->vmem = (void **)&txr->tx_buf_ring;
}
}
}
@@ -0,0 +1,202 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_RMEM_H_
#define _BNGE_RMEM_H_
struct bnge_ctx_mem_type;
struct bnge_dev;
struct bnge_net;
#define PTU_PTE_VALID 0x1UL
#define PTU_PTE_LAST 0x2UL
#define PTU_PTE_NEXT_TO_LAST 0x4UL
struct bnge_ring_mem_info {
/* Number of pages to next level */
int nr_pages;
int page_size;
u16 flags;
#define BNGE_RMEM_VALID_PTE_FLAG 1
#define BNGE_RMEM_RING_PTE_FLAG 2
#define BNGE_RMEM_USE_FULL_PAGE_FLAG 4
u16 depth;
void **pg_arr;
dma_addr_t *dma_arr;
__le64 *pg_tbl;
dma_addr_t dma_pg_tbl;
int vmem_size;
void **vmem;
struct bnge_ctx_mem_type *ctx_mem;
};
/* The hardware supports certain page sizes.
* Use the supported page sizes to allocate the rings.
*/
#if (PAGE_SHIFT < 12)
#define BNGE_PAGE_SHIFT 12
#elif (PAGE_SHIFT <= 13)
#define BNGE_PAGE_SHIFT PAGE_SHIFT
#elif (PAGE_SHIFT < 16)
#define BNGE_PAGE_SHIFT 13
#else
#define BNGE_PAGE_SHIFT 16
#endif
#define BNGE_PAGE_SIZE (1 << BNGE_PAGE_SHIFT)
/* The RXBD length is 16-bit so we can only support page sizes < 64K */
#if (PAGE_SHIFT > 15)
#define BNGE_RX_PAGE_SHIFT 15
#else
#define BNGE_RX_PAGE_SHIFT PAGE_SHIFT
#endif
#define MAX_CTX_PAGES (BNGE_PAGE_SIZE / 8)
#define MAX_CTX_TOTAL_PAGES (MAX_CTX_PAGES * MAX_CTX_PAGES)
struct bnge_ctx_pg_info {
u32 entries;
u32 nr_pages;
void *ctx_pg_arr[MAX_CTX_PAGES];
dma_addr_t ctx_dma_arr[MAX_CTX_PAGES];
struct bnge_ring_mem_info ring_mem;
struct bnge_ctx_pg_info **ctx_pg_tbl;
};
#define BNGE_MAX_TQM_SP_RINGS 1
#define BNGE_MAX_TQM_FP_RINGS 8
#define BNGE_MAX_TQM_RINGS \
(BNGE_MAX_TQM_SP_RINGS + BNGE_MAX_TQM_FP_RINGS)
#define BNGE_BACKING_STORE_CFG_LEGACY_LEN 256
#define BNGE_SET_CTX_PAGE_ATTR(attr) \
do { \
if (BNGE_PAGE_SIZE == 0x2000) \
attr = FUNC_BACKING_STORE_CFG_REQ_SRQ_PG_SIZE_PG_8K; \
else if (BNGE_PAGE_SIZE == 0x10000) \
attr = FUNC_BACKING_STORE_CFG_REQ_QPC_PG_SIZE_PG_64K; \
else \
attr = FUNC_BACKING_STORE_CFG_REQ_QPC_PG_SIZE_PG_4K; \
} while (0)
#define BNGE_CTX_MRAV_AV_SPLIT_ENTRY 0
#define BNGE_CTX_QP \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_QP
#define BNGE_CTX_SRQ \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_SRQ
#define BNGE_CTX_CQ \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_CQ
#define BNGE_CTX_VNIC \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_VNIC
#define BNGE_CTX_STAT \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_STAT
#define BNGE_CTX_STQM \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_SP_TQM_RING
#define BNGE_CTX_FTQM \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_FP_TQM_RING
#define BNGE_CTX_MRAV \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_MRAV
#define BNGE_CTX_TIM \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_TIM
#define BNGE_CTX_TCK \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_TX_CK
#define BNGE_CTX_RCK \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_RX_CK
#define BNGE_CTX_MTQM \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_MP_TQM_RING
#define BNGE_CTX_SQDBS \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_SQ_DB_SHADOW
#define BNGE_CTX_RQDBS \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_RQ_DB_SHADOW
#define BNGE_CTX_SRQDBS \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_SRQ_DB_SHADOW
#define BNGE_CTX_CQDBS \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_CQ_DB_SHADOW
#define BNGE_CTX_SRT_TRACE \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_SRT_TRACE
#define BNGE_CTX_SRT2_TRACE \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_SRT2_TRACE
#define BNGE_CTX_CRT_TRACE \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_CRT_TRACE
#define BNGE_CTX_CRT2_TRACE \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_CRT2_TRACE
#define BNGE_CTX_RIGP0_TRACE \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_RIGP0_TRACE
#define BNGE_CTX_L2_HWRM_TRACE \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_L2_HWRM_TRACE
#define BNGE_CTX_ROCE_HWRM_TRACE \
FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_ROCE_HWRM_TRACE
#define BNGE_CTX_MAX (BNGE_CTX_TIM + 1)
#define BNGE_CTX_L2_MAX (BNGE_CTX_FTQM + 1)
#define BNGE_CTX_INV ((u16)-1)
#define BNGE_CTX_V2_MAX \
(FUNC_BACKING_STORE_QCAPS_V2_REQ_TYPE_ROCE_HWRM_TRACE + 1)
#define BNGE_BS_CFG_ALL_DONE \
FUNC_BACKING_STORE_CFG_V2_REQ_FLAGS_BS_CFG_ALL_DONE
struct bnge_ctx_mem_type {
u16 type;
u16 entry_size;
u32 flags;
#define BNGE_CTX_MEM_TYPE_VALID \
FUNC_BACKING_STORE_QCAPS_V2_RESP_FLAGS_TYPE_VALID
u32 instance_bmap;
u8 init_value;
u8 entry_multiple;
u16 init_offset;
#define BNGE_CTX_INIT_INVALID_OFFSET 0xffff
u32 max_entries;
u32 min_entries;
u8 last:1;
u8 split_entry_cnt;
#define BNGE_MAX_SPLIT_ENTRY 4
union {
struct {
u32 qp_l2_entries;
u32 qp_qp1_entries;
u32 qp_fast_qpmd_entries;
};
u32 srq_l2_entries;
u32 cq_l2_entries;
u32 vnic_entries;
struct {
u32 mrav_av_entries;
u32 mrav_num_entries_units;
};
u32 split[BNGE_MAX_SPLIT_ENTRY];
};
struct bnge_ctx_pg_info *pg_info;
};
struct bnge_ctx_mem_info {
u8 tqm_fp_rings_count;
u32 flags;
#define BNGE_CTX_FLAG_INITED 0x01
struct bnge_ctx_mem_type ctx_arr[BNGE_CTX_V2_MAX];
};
struct bnge_ring_struct {
struct bnge_ring_mem_info ring_mem;
u32 fw_ring_id;
union {
u16 grp_idx;
u16 map_idx; /* Used by NQs */
};
u32 handle;
u8 queue_id;
};
int bnge_alloc_ring(struct bnge_dev *bd, struct bnge_ring_mem_info *rmem);
void bnge_free_ring(struct bnge_dev *bd, struct bnge_ring_mem_info *rmem);
int bnge_alloc_ctx_mem(struct bnge_dev *bd);
void bnge_free_ctx_mem(struct bnge_dev *bd);
void bnge_init_ring_struct(struct bnge_net *bn);
#endif /* _BNGE_RMEM_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,126 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Broadcom */
#ifndef _BNGE_TXRX_H_
#define _BNGE_TXRX_H_
#include <linux/bnge/hsi.h>
#include "bnge_netdev.h"
static inline u32 bnge_tx_avail(struct bnge_net *bn,
const struct bnge_tx_ring_info *txr)
{
u32 used = READ_ONCE(txr->tx_prod) - READ_ONCE(txr->tx_cons);
return bn->tx_ring_size - (used & bn->tx_ring_mask);
}
static inline void bnge_writeq_relaxed(struct bnge_dev *bd, u64 val,
void __iomem *addr)
{
#if BITS_PER_LONG == 32
spin_lock(&bd->db_lock);
lo_hi_writeq_relaxed(val, addr);
spin_unlock(&bd->db_lock);
#else
writeq_relaxed(val, addr);
#endif
}
/* For TX and RX ring doorbells with no ordering guarantee*/
static inline void bnge_db_write_relaxed(struct bnge_net *bn,
struct bnge_db_info *db, u32 idx)
{
bnge_writeq_relaxed(bn->bd, db->db_key64 | DB_RING_IDX(db, idx),
db->doorbell);
}
#define TX_OPAQUE_IDX_MASK 0x0000ffff
#define TX_OPAQUE_BDS_MASK 0x00ff0000
#define TX_OPAQUE_BDS_SHIFT 16
#define TX_OPAQUE_RING_MASK 0xff000000
#define TX_OPAQUE_RING_SHIFT 24
#define SET_TX_OPAQUE(bn, txr, idx, bds) \
(((txr)->tx_napi_idx << TX_OPAQUE_RING_SHIFT) | \
((bds) << TX_OPAQUE_BDS_SHIFT) | ((idx) & (bn)->tx_ring_mask))
#define TX_OPAQUE_IDX(opq) ((opq) & TX_OPAQUE_IDX_MASK)
#define TX_OPAQUE_RING(opq) (((opq) & TX_OPAQUE_RING_MASK) >> \
TX_OPAQUE_RING_SHIFT)
#define TX_OPAQUE_BDS(opq) (((opq) & TX_OPAQUE_BDS_MASK) >> \
TX_OPAQUE_BDS_SHIFT)
#define TX_OPAQUE_PROD(bn, opq) ((TX_OPAQUE_IDX(opq) + TX_OPAQUE_BDS(opq)) &\
(bn)->tx_ring_mask)
#define TX_BD_CNT(n) (((n) << TX_BD_FLAGS_BD_CNT_SHIFT) & TX_BD_FLAGS_BD_CNT)
#define TX_MAX_BD_CNT 32
#define TX_MAX_FRAGS (TX_MAX_BD_CNT - 2)
/* Minimum TX BDs for a TX packet with MAX_SKB_FRAGS + 1. We need one extra
* BD because the first TX BD is always a long BD.
*/
#define BNGE_MIN_TX_DESC_CNT (MAX_SKB_FRAGS + 2)
#define RX_RING(bn, x) (((x) & (bn)->rx_ring_mask) >> (BNGE_PAGE_SHIFT - 4))
#define RX_AGG_RING(bn, x) (((x) & (bn)->rx_agg_ring_mask) >> \
(BNGE_PAGE_SHIFT - 4))
#define RX_IDX(x) ((x) & (RX_DESC_CNT - 1))
#define TX_RING(bn, x) (((x) & (bn)->tx_ring_mask) >> (BNGE_PAGE_SHIFT - 4))
#define TX_IDX(x) ((x) & (TX_DESC_CNT - 1))
#define CP_RING(x) (((x) & ~(CP_DESC_CNT - 1)) >> (BNGE_PAGE_SHIFT - 4))
#define CP_IDX(x) ((x) & (CP_DESC_CNT - 1))
#define TX_CMP_VALID(bn, txcmp, raw_cons) \
(!!((txcmp)->tx_cmp_errors_v & cpu_to_le32(TX_CMP_V)) == \
!((raw_cons) & (bn)->cp_bit))
#define RX_CMP_VALID(bn, rxcmp1, raw_cons) \
(!!((rxcmp1)->rx_cmp_cfa_code_errors_v2 & cpu_to_le32(RX_CMP_V)) ==\
!((raw_cons) & (bn)->cp_bit))
#define RX_AGG_CMP_VALID(bn, agg, raw_cons) \
(!!((agg)->rx_agg_cmp_v & cpu_to_le32(RX_AGG_CMP_V)) == \
!((raw_cons) & (bn)->cp_bit))
#define NQ_CMP_VALID(bn, nqcmp, raw_cons) \
(!!((nqcmp)->v & cpu_to_le32(NQ_CN_V)) == !((raw_cons) & (bn)->cp_bit))
#define TX_CMP_TYPE(txcmp) \
(le32_to_cpu((txcmp)->tx_cmp_flags_type) & CMP_TYPE)
#define RX_CMP_TYPE(rxcmp) \
(le32_to_cpu((rxcmp)->rx_cmp_len_flags_type) & RX_CMP_CMP_TYPE)
#define RING_RX(bn, idx) ((idx) & (bn)->rx_ring_mask)
#define NEXT_RX(idx) ((idx) + 1)
#define RING_RX_AGG(bn, idx) ((idx) & (bn)->rx_agg_ring_mask)
#define NEXT_RX_AGG(idx) ((idx) + 1)
#define SW_TX_RING(bn, idx) ((idx) & (bn)->tx_ring_mask)
#define NEXT_TX(idx) ((idx) + 1)
#define ADV_RAW_CMP(idx, n) ((idx) + (n))
#define NEXT_RAW_CMP(idx) ADV_RAW_CMP(idx, 1)
#define RING_CMP(bn, idx) ((idx) & (bn)->cp_ring_mask)
#define NEXT_CMP(bn, idx) RING_CMP(bn, ADV_RAW_CMP(idx, 1))
#define RX_CMP_ITYPES(rxcmp) \
(le32_to_cpu((rxcmp)->rx_cmp_len_flags_type) & RX_CMP_FLAGS_ITYPES_MASK)
#define RX_CMP_CFA_CODE(rxcmpl1) \
((le32_to_cpu((rxcmpl1)->rx_cmp_cfa_code_errors_v2) & \
RX_CMPL_CFA_CODE_MASK) >> RX_CMPL_CFA_CODE_SFT)
irqreturn_t bnge_msix(int irq, void *dev_instance);
netdev_tx_t bnge_start_xmit(struct sk_buff *skb, struct net_device *dev);
void bnge_reuse_rx_data(struct bnge_rx_ring_info *rxr, u16 cons, void *data);
int bnge_napi_poll(struct napi_struct *napi, int budget);
netdev_features_t bnge_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features);
#endif /* _BNGE_TXRX_H_ */