Commit af58de31 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'eth-fbnic-add-network-driver-for-meta-platforms-host-network-interface'

Alexander Duyck says:

====================
eth: fbnic: Add network driver for Meta Platforms Host Network Interface

This patch set includes the necessary patches to enable basic Tx and Rx
over the Meta Platforms Host Network Interface. To do this we introduce a
new driver and driver directories in the form of
"drivers/net/ethernet/meta/fbnic".

The NIC itself is fairly simplistic. As far as speeds we support 25Gb,
50Gb, and 100Gb and we are mostly focused on speeds and feeds. As far as
future patch sets we will be supporting the basic Rx/Tx offloads such as
header/payload data split, TSO, checksum, and timestamp offloads. We have
access to the MAC and PCS from the NIC, however the PHY and QSFP are hidden
behind a FW layer as it is shared between 4 slices and the BMC.

Due to submission limits the general plan to submit a minimal driver for
now almost equivalent to a UEFI driver in functionality, and then follow up
over the coming months enabling additional offloads and enabling more
features for the device.
====================

Link: https://patch.msgid.link/172079913640.1778861.11459276843992867323.stgit@ahduyck-xeon-server.home.arpaSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents dd3cd3ca 355440a6
......@@ -14579,6 +14579,13 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/amlogic,gx-vdec.yaml
F: drivers/staging/media/meson/vdec/
META ETHERNET DRIVERS
M: Alexander Duyck <alexanderduyck@fb.com>
M: Jakub Kicinski <kuba@kernel.org>
R: kernel-team@meta.com
S: Supported
F: drivers/net/ethernet/meta/
METHODE UDPU SUPPORT
M: Robert Marko <robert.marko@sartura.hr>
S: Maintained
......
......@@ -122,6 +122,7 @@ source "drivers/net/ethernet/litex/Kconfig"
source "drivers/net/ethernet/marvell/Kconfig"
source "drivers/net/ethernet/mediatek/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/meta/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
source "drivers/net/ethernet/mscc/Kconfig"
......
......@@ -59,6 +59,7 @@ obj-$(CONFIG_NET_VENDOR_LITEX) += litex/
obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_NET_VENDOR_MEDIATEK) += mediatek/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_META) += meta/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/
......
# SPDX-License-Identifier: GPL-2.0-only
#
# Meta Platforms network device configuration
#
config NET_VENDOR_META
bool "Meta Platforms devices"
default y
help
If you have a network (Ethernet) card designed by Meta, say Y.
That's Meta as in the parent company of Facebook.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about Meta cards. If you say Y, you will be asked for
your specific card in the following questions.
if NET_VENDOR_META
config FBNIC
tristate "Meta Platforms Host Network Interface"
depends on X86_64 || COMPILE_TEST
depends on PCI_MSI
select PHYLINK
help
This driver supports Meta Platforms Host Network Interface.
To compile this driver as a module, choose M here. The module
will be called fbnic. MSI-X interrupt support is required.
endif # NET_VENDOR_META
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Meta Platforms network device drivers.
#
obj-$(CONFIG_FBNIC) += fbnic/
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Makefile for the Meta(R) Host Network Interface
#
obj-$(CONFIG_FBNIC) += fbnic.o
fbnic-y := fbnic_devlink.o \
fbnic_fw.o \
fbnic_irq.o \
fbnic_mac.o \
fbnic_netdev.o \
fbnic_pci.o \
fbnic_phylink.o \
fbnic_rpc.o \
fbnic_tlv.o \
fbnic_txrx.o
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#ifndef _FBNIC_H_
#define _FBNIC_H_
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include "fbnic_csr.h"
#include "fbnic_fw.h"
#include "fbnic_mac.h"
#include "fbnic_rpc.h"
struct fbnic_dev {
struct device *dev;
struct net_device *netdev;
u32 __iomem *uc_addr0;
u32 __iomem *uc_addr4;
const struct fbnic_mac *mac;
unsigned int fw_msix_vector;
unsigned int pcs_msix_vector;
unsigned short num_irqs;
struct delayed_work service_task;
struct fbnic_fw_mbx mbx[FBNIC_IPC_MBX_INDICES];
struct fbnic_fw_cap fw_cap;
/* Lock protecting Tx Mailbox queue to prevent possible races */
spinlock_t fw_tx_lock;
unsigned long last_heartbeat_request;
unsigned long last_heartbeat_response;
u8 fw_heartbeat_enabled;
u64 dsn;
u32 mps;
u32 readrq;
/* Local copy of the devices TCAM */
struct fbnic_act_tcam act_tcam[FBNIC_RPC_TCAM_ACT_NUM_ENTRIES];
struct fbnic_mac_addr mac_addr[FBNIC_RPC_TCAM_MACDA_NUM_ENTRIES];
u8 mac_addr_boundary;
/* Number of TCQs/RCQs available on hardware */
u16 max_num_queues;
};
/* Reserve entry 0 in the MSI-X "others" array until we have filled all
* 32 of the possible interrupt slots. By doing this we can avoid any
* potential conflicts should we need to enable one of the debug interrupt
* causes later.
*/
enum {
FBNIC_FW_MSIX_ENTRY,
FBNIC_PCS_MSIX_ENTRY,
FBNIC_NON_NAPI_VECTORS
};
static inline bool fbnic_present(struct fbnic_dev *fbd)
{
return !!READ_ONCE(fbd->uc_addr0);
}
static inline void fbnic_wr32(struct fbnic_dev *fbd, u32 reg, u32 val)
{
u32 __iomem *csr = READ_ONCE(fbd->uc_addr0);
if (csr)
writel(val, csr + reg);
}
u32 fbnic_rd32(struct fbnic_dev *fbd, u32 reg);
static inline void fbnic_wrfl(struct fbnic_dev *fbd)
{
fbnic_rd32(fbd, FBNIC_MASTER_SPARE_0);
}
static inline void
fbnic_rmw32(struct fbnic_dev *fbd, u32 reg, u32 mask, u32 val)
{
u32 v;
v = fbnic_rd32(fbd, reg);
v &= ~mask;
v |= val;
fbnic_wr32(fbd, reg, v);
}
#define wr32(_f, _r, _v) fbnic_wr32(_f, _r, _v)
#define rd32(_f, _r) fbnic_rd32(_f, _r)
#define wrfl(_f) fbnic_wrfl(_f)
bool fbnic_fw_present(struct fbnic_dev *fbd);
u32 fbnic_fw_rd32(struct fbnic_dev *fbd, u32 reg);
void fbnic_fw_wr32(struct fbnic_dev *fbd, u32 reg, u32 val);
#define fw_rd32(_f, _r) fbnic_fw_rd32(_f, _r)
#define fw_wr32(_f, _r, _v) fbnic_fw_wr32(_f, _r, _v)
#define fw_wrfl(_f) fbnic_fw_rd32(_f, FBNIC_FW_ZERO_REG)
static inline bool fbnic_bmc_present(struct fbnic_dev *fbd)
{
return fbd->fw_cap.bmc_present;
}
static inline bool fbnic_init_failure(struct fbnic_dev *fbd)
{
return !fbd->netdev;
}
extern char fbnic_driver_name[];
void fbnic_devlink_free(struct fbnic_dev *fbd);
struct fbnic_dev *fbnic_devlink_alloc(struct pci_dev *pdev);
void fbnic_devlink_register(struct fbnic_dev *fbd);
void fbnic_devlink_unregister(struct fbnic_dev *fbd);
int fbnic_fw_enable_mbx(struct fbnic_dev *fbd);
void fbnic_fw_disable_mbx(struct fbnic_dev *fbd);
int fbnic_pcs_irq_enable(struct fbnic_dev *fbd);
void fbnic_pcs_irq_disable(struct fbnic_dev *fbd);
int fbnic_request_irq(struct fbnic_dev *dev, int nr, irq_handler_t handler,
unsigned long flags, const char *name, void *data);
void fbnic_free_irq(struct fbnic_dev *dev, int nr, void *data);
void fbnic_free_irqs(struct fbnic_dev *fbd);
int fbnic_alloc_irqs(struct fbnic_dev *fbd);
enum fbnic_boards {
fbnic_board_asic
};
struct fbnic_info {
unsigned int max_num_queues;
unsigned int bar_mask;
};
#endif /* _FBNIC_H_ */
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#include <asm/unaligned.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <net/devlink.h>
#include "fbnic.h"
#define FBNIC_SN_STR_LEN 24
static int fbnic_devlink_info_get(struct devlink *devlink,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct fbnic_dev *fbd = devlink_priv(devlink);
int err;
if (fbd->dsn) {
unsigned char serial[FBNIC_SN_STR_LEN];
u8 dsn[8];
put_unaligned_be64(fbd->dsn, dsn);
err = snprintf(serial, FBNIC_SN_STR_LEN, "%8phD", dsn);
if (err < 0)
return err;
err = devlink_info_serial_number_put(req, serial);
if (err)
return err;
}
return 0;
}
static const struct devlink_ops fbnic_devlink_ops = {
.info_get = fbnic_devlink_info_get,
};
void fbnic_devlink_free(struct fbnic_dev *fbd)
{
struct devlink *devlink = priv_to_devlink(fbd);
devlink_free(devlink);
}
struct fbnic_dev *fbnic_devlink_alloc(struct pci_dev *pdev)
{
void __iomem * const *iomap_table;
struct devlink *devlink;
struct fbnic_dev *fbd;
devlink = devlink_alloc(&fbnic_devlink_ops, sizeof(struct fbnic_dev),
&pdev->dev);
if (!devlink)
return NULL;
fbd = devlink_priv(devlink);
pci_set_drvdata(pdev, fbd);
fbd->dev = &pdev->dev;
iomap_table = pcim_iomap_table(pdev);
fbd->uc_addr0 = iomap_table[0];
fbd->uc_addr4 = iomap_table[4];
fbd->dsn = pci_get_dsn(pdev);
fbd->mps = pcie_get_mps(pdev);
fbd->readrq = pcie_get_readrq(pdev);
fbd->mac_addr_boundary = FBNIC_RPC_TCAM_MACDA_DEFAULT_BOUNDARY;
return fbd;
}
void fbnic_devlink_register(struct fbnic_dev *fbd)
{
struct devlink *devlink = priv_to_devlink(fbd);
devlink_register(devlink);
}
void fbnic_devlink_unregister(struct fbnic_dev *fbd)
{
struct devlink *devlink = priv_to_devlink(fbd);
devlink_unregister(devlink);
}
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#define DRV_NAME "fbnic"
#define DRV_SUMMARY "Meta(R) Host Network Interface Driver"
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#ifndef _FBNIC_FW_H_
#define _FBNIC_FW_H_
#include <linux/if_ether.h>
#include <linux/types.h>
struct fbnic_dev;
struct fbnic_tlv_msg;
struct fbnic_fw_mbx {
u8 ready, head, tail;
struct {
struct fbnic_tlv_msg *msg;
dma_addr_t addr;
} buf_info[FBNIC_IPC_MBX_DESC_LEN];
};
// FW_VER_MAX_SIZE must match ETHTOOL_FWVERS_LEN
#define FBNIC_FW_VER_MAX_SIZE 32
// Formatted version is in the format XX.YY.ZZ_RRR_COMMIT
#define FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE (FBNIC_FW_VER_MAX_SIZE - 13)
#define FBNIC_FW_LOG_MAX_SIZE 256
struct fbnic_fw_ver {
u32 version;
char commit[FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE];
};
struct fbnic_fw_cap {
struct {
struct fbnic_fw_ver mgmt, bootloader;
} running;
struct {
struct fbnic_fw_ver mgmt, bootloader, undi;
} stored;
u8 active_slot;
u8 bmc_mac_addr[4][ETH_ALEN];
u8 bmc_present : 1;
u8 all_multi : 1;
u8 link_speed;
u8 link_fec;
};
void fbnic_mbx_init(struct fbnic_dev *fbd);
void fbnic_mbx_clean(struct fbnic_dev *fbd);
void fbnic_mbx_poll(struct fbnic_dev *fbd);
int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd);
void fbnic_mbx_flush_tx(struct fbnic_dev *fbd);
int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership);
int fbnic_fw_init_heartbeat(struct fbnic_dev *fbd, bool poll);
void fbnic_fw_check_heartbeat(struct fbnic_dev *fbd);
#define fbnic_mk_full_fw_ver_str(_rev_id, _delim, _commit, _str) \
do { \
const u32 __rev_id = _rev_id; \
snprintf(_str, sizeof(_str), "%02lu.%02lu.%02lu-%03lu%s%s", \
FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_MAJOR, __rev_id), \
FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_MINOR, __rev_id), \
FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_PATCH, __rev_id), \
FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_BUILD, __rev_id), \
_delim, _commit); \
} while (0)
#define fbnic_mk_fw_ver_str(_rev_id, _str) \
fbnic_mk_full_fw_ver_str(_rev_id, "", "", _str)
#define FW_HEARTBEAT_PERIOD (10 * HZ)
enum {
FBNIC_TLV_MSG_ID_HOST_CAP_REQ = 0x10,
FBNIC_TLV_MSG_ID_FW_CAP_RESP = 0x11,
FBNIC_TLV_MSG_ID_OWNERSHIP_REQ = 0x12,
FBNIC_TLV_MSG_ID_OWNERSHIP_RESP = 0x13,
FBNIC_TLV_MSG_ID_HEARTBEAT_REQ = 0x14,
FBNIC_TLV_MSG_ID_HEARTBEAT_RESP = 0x15,
};
#define FBNIC_FW_CAP_RESP_VERSION_MAJOR CSR_GENMASK(31, 24)
#define FBNIC_FW_CAP_RESP_VERSION_MINOR CSR_GENMASK(23, 16)
#define FBNIC_FW_CAP_RESP_VERSION_PATCH CSR_GENMASK(15, 8)
#define FBNIC_FW_CAP_RESP_VERSION_BUILD CSR_GENMASK(7, 0)
enum {
FBNIC_FW_CAP_RESP_VERSION = 0x0,
FBNIC_FW_CAP_RESP_BMC_PRESENT = 0x1,
FBNIC_FW_CAP_RESP_BMC_MAC_ADDR = 0x2,
FBNIC_FW_CAP_RESP_BMC_MAC_ARRAY = 0x3,
FBNIC_FW_CAP_RESP_STORED_VERSION = 0x4,
FBNIC_FW_CAP_RESP_ACTIVE_FW_SLOT = 0x5,
FBNIC_FW_CAP_RESP_VERSION_COMMIT_STR = 0x6,
FBNIC_FW_CAP_RESP_BMC_ALL_MULTI = 0x8,
FBNIC_FW_CAP_RESP_FW_STATE = 0x9,
FBNIC_FW_CAP_RESP_FW_LINK_SPEED = 0xa,
FBNIC_FW_CAP_RESP_FW_LINK_FEC = 0xb,
FBNIC_FW_CAP_RESP_STORED_COMMIT_STR = 0xc,
FBNIC_FW_CAP_RESP_CMRT_VERSION = 0xd,
FBNIC_FW_CAP_RESP_STORED_CMRT_VERSION = 0xe,
FBNIC_FW_CAP_RESP_CMRT_COMMIT_STR = 0xf,
FBNIC_FW_CAP_RESP_STORED_CMRT_COMMIT_STR = 0x10,
FBNIC_FW_CAP_RESP_UEFI_VERSION = 0x11,
FBNIC_FW_CAP_RESP_UEFI_COMMIT_STR = 0x12,
FBNIC_FW_CAP_RESP_MSG_MAX
};
enum {
FBNIC_FW_LINK_SPEED_25R1 = 1,
FBNIC_FW_LINK_SPEED_50R2 = 2,
FBNIC_FW_LINK_SPEED_50R1 = 3,
FBNIC_FW_LINK_SPEED_100R2 = 4,
};
enum {
FBNIC_FW_LINK_FEC_NONE = 1,
FBNIC_FW_LINK_FEC_RS = 2,
FBNIC_FW_LINK_FEC_BASER = 3,
};
enum {
FBNIC_FW_OWNERSHIP_FLAG = 0x0,
FBNIC_FW_OWNERSHIP_MSG_MAX
};
#endif /* _FBNIC_FW_H_ */
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#include <linux/pci.h>
#include <linux/types.h>
#include "fbnic.h"
#include "fbnic_netdev.h"
#include "fbnic_txrx.h"
static irqreturn_t fbnic_fw_msix_intr(int __always_unused irq, void *data)
{
struct fbnic_dev *fbd = (struct fbnic_dev *)data;
fbnic_mbx_poll(fbd);
fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
return IRQ_HANDLED;
}
/**
* fbnic_fw_enable_mbx - Configure and initialize Firmware Mailbox
* @fbd: Pointer to device to initialize
*
* This function will initialize the firmware mailbox rings, enable the IRQ
* and initialize the communication between the Firmware and the host. The
* firmware is expected to respond to the initialization by sending an
* interrupt essentially notifying the host that it has seen the
* initialization and is now synced up.
*
* Return: non-zero on failure.
**/
int fbnic_fw_enable_mbx(struct fbnic_dev *fbd)
{
u32 vector = fbd->fw_msix_vector;
int err;
/* Request the IRQ for FW Mailbox vector. */
err = request_threaded_irq(vector, NULL, &fbnic_fw_msix_intr,
IRQF_ONESHOT, dev_name(fbd->dev), fbd);
if (err)
return err;
/* Initialize mailbox and attempt to poll it into ready state */
fbnic_mbx_init(fbd);
err = fbnic_mbx_poll_tx_ready(fbd);
if (err) {
dev_warn(fbd->dev, "FW mailbox did not enter ready state\n");
free_irq(vector, fbd);
return err;
}
/* Enable interrupts */
fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
return 0;
}
/**
* fbnic_fw_disable_mbx - Disable mailbox and place it in standby state
* @fbd: Pointer to device to disable
*
* This function will disable the mailbox interrupt, free any messages still
* in the mailbox and place it into a standby state. The firmware is
* expected to see the update and assume that the host is in the reset state.
**/
void fbnic_fw_disable_mbx(struct fbnic_dev *fbd)
{
/* Disable interrupt and free vector */
fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_FW_MSIX_ENTRY);
/* Free the vector */
free_irq(fbd->fw_msix_vector, fbd);
/* Make sure disabling logs message is sent, must be done here to
* avoid risk of completing without a running interrupt.
*/
fbnic_mbx_flush_tx(fbd);
/* Reset the mailboxes to the initialized state */
fbnic_mbx_clean(fbd);
}
static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data)
{
struct fbnic_dev *fbd = data;
struct fbnic_net *fbn;
if (fbd->mac->pcs_get_link_event(fbd) == FBNIC_LINK_EVENT_NONE) {
fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0),
1u << FBNIC_PCS_MSIX_ENTRY);
return IRQ_HANDLED;
}
fbn = netdev_priv(fbd->netdev);
phylink_pcs_change(&fbn->phylink_pcs, false);
return IRQ_HANDLED;
}
/**
* fbnic_pcs_irq_enable - Configure the MAC to enable it to advertise link
* @fbd: Pointer to device to initialize
*
* This function provides basic bringup for the MAC/PCS IRQ. For now the IRQ
* will remain disabled until we start the MAC/PCS/PHY logic via phylink.
*
* Return: non-zero on failure.
**/
int fbnic_pcs_irq_enable(struct fbnic_dev *fbd)
{
u32 vector = fbd->pcs_msix_vector;
int err;
/* Request the IRQ for MAC link vector.
* Map MAC cause to it, and unmask it
*/
err = request_irq(vector, &fbnic_pcs_msix_intr, 0,
fbd->netdev->name, fbd);
if (err)
return err;
fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE);
return 0;
}
/**
* fbnic_pcs_irq_disable - Teardown the MAC IRQ to prepare for stopping
* @fbd: Pointer to device that is stopping
*
* This function undoes the work done in fbnic_pcs_irq_enable and prepares
* the device to no longer receive traffic on the host interface.
**/
void fbnic_pcs_irq_disable(struct fbnic_dev *fbd)
{
/* Disable interrupt */
fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
FBNIC_PCS_MSIX_ENTRY);
fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY);
/* Free the vector */
free_irq(fbd->pcs_msix_vector, fbd);
}
int fbnic_request_irq(struct fbnic_dev *fbd, int nr, irq_handler_t handler,
unsigned long flags, const char *name, void *data)
{
struct pci_dev *pdev = to_pci_dev(fbd->dev);
int irq = pci_irq_vector(pdev, nr);
if (irq < 0)
return irq;
return request_irq(irq, handler, flags, name, data);
}
void fbnic_free_irq(struct fbnic_dev *fbd, int nr, void *data)
{
struct pci_dev *pdev = to_pci_dev(fbd->dev);
int irq = pci_irq_vector(pdev, nr);
if (irq < 0)
return;
free_irq(irq, data);
}
void fbnic_free_irqs(struct fbnic_dev *fbd)
{
struct pci_dev *pdev = to_pci_dev(fbd->dev);
fbd->pcs_msix_vector = 0;
fbd->fw_msix_vector = 0;
fbd->num_irqs = 0;
pci_free_irq_vectors(pdev);
}
int fbnic_alloc_irqs(struct fbnic_dev *fbd)
{
unsigned int wanted_irqs = FBNIC_NON_NAPI_VECTORS;
struct pci_dev *pdev = to_pci_dev(fbd->dev);
int num_irqs;
wanted_irqs += min_t(unsigned int, num_online_cpus(), FBNIC_MAX_RXQS);
num_irqs = pci_alloc_irq_vectors(pdev, FBNIC_NON_NAPI_VECTORS + 1,
wanted_irqs, PCI_IRQ_MSIX);
if (num_irqs < 0) {
dev_err(fbd->dev, "Failed to allocate MSI-X entries\n");
return num_irqs;
}
if (num_irqs < wanted_irqs)
dev_warn(fbd->dev, "Allocated %d IRQs, expected %d\n",
num_irqs, wanted_irqs);
fbd->num_irqs = num_irqs;
fbd->pcs_msix_vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY);
fbd->fw_msix_vector = pci_irq_vector(pdev, FBNIC_FW_MSIX_ENTRY);
return 0;
}
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#ifndef _FBNIC_MAC_H_
#define _FBNIC_MAC_H_
#include <linux/types.h>
struct fbnic_dev;
#define FBNIC_MAX_JUMBO_FRAME_SIZE 9742
enum {
FBNIC_LINK_EVENT_NONE = 0,
FBNIC_LINK_EVENT_UP = 1,
FBNIC_LINK_EVENT_DOWN = 2,
};
/* Treat the FEC bits as a bitmask laid out as follows:
* Bit 0: RS Enabled
* Bit 1: BASER(Firecode) Enabled
* Bit 2: Retrieve FEC from FW
*/
enum {
FBNIC_FEC_OFF = 0,
FBNIC_FEC_RS = 1,
FBNIC_FEC_BASER = 2,
FBNIC_FEC_AUTO = 4,
};
#define FBNIC_FEC_MODE_MASK (FBNIC_FEC_AUTO - 1)
/* Treat the link modes as a set of modulation/lanes bitmask:
* Bit 0: Lane Count, 0 = R1, 1 = R2
* Bit 1: Modulation, 0 = NRZ, 1 = PAM4
* Bit 2: Retrieve link mode from FW
*/
enum {
FBNIC_LINK_25R1 = 0,
FBNIC_LINK_50R2 = 1,
FBNIC_LINK_50R1 = 2,
FBNIC_LINK_100R2 = 3,
FBNIC_LINK_AUTO = 4,
};
#define FBNIC_LINK_MODE_R2 (FBNIC_LINK_50R2)
#define FBNIC_LINK_MODE_PAM4 (FBNIC_LINK_50R1)
#define FBNIC_LINK_MODE_MASK (FBNIC_LINK_AUTO - 1)
/* This structure defines the interface hooks for the MAC. The MAC hooks
* will be configured as a const struct provided with a set of function
* pointers.
*
* void (*init_regs)(struct fbnic_dev *fbd);
* Initialize MAC registers to enable Tx/Rx paths and FIFOs.
*
* void (*pcs_enable)(struct fbnic_dev *fbd);
* Configure and enable PCS to enable link if not already enabled
* void (*pcs_disable)(struct fbnic_dev *fbd);
* Shutdown the link if we are the only consumer of it.
* bool (*pcs_get_link)(struct fbnic_dev *fbd);
* Check PCS link status
* int (*pcs_get_link_event)(struct fbnic_dev *fbd)
* Get the current link event status, reports true if link has
* changed to either FBNIC_LINK_EVENT_DOWN or FBNIC_LINK_EVENT_UP
*
* void (*link_down)(struct fbnic_dev *fbd);
* Configure MAC for link down event
* void (*link_up)(struct fbnic_dev *fbd, bool tx_pause, bool rx_pause);
* Configure MAC for link up event;
*
*/
struct fbnic_mac {
void (*init_regs)(struct fbnic_dev *fbd);
int (*pcs_enable)(struct fbnic_dev *fbd);
void (*pcs_disable)(struct fbnic_dev *fbd);
bool (*pcs_get_link)(struct fbnic_dev *fbd);
int (*pcs_get_link_event)(struct fbnic_dev *fbd);
void (*link_down)(struct fbnic_dev *fbd);
void (*link_up)(struct fbnic_dev *fbd, bool tx_pause, bool rx_pause);
};
int fbnic_mac_init(struct fbnic_dev *fbd);
#endif /* _FBNIC_MAC_H_ */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#ifndef _FBNIC_NETDEV_H_
#define _FBNIC_NETDEV_H_
#include <linux/types.h>
#include <linux/phylink.h>
#include "fbnic_csr.h"
#include "fbnic_rpc.h"
#include "fbnic_txrx.h"
struct fbnic_net {
struct fbnic_ring *tx[FBNIC_MAX_TXQS];
struct fbnic_ring *rx[FBNIC_MAX_RXQS];
struct net_device *netdev;
struct fbnic_dev *fbd;
u32 txq_size;
u32 hpq_size;
u32 ppq_size;
u32 rcq_size;
u16 num_napi;
struct phylink *phylink;
struct phylink_config phylink_config;
struct phylink_pcs phylink_pcs;
/* TBD: Remove these when phylink supports FEC and lane config */
u8 fec;
u8 link_mode;
u16 num_tx_queues;
u16 num_rx_queues;
u8 indir_tbl[FBNIC_RPC_RSS_TBL_COUNT][FBNIC_RPC_RSS_TBL_SIZE];
u32 rss_key[FBNIC_RPC_RSS_KEY_DWORD_LEN];
u32 rss_flow_hash[FBNIC_NUM_HASH_OPT];
u64 link_down_events;
struct list_head napis;
};
int __fbnic_open(struct fbnic_net *fbn);
void fbnic_up(struct fbnic_net *fbn);
void fbnic_down(struct fbnic_net *fbn);
struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd);
void fbnic_netdev_free(struct fbnic_dev *fbd);
int fbnic_netdev_register(struct net_device *netdev);
void fbnic_netdev_unregister(struct net_device *netdev);
void fbnic_reset_queues(struct fbnic_net *fbn,
unsigned int tx, unsigned int rx);
void __fbnic_set_rx_mode(struct net_device *netdev);
void fbnic_clear_rx_mode(struct net_device *netdev);
int fbnic_phylink_init(struct net_device *netdev);
#endif /* _FBNIC_NETDEV_H_ */
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#include <linux/phy.h>
#include <linux/phylink.h>
#include "fbnic.h"
#include "fbnic_mac.h"
#include "fbnic_netdev.h"
static struct fbnic_net *
fbnic_pcs_to_net(struct phylink_pcs *pcs)
{
return container_of(pcs, struct fbnic_net, phylink_pcs);
}
static void
fbnic_phylink_pcs_get_state(struct phylink_pcs *pcs,
struct phylink_link_state *state)
{
struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
struct fbnic_dev *fbd = fbn->fbd;
/* For now we use hard-coded defaults and FW config to determine
* the current values. In future patches we will add support for
* reconfiguring these values and changing link settings.
*/
switch (fbd->fw_cap.link_speed) {
case FBNIC_FW_LINK_SPEED_25R1:
state->speed = SPEED_25000;
break;
case FBNIC_FW_LINK_SPEED_50R2:
state->speed = SPEED_50000;
break;
case FBNIC_FW_LINK_SPEED_100R2:
state->speed = SPEED_100000;
break;
default:
state->speed = SPEED_UNKNOWN;
break;
}
state->duplex = DUPLEX_FULL;
state->link = fbd->mac->pcs_get_link(fbd);
}
static int
fbnic_phylink_pcs_enable(struct phylink_pcs *pcs)
{
struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
struct fbnic_dev *fbd = fbn->fbd;
return fbd->mac->pcs_enable(fbd);
}
static void
fbnic_phylink_pcs_disable(struct phylink_pcs *pcs)
{
struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
struct fbnic_dev *fbd = fbn->fbd;
return fbd->mac->pcs_disable(fbd);
}
static int
fbnic_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface,
const unsigned long *advertising,
bool permit_pause_to_mac)
{
return 0;
}
static const struct phylink_pcs_ops fbnic_phylink_pcs_ops = {
.pcs_config = fbnic_phylink_pcs_config,
.pcs_enable = fbnic_phylink_pcs_enable,
.pcs_disable = fbnic_phylink_pcs_disable,
.pcs_get_state = fbnic_phylink_pcs_get_state,
};
static struct phylink_pcs *
fbnic_phylink_mac_select_pcs(struct phylink_config *config,
phy_interface_t interface)
{
struct net_device *netdev = to_net_dev(config->dev);
struct fbnic_net *fbn = netdev_priv(netdev);
return &fbn->phylink_pcs;
}
static void
fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
}
static void
fbnic_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
{
struct net_device *netdev = to_net_dev(config->dev);
struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_dev *fbd = fbn->fbd;
fbd->mac->link_down(fbd);
fbn->link_down_events++;
}
static void
fbnic_phylink_mac_link_up(struct phylink_config *config,
struct phy_device *phy, unsigned int mode,
phy_interface_t interface, int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct net_device *netdev = to_net_dev(config->dev);
struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_dev *fbd = fbn->fbd;
fbd->mac->link_up(fbd, tx_pause, rx_pause);
}
static const struct phylink_mac_ops fbnic_phylink_mac_ops = {
.mac_select_pcs = fbnic_phylink_mac_select_pcs,
.mac_config = fbnic_phylink_mac_config,
.mac_link_down = fbnic_phylink_mac_link_down,
.mac_link_up = fbnic_phylink_mac_link_up,
};
int fbnic_phylink_init(struct net_device *netdev)
{
struct fbnic_net *fbn = netdev_priv(netdev);
struct phylink *phylink;
fbn->phylink_pcs.neg_mode = true;
fbn->phylink_pcs.ops = &fbnic_phylink_pcs_ops;
fbn->phylink_config.dev = &netdev->dev;
fbn->phylink_config.type = PHYLINK_NETDEV;
fbn->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
MAC_10000FD | MAC_25000FD |
MAC_40000FD | MAC_50000FD |
MAC_100000FD;
fbn->phylink_config.default_an_inband = true;
__set_bit(PHY_INTERFACE_MODE_XGMII,
fbn->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_XLGMII,
fbn->phylink_config.supported_interfaces);
phylink = phylink_create(&fbn->phylink_config, NULL,
PHY_INTERFACE_MODE_XLGMII,
&fbnic_phylink_mac_ops);
if (IS_ERR(phylink))
return PTR_ERR(phylink);
fbn->phylink = phylink;
return 0;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -2601,6 +2601,8 @@
#define PCI_VENDOR_ID_HYGON 0x1d94
#define PCI_VENDOR_ID_META 0x1d9b
#define PCI_VENDOR_ID_FUNGIBLE 0x1dad
#define PCI_VENDOR_ID_HXT 0x1dbf
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment