diff options
Diffstat (limited to 'drivers/net/ethernet/meta')
18 files changed, 500 insertions, 145 deletions
diff --git a/drivers/net/ethernet/meta/Kconfig b/drivers/net/ethernet/meta/Kconfig index 3ba527514f1e..ca5c7ac2a5bc 100644 --- a/drivers/net/ethernet/meta/Kconfig +++ b/drivers/net/ethernet/meta/Kconfig @@ -19,13 +19,14 @@ if NET_VENDOR_META config FBNIC tristate "Meta Platforms Host Network Interface" - depends on X86_64 || COMPILE_TEST + depends on 64BIT || COMPILE_TEST depends on !S390 depends on MAX_SKB_FRAGS < 22 depends on PCI_MSI depends on PTP_1588_CLOCK_OPTIONAL select NET_DEVLINK select PAGE_POOL + select PCS_XPCS select PHYLINK select PLDMFW help diff --git a/drivers/net/ethernet/meta/fbnic/Makefile b/drivers/net/ethernet/meta/fbnic/Makefile index 15e8ff649615..72c41af65364 100644 --- a/drivers/net/ethernet/meta/fbnic/Makefile +++ b/drivers/net/ethernet/meta/fbnic/Makefile @@ -21,6 +21,7 @@ fbnic-y := fbnic_csr.o \ fbnic_pci.o \ fbnic_phylink.o \ fbnic_rpc.o \ + fbnic_mdio.o \ fbnic_time.o \ fbnic_tlv.o \ fbnic_txrx.o \ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic.h b/drivers/net/ethernet/meta/fbnic/fbnic.h index b03e5a3d5144..779a083b9215 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic.h @@ -34,7 +34,7 @@ struct fbnic_dev { u32 __iomem *uc_addr4; const struct fbnic_mac *mac; unsigned int fw_msix_vector; - unsigned int pcs_msix_vector; + unsigned int mac_msix_vector; unsigned short num_irqs; struct { @@ -83,6 +83,10 @@ struct fbnic_dev { /* Last @time_high refresh time in jiffies (to catch stalls) */ unsigned long last_read; + /* PMD specific data */ + unsigned long end_of_pmd_training; + u8 pmd_state; + /* Local copy of hardware statistics */ struct fbnic_hw_stats hw_stats; @@ -91,6 +95,9 @@ struct fbnic_dev { u64 prev_firmware_time; struct fbnic_fw_log fw_log; + + /* MDIO bus for PHYs */ + struct mii_bus *mdio_bus; }; /* Reserve entry 0 in the MSI-X "others" array until we have filled all @@ -175,8 +182,8 @@ void fbnic_fw_free_mbx(struct fbnic_dev *fbd); void fbnic_hwmon_register(struct fbnic_dev *fbd); void fbnic_hwmon_unregister(struct fbnic_dev *fbd); -int fbnic_pcs_request_irq(struct fbnic_dev *fbd); -void fbnic_pcs_free_irq(struct fbnic_dev *fbd); +int fbnic_mac_request_irq(struct fbnic_dev *fbd); +void fbnic_mac_free_irq(struct fbnic_dev *fbd); void fbnic_napi_name_irqs(struct fbnic_dev *fbd); int fbnic_napi_request_irq(struct fbnic_dev *fbd, @@ -200,6 +207,8 @@ void fbnic_dbg_exit(void); void fbnic_rpc_reset_valid_entries(struct fbnic_dev *fbd); +int fbnic_mdiobus_create(struct fbnic_dev *fbd); + void fbnic_csr_get_regs(struct fbnic_dev *fbd, u32 *data, u32 *regs_version); int fbnic_csr_regs_len(struct fbnic_dev *fbd); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h index d3a7ad921f18..422265dc7abd 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h @@ -787,6 +787,8 @@ enum { /* MAC PCS registers */ #define FBNIC_CSR_START_PCS 0x10000 /* CSR section delimiter */ +#define FBNIC_PCS_PAGE(n) (0x10000 + 0x400 * (n)) /* 0x40000 + 1024*n */ +#define FBNIC_PCS(reg, n) ((reg) + FBNIC_PCS_PAGE(n)) #define FBNIC_CSR_END_PCS 0x10668 /* CSR section delimiter */ #define FBNIC_CSR_START_RSFEC 0x10800 /* CSR section delimiter */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c index 95fac020eb93..693ebdf38705 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c @@ -1863,6 +1863,14 @@ fbnic_get_rmon_stats(struct net_device *netdev, *ranges = fbnic_rmon_ranges; } +static void fbnic_get_link_ext_stats(struct net_device *netdev, + struct ethtool_link_ext_stats *stats) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + + stats->link_down_events = fbn->link_down_events; +} + static const struct ethtool_ops fbnic_ethtool_ops = { .cap_link_lanes_supported = true, .supported_coalesce_params = ETHTOOL_COALESCE_USECS | @@ -1874,6 +1882,7 @@ static const struct ethtool_ops fbnic_ethtool_ops = { .get_regs_len = fbnic_get_regs_len, .get_regs = fbnic_get_regs, .get_link = ethtool_op_get_link, + .get_link_ext_stats = fbnic_get_link_ext_stats, .get_coalesce = fbnic_get_coalesce, .set_coalesce = fbnic_set_coalesce, .get_ringparam = fbnic_get_ringparam, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c index fcd9912e7ad3..d8d9b6cfde82 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c @@ -878,11 +878,11 @@ msg_err: * @fbd: FBNIC device structure * @cmpl_data: Completion struct to store coredump * @offset: Offset into coredump requested - * @length: Length of section of cordeump to fetch + * @length: Length of section of coredump to fetch * * Return: zero on success, negative errno on failure * - * Asks the firmware to provide a section of the cordeump back in a message. + * Asks the firmware to provide a section of the coredump back in a message. * The response will have an offset and size matching the values provided. */ int fbnic_fw_xmit_coredump_read_msg(struct fbnic_dev *fbd, @@ -1868,7 +1868,7 @@ int fbnic_fw_xmit_rpc_macda_sync(struct fbnic_dev *fbd) if (err) goto free_message; - /* Send message of to FW notifying it of current RPC config */ + /* Send message off to FW notifying it of current RPC config */ err = fbnic_mbx_map_tlv_msg(fbd, msg); if (err) goto free_message; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_irq.c b/drivers/net/ethernet/meta/fbnic/fbnic_irq.c index 1c88a2bf3a7a..02e8b0b257fe 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_irq.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_irq.c @@ -118,12 +118,12 @@ void fbnic_fw_free_mbx(struct fbnic_dev *fbd) fbd->fw_msix_vector = 0; } -static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data) +static irqreturn_t fbnic_mac_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) { + if (fbd->mac->get_link_event(fbd) == FBNIC_LINK_EVENT_NONE) { fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_PCS_MSIX_ENTRY); return IRQ_HANDLED; @@ -131,26 +131,28 @@ static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data) fbn = netdev_priv(fbd->netdev); - phylink_pcs_change(&fbn->phylink_pcs, false); + /* Record link down events */ + if (!fbd->mac->get_link(fbd, fbn->aui, fbn->fec)) + phylink_pcs_change(fbn->pcs, false); return IRQ_HANDLED; } /** - * fbnic_pcs_request_irq - Configure the PCS to enable it to advertise link + * fbnic_mac_request_irq - 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 + * This function provides basic bringup for the MAC/PHY 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_request_irq(struct fbnic_dev *fbd) +int fbnic_mac_request_irq(struct fbnic_dev *fbd) { struct pci_dev *pdev = to_pci_dev(fbd->dev); int vector, err; - WARN_ON(fbd->pcs_msix_vector); + WARN_ON(fbd->mac_msix_vector); vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY); if (vector < 0) @@ -159,7 +161,7 @@ int fbnic_pcs_request_irq(struct fbnic_dev *fbd) /* Request the IRQ for PCS link vector. * Map PCS cause to it, and unmask it */ - err = request_irq(vector, &fbnic_pcs_msix_intr, 0, + err = request_irq(vector, &fbnic_mac_msix_intr, 0, fbd->netdev->name, fbd); if (err) return err; @@ -168,22 +170,22 @@ int fbnic_pcs_request_irq(struct fbnic_dev *fbd) fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX), FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE); - fbd->pcs_msix_vector = vector; + fbd->mac_msix_vector = vector; return 0; } /** - * fbnic_pcs_free_irq - Teardown the PCS IRQ to prepare for stopping + * fbnic_mac_free_irq - Teardown the MAC IRQ to prepare for stopping * @fbd: Pointer to device that is stopping * - * This function undoes the work done in fbnic_pcs_request_irq and prepares + * This function undoes the work done in fbnic_mac_request_irq and prepares * the device to no longer receive traffic on the host interface. **/ -void fbnic_pcs_free_irq(struct fbnic_dev *fbd) +void fbnic_mac_free_irq(struct fbnic_dev *fbd) { /* Vector has already been freed */ - if (!fbd->pcs_msix_vector) + if (!fbd->mac_msix_vector) return; /* Disable interrupt */ @@ -192,14 +194,14 @@ void fbnic_pcs_free_irq(struct fbnic_dev *fbd) fbnic_wrfl(fbd); /* Synchronize IRQ to prevent race that would unmask vector */ - synchronize_irq(fbd->pcs_msix_vector); + synchronize_irq(fbd->mac_msix_vector); /* Mask the vector */ fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY); /* Free the vector */ - free_irq(fbd->pcs_msix_vector, fbd); - fbd->pcs_msix_vector = 0; + free_irq(fbd->mac_msix_vector, fbd); + fbd->mac_msix_vector = 0; } void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mac.c b/drivers/net/ethernet/meta/fbnic/fbnic_mac.c index 2a84bd1d7e26..fc7abea4ef5b 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_mac.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mac.c @@ -434,14 +434,14 @@ static void fbnic_mac_tx_pause_config(struct fbnic_dev *fbd, bool tx_pause) wr32(fbd, FBNIC_RXB_PAUSE_DROP_CTRL, rxb_pause_ctrl); } -static int fbnic_pcs_get_link_event_asic(struct fbnic_dev *fbd) +static int fbnic_mac_get_link_event(struct fbnic_dev *fbd) { - u32 pcs_intr_mask = rd32(fbd, FBNIC_SIG_PCS_INTR_STS); + u32 intr_mask = rd32(fbd, FBNIC_SIG_PCS_INTR_STS); - if (pcs_intr_mask & FBNIC_SIG_PCS_INTR_LINK_DOWN) + if (intr_mask & FBNIC_SIG_PCS_INTR_LINK_DOWN) return FBNIC_LINK_EVENT_DOWN; - return (pcs_intr_mask & FBNIC_SIG_PCS_INTR_LINK_UP) ? + return (intr_mask & FBNIC_SIG_PCS_INTR_LINK_UP) ? FBNIC_LINK_EVENT_UP : FBNIC_LINK_EVENT_NONE; } @@ -466,9 +466,8 @@ static u32 __fbnic_mac_cmd_config_asic(struct fbnic_dev *fbd, return command_config; } -static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd) +static bool fbnic_mac_get_link_status(struct fbnic_dev *fbd, u8 aui, u8 fec) { - struct fbnic_net *fbn = netdev_priv(fbd->netdev); u32 pcs_status, lane_mask = ~0; pcs_status = rd32(fbd, FBNIC_SIG_PCS_OUT0); @@ -476,7 +475,7 @@ static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd) return false; /* Define the expected lane mask for the status bits we need to check */ - switch (fbn->aui) { + switch (aui) { case FBNIC_AUI_100GAUI2: lane_mask = 0xf; break; @@ -484,7 +483,7 @@ static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd) lane_mask = 3; break; case FBNIC_AUI_LAUI2: - switch (fbn->fec) { + switch (fec) { case FBNIC_FEC_OFF: lane_mask = 0x63; break; @@ -502,7 +501,7 @@ static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd) } /* Use an XOR to remove the bits we expect to see set */ - switch (fbn->fec) { + switch (fec) { case FBNIC_FEC_OFF: lane_mask ^= FIELD_GET(FBNIC_SIG_PCS_OUT0_BLOCK_LOCK, pcs_status); @@ -521,7 +520,46 @@ static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd) return !lane_mask; } -static bool fbnic_pcs_get_link_asic(struct fbnic_dev *fbd) +static bool fbnic_pmd_update_state(struct fbnic_dev *fbd, bool signal_detect) +{ + /* Delay link up for 4 seconds to allow for link training. + * The state transitions for this are as follows: + * + * All states have the following two transitions in common: + * Loss of signal -> FBNIC_PMD_INITIALIZE + * The condition handled below (!signal) + * Reconfiguration -> FBNIC_PMD_INITIALIZE + * Occurs when mac_prepare starts a PHY reconfig + * FBNIC_PMD_TRAINING: + * signal still detected && 4s have passed -> Report link up + * When link is brought up in link_up -> FBNIC_PMD_SEND_DATA + * FBNIC_PMD_INITIALIZE: + * signal detected -> FBNIC_PMD_TRAINING + */ + if (!signal_detect) { + fbd->pmd_state = FBNIC_PMD_INITIALIZE; + return false; + } + + switch (fbd->pmd_state) { + case FBNIC_PMD_TRAINING: + return time_before(fbd->end_of_pmd_training, jiffies); + case FBNIC_PMD_LINK_READY: + case FBNIC_PMD_SEND_DATA: + return true; + } + + fbd->end_of_pmd_training = jiffies + 4 * HZ; + + /* Ensure end_of_training is visible before the state change */ + smp_wmb(); + + fbd->pmd_state = FBNIC_PMD_TRAINING; + + return false; +} + +static bool fbnic_mac_get_link(struct fbnic_dev *fbd, u8 aui, u8 fec) { bool link; @@ -538,7 +576,8 @@ static bool fbnic_pcs_get_link_asic(struct fbnic_dev *fbd) wr32(fbd, FBNIC_SIG_PCS_INTR_STS, FBNIC_SIG_PCS_INTR_LINK_DOWN | FBNIC_SIG_PCS_INTR_LINK_UP); - link = fbnic_mac_get_pcs_link_status(fbd); + link = fbnic_mac_get_link_status(fbd, aui, fec); + link = fbnic_pmd_update_state(fbd, link); /* Enable interrupt to only capture changes in link state */ wr32(fbd, FBNIC_SIG_PCS_INTR_MASK, @@ -586,20 +625,15 @@ void fbnic_mac_get_fw_settings(struct fbnic_dev *fbd, u8 *aui, u8 *fec) } } -static int fbnic_pcs_enable_asic(struct fbnic_dev *fbd) +static void fbnic_mac_prepare(struct fbnic_dev *fbd, u8 aui, u8 fec) { /* Mask and clear the PCS interrupt, will be enabled by link handler */ wr32(fbd, FBNIC_SIG_PCS_INTR_MASK, ~0); wr32(fbd, FBNIC_SIG_PCS_INTR_STS, ~0); - return 0; -} - -static void fbnic_pcs_disable_asic(struct fbnic_dev *fbd) -{ - /* Mask and clear the PCS interrupt */ - wr32(fbd, FBNIC_SIG_PCS_INTR_MASK, ~0); - wr32(fbd, FBNIC_SIG_PCS_INTR_STS, ~0); + /* If we don't have link tear it all down and start over */ + if (!fbnic_mac_get_link_status(fbd, aui, fec)) + fbd->pmd_state = FBNIC_PMD_INITIALIZE; } static void fbnic_mac_link_down_asic(struct fbnic_dev *fbd) @@ -867,10 +901,9 @@ exit_free: static const struct fbnic_mac fbnic_mac_asic = { .init_regs = fbnic_mac_init_regs, - .pcs_enable = fbnic_pcs_enable_asic, - .pcs_disable = fbnic_pcs_disable_asic, - .pcs_get_link = fbnic_pcs_get_link_asic, - .pcs_get_link_event = fbnic_pcs_get_link_event_asic, + .get_link = fbnic_mac_get_link, + .get_link_event = fbnic_mac_get_link_event, + .prepare = fbnic_mac_prepare, .get_fec_stats = fbnic_mac_get_fec_stats, .get_pcs_stats = fbnic_mac_get_pcs_stats, .get_eth_mac_stats = fbnic_mac_get_eth_mac_stats, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mac.h b/drivers/net/ethernet/meta/fbnic/fbnic_mac.h index ede5ff0dae22..f08fe8b7c497 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_mac.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mac.h @@ -10,6 +10,24 @@ struct fbnic_dev; #define FBNIC_MAX_JUMBO_FRAME_SIZE 9742 +/* States loosely based on section 136.8.11.7.5 of IEEE 802.3-2022 Ethernet + * Standard. These are needed to track the state of the PHY as it has a delay + * of several seconds from the time link comes up until it has completed + * training that we need to wait to report the link. + * + * Currently we treat training as a single block as this is managed by the + * firmware. + * + * We have FBNIC_PMD_SEND_DATA set to 0 as the expected default at driver load + * and we initialize the structure containing it to zero at allocation. + */ +enum { + FBNIC_PMD_SEND_DATA = 0x0, + FBNIC_PMD_INITIALIZE = 0x1, + FBNIC_PMD_TRAINING = 0x2, + FBNIC_PMD_LINK_READY = 0x3, +}; + enum { FBNIC_LINK_EVENT_NONE = 0, FBNIC_LINK_EVENT_UP = 1, @@ -38,6 +56,7 @@ enum { FBNIC_AUI_50GAUI1 = 2, /* 53.125GBd 53.125 * 1 */ FBNIC_AUI_100GAUI2 = 3, /* 106.25GBd 53.125 * 2 */ FBNIC_AUI_UNKNOWN = 4, + __FBNIC_AUI_MAX__ }; #define FBNIC_AUI_MODE_R2 (FBNIC_AUI_LAUI2) @@ -55,15 +74,15 @@ enum fbnic_sensor_id { * 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) + * int (*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 + * bool (*get_link)(struct fbnic_dev *fbd, u8 aui, u8 fec); + * Check link status + * + * void (*prepare)(struct fbnic_dev *fbd, u8 aui, u8 fec); + * Prepare PHY for init by fetching settings, disabling interrupts, + * and sending an updated PHY config to FW if needed. * * void (*link_down)(struct fbnic_dev *fbd); * Configure MAC for link down event @@ -74,10 +93,10 @@ enum fbnic_sensor_id { 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); + int (*get_link_event)(struct fbnic_dev *fbd); + bool (*get_link)(struct fbnic_dev *fbd, u8 aui, u8 fec); + + void (*prepare)(struct fbnic_dev *fbd, u8 aui, u8 fec); void (*get_fec_stats)(struct fbnic_dev *fbd, bool reset, struct fbnic_fec_stats *fec_stats); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c new file mode 100644 index 000000000000..709041f7fc43 --- /dev/null +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) Meta Platforms, Inc. and affiliates. */ + +#include <linux/mdio.h> +#include <linux/pcs/pcs-xpcs.h> + +#include "fbnic.h" +#include "fbnic_netdev.h" + +#define DW_VENDOR BIT(15) +#define FBNIC_PCS_VENDOR BIT(9) +#define FBNIC_PCS_ZERO_MASK (DW_VENDOR - FBNIC_PCS_VENDOR) + +static int +fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) +{ + u8 aui = FBNIC_AUI_UNKNOWN; + struct fbnic_net *fbn; + int ret = 0; + + /* We don't need a second PMD, just one can handle both lanes */ + if (addr) + return 0; + + if (fbd->netdev) { + fbn = netdev_priv(fbd->netdev); + if (fbn->aui < FBNIC_AUI_UNKNOWN) + aui = fbn->aui; + } + + switch (regnum) { + case MDIO_DEVID1: + ret = MP_FBNIC_XPCS_PMA_100G_ID >> 16; + break; + case MDIO_DEVID2: + ret = MP_FBNIC_XPCS_PMA_100G_ID & 0xffff; + break; + case MDIO_DEVS1: + ret = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; + break; + case MDIO_STAT2: + ret = MDIO_STAT2_DEVPRST_VAL; + break; + case MDIO_PMA_RXDET: + /* If training isn't complete default to 0 */ + if (fbd->pmd_state != FBNIC_PMD_SEND_DATA) + break; + /* Report either 1 or 2 lanes detected depending on config */ + ret = (MDIO_PMD_RXDET_GLOBAL | MDIO_PMD_RXDET_0) | + ((aui & FBNIC_AUI_MODE_R2) * + (MDIO_PMD_RXDET_1 / FBNIC_AUI_MODE_R2)); + break; + default: + break; + } + + dev_dbg(fbd->dev, + "SWMII PMD Rd: Addr: %d RegNum: %d Value: 0x%04x\n", + addr, regnum, ret); + + return ret; +} + +static int +fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum) +{ + int ret, offset = 0; + + /* We will need access to both PCS instances to get config info */ + if (addr >= 2) + return 0; + + /* Report 0 for reserved registers */ + if (regnum & FBNIC_PCS_ZERO_MASK) + return 0; + + /* Intercept and return correct ID for PCS */ + if (regnum == MDIO_DEVID1) + return DW_XPCS_ID >> 16; + if (regnum == MDIO_DEVID2) + return DW_XPCS_ID & 0xffff; + if (regnum == MDIO_DEVS1) + return MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; + + /* Swap vendor page bit for FBNIC PCS vendor page bit */ + if (regnum & DW_VENDOR) + offset ^= DW_VENDOR | FBNIC_PCS_VENDOR; + + ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset)); + + dev_dbg(fbd->dev, + "SWMII PCS Rd: Addr: %d RegNum: %d Value: 0x%04x\n", + addr, regnum, ret); + + return ret; +} + +static int +fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) +{ + struct fbnic_dev *fbd = bus->priv; + + if (devnum == MDIO_MMD_PMAPMD) + return fbnic_mdio_read_pmd(fbd, addr, regnum); + + if (devnum == MDIO_MMD_PCS) + return fbnic_mdio_read_pcs(fbd, addr, regnum); + + return 0; +} + +static void +fbnic_mdio_write_pmd(struct fbnic_dev *fbd, int addr, int regnum, u16 val) +{ + dev_dbg(fbd->dev, + "SWMII PMD Wr: Addr: %d RegNum: %d Value: 0x%04x\n", + addr, regnum, val); +} + +static void +fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val) +{ + dev_dbg(fbd->dev, + "SWMII PCS Wr: Addr: %d RegNum: %d Value: 0x%04x\n", + addr, regnum, val); + + /* Allow access to both halves of PCS for 50R2 config */ + if (addr > 2) + return; + + /* Skip write for reserved registers */ + if (regnum & FBNIC_PCS_ZERO_MASK) + return; + + /* Swap vendor page bit for FBNIC PCS vendor page bit */ + if (regnum & DW_VENDOR) + regnum ^= DW_VENDOR | FBNIC_PCS_VENDOR; + + fbnic_wr32(fbd, FBNIC_PCS_PAGE(addr) + regnum, val); +} + +static int +fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum, + int regnum, u16 val) +{ + struct fbnic_dev *fbd = bus->priv; + + if (devnum == MDIO_MMD_PMAPMD) + fbnic_mdio_write_pmd(fbd, addr, regnum, val); + + if (devnum == MDIO_MMD_PCS) + fbnic_mdio_write_pcs(fbd, addr, regnum, val); + + return 0; +} + +/** + * fbnic_mdiobus_create - Create an MDIO bus to allow interfacing w/ PHYs + * @fbd: Pointer to FBNIC device structure to populate bus on + * + * Initialize an MDIO bus and place a pointer to it on the fbd struct. This bus + * will be used to interface with the PMA/PMD and PCS. + * + * Return: 0 on success, negative on failure + **/ +int fbnic_mdiobus_create(struct fbnic_dev *fbd) +{ + struct mii_bus *bus; + int err; + + bus = devm_mdiobus_alloc(fbd->dev); + if (!bus) + return -ENOMEM; + + bus->name = "fbnic_mii_bus"; + bus->read_c45 = &fbnic_mdio_read_c45; + bus->write_c45 = &fbnic_mdio_write_c45; + + /* Disable PHY auto probing. We will add PCS manually */ + bus->phy_mask = ~0; + + bus->parent = fbd->dev; + bus->priv = fbd; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(fbd->dev)); + + err = devm_mdiobus_register(fbd->dev, bus); + if (err) { + dev_err(fbd->dev, "Failed to create MDIO bus: %d\n", err); + return err; + } + + fbd->mdio_bus = bus; + + return 0; +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c index e95be0e7bd9e..81c9d5c9a4b2 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c @@ -44,7 +44,7 @@ int __fbnic_open(struct fbnic_net *fbn) if (err) goto time_stop; - err = fbnic_pcs_request_irq(fbd); + err = fbnic_mac_request_irq(fbd); if (err) goto time_stop; @@ -86,10 +86,10 @@ static int fbnic_stop(struct net_device *netdev) { struct fbnic_net *fbn = netdev_priv(netdev); + fbnic_mac_free_irq(fbn->fbd); phylink_suspend(fbn->phylink, fbnic_bmc_present(fbn->fbd)); fbnic_down(fbn); - fbnic_pcs_free_irq(fbn->fbd); fbnic_time_stop(fbn); fbnic_fw_xmit_ownership_msg(fbn->fbd, false); @@ -697,10 +697,7 @@ void fbnic_reset_queues(struct fbnic_net *fbn, **/ void fbnic_netdev_free(struct fbnic_dev *fbd) { - struct fbnic_net *fbn = netdev_priv(fbd->netdev); - - if (fbn->phylink) - phylink_destroy(fbn->phylink); + fbnic_phylink_destroy(fbd->netdev); free_netdev(fbd->netdev); fbd->netdev = NULL; @@ -802,7 +799,7 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd) netif_tx_stop_all_queues(netdev); - if (fbnic_phylink_init(netdev)) { + if (fbnic_phylink_create(netdev)) { fbnic_netdev_free(fbd); return NULL; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h index b0a87c57910f..9129a658f8fa 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h @@ -44,7 +44,7 @@ struct fbnic_net { struct phylink *phylink; struct phylink_config phylink_config; - struct phylink_pcs phylink_pcs; + struct phylink_pcs *pcs; u8 aui; u8 fec; @@ -73,6 +73,8 @@ struct fbnic_net { /* Time stamping filter config */ struct kernel_hwtstamp_config hwtstamp_config; + + bool tx_pause; }; int __fbnic_open(struct fbnic_net *fbn); @@ -106,8 +108,10 @@ int fbnic_phylink_ethtool_ksettings_get(struct net_device *netdev, struct ethtool_link_ksettings *cmd); int fbnic_phylink_get_fecparam(struct net_device *netdev, struct ethtool_fecparam *fecparam); +int fbnic_phylink_create(struct net_device *netdev); +void fbnic_phylink_destroy(struct net_device *netdev); int fbnic_phylink_init(struct net_device *netdev); - +void fbnic_phylink_pmd_training_complete_notify(struct net_device *netdev); bool fbnic_check_split_frames(struct bpf_prog *prog, unsigned int mtu, u32 hds_threshold); #endif /* _FBNIC_NETDEV_H_ */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c index a7a6b4db8016..861d98099c44 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c @@ -185,7 +185,7 @@ static void fbnic_health_check(struct fbnic_dev *fbd) { struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX]; - /* As long as the heart is beating the FW is healty */ + /* As long as the heart is beating the FW is healthy */ if (fbd->fw_heartbeat_enabled) return; @@ -196,7 +196,7 @@ static void fbnic_health_check(struct fbnic_dev *fbd) if (tx_mbx->head != tx_mbx->tail) return; - fbnic_devlink_fw_report(fbd, "Firmware crashed detected!"); + fbnic_devlink_fw_report(fbd, "Firmware crash detected!"); fbnic_devlink_otp_check(fbd, "error detected after firmware recovery"); if (fbnic_fw_config_after_crash(fbd)) @@ -207,6 +207,10 @@ static void fbnic_service_task(struct work_struct *work) { struct fbnic_dev *fbd = container_of(to_delayed_work(work), struct fbnic_dev, service_task); + struct net_device *netdev = fbd->netdev; + + if (netif_running(netdev)) + fbnic_phylink_pmd_training_complete_notify(netdev); rtnl_lock(); @@ -224,7 +228,7 @@ static void fbnic_service_task(struct work_struct *work) netdev_unlock(fbd->netdev); } - if (netif_running(fbd->netdev)) + if (netif_running(netdev)) schedule_delayed_work(&fbd->service_task, HZ); rtnl_unlock(); @@ -335,6 +339,9 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto init_failure_mode; } + if (fbnic_mdiobus_create(fbd)) + goto init_failure_mode; + netdev = fbnic_netdev_alloc(fbd); if (!netdev) { dev_err(&pdev->dev, "Netdev allocation failed\n"); @@ -378,7 +385,7 @@ free_fbd: * @pdev: PCI device information struct * * Called by the PCI subsystem to alert the driver that it should release - * a PCI device. The could be caused by a Hot-Plug event, or because the + * a PCI device. This could be caused by a Hot-Plug event, or because the * driver is going to be removed from memory. **/ static void fbnic_remove(struct pci_dev *pdev) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c b/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c index 7ce3fdd25282..09c5225111be 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) Meta Platforms, Inc. and affiliates. */ +#include <linux/pcs/pcs-xpcs.h> #include <linux/phy.h> #include <linux/phylink.h> @@ -101,88 +102,47 @@ int fbnic_phylink_get_fecparam(struct net_device *netdev, return 0; } -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, unsigned int neg_mode, - struct phylink_link_state *state) +static struct phylink_pcs * +fbnic_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) { - struct fbnic_net *fbn = fbnic_pcs_to_net(pcs); - struct fbnic_dev *fbd = fbn->fbd; - - switch (fbn->aui) { - case FBNIC_AUI_25GAUI: - state->speed = SPEED_25000; - break; - case FBNIC_AUI_LAUI2: - case FBNIC_AUI_50GAUI1: - state->speed = SPEED_50000; - break; - case FBNIC_AUI_100GAUI2: - state->speed = SPEED_100000; - break; - default: - state->link = 0; - return; - } - - state->duplex = DUPLEX_FULL; + struct net_device *netdev = to_net_dev(config->dev); + struct fbnic_net *fbn = netdev_priv(netdev); - state->link = fbd->mac->pcs_get_link(fbd); + return fbn->pcs; } static int -fbnic_phylink_pcs_enable(struct phylink_pcs *pcs) +fbnic_phylink_mac_prepare(struct phylink_config *config, unsigned int mode, + phy_interface_t iface) { - struct fbnic_net *fbn = fbnic_pcs_to_net(pcs); + struct net_device *netdev = to_net_dev(config->dev); + struct fbnic_net *fbn = netdev_priv(netdev); struct fbnic_dev *fbd = fbn->fbd; - return fbd->mac->pcs_enable(fbd); + fbd->mac->prepare(fbd, fbn->aui, fbn->fec); + + return 0; } static void -fbnic_phylink_pcs_disable(struct phylink_pcs *pcs) +fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) { - 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) +fbnic_phylink_mac_finish(struct phylink_config *config, unsigned int mode, + phy_interface_t iface) { struct net_device *netdev = to_net_dev(config->dev); struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_dev *fbd = fbn->fbd; - return &fbn->phylink_pcs; -} + /* Retest the link state and restart interrupts */ + fbd->mac->get_link(fbd, fbn->aui, fbn->fec); -static void -fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode, - const struct phylink_link_state *state) -{ + return 0; } static void @@ -208,23 +168,48 @@ fbnic_phylink_mac_link_up(struct phylink_config *config, struct fbnic_net *fbn = netdev_priv(netdev); struct fbnic_dev *fbd = fbn->fbd; + fbn->tx_pause = tx_pause; + fbnic_config_drop_mode(fbn, tx_pause); + 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_prepare = fbnic_phylink_mac_prepare, .mac_config = fbnic_phylink_mac_config, + .mac_finish = fbnic_phylink_mac_finish, .mac_link_down = fbnic_phylink_mac_link_down, .mac_link_up = fbnic_phylink_mac_link_up, }; -int fbnic_phylink_init(struct net_device *netdev) +/** + * fbnic_phylink_create - Phylink device creation + * @netdev: Network Device struct to attach phylink device + * + * Initialize and attach a phylink instance to the device. The phylink + * device will make use of the netdev struct to track carrier and will + * eventually be used to expose the current state of the MAC and PCS + * setup. + * + * Return: 0 on success, negative on failure + **/ +int fbnic_phylink_create(struct net_device *netdev) { struct fbnic_net *fbn = netdev_priv(netdev); struct fbnic_dev *fbd = fbn->fbd; + struct phylink_pcs *pcs; struct phylink *phylink; + int err; + + pcs = xpcs_create_pcs_mdiodev(fbd->mdio_bus, 0); + if (IS_ERR(pcs)) { + err = PTR_ERR(pcs); + dev_err(fbd->dev, "Failed to create PCS device: %d\n", err); + return err; + } - fbn->phylink_pcs.ops = &fbnic_phylink_pcs_ops; + fbn->pcs = pcs; fbn->phylink_config.dev = &netdev->dev; fbn->phylink_config.type = PHYLINK_NETDEV; @@ -247,10 +232,80 @@ int fbnic_phylink_init(struct net_device *netdev) phylink = phylink_create(&fbn->phylink_config, NULL, fbnic_phylink_select_interface(fbn->aui), &fbnic_phylink_mac_ops); - if (IS_ERR(phylink)) - return PTR_ERR(phylink); + if (IS_ERR(phylink)) { + err = PTR_ERR(phylink); + dev_err(netdev->dev.parent, + "Failed to create Phylink interface, err: %d\n", err); + xpcs_destroy_pcs(pcs); + return err; + } fbn->phylink = phylink; return 0; } + +/** + * fbnic_phylink_destroy - Teardown phylink related interfaces + * @netdev: Network Device struct containing phylink device + * + * Detach and free resources related to phylink interface. + **/ +void fbnic_phylink_destroy(struct net_device *netdev) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + + if (fbn->phylink) + phylink_destroy(fbn->phylink); + if (fbn->pcs) + xpcs_destroy_pcs(fbn->pcs); +} + +/** + * fbnic_phylink_pmd_training_complete_notify - PMD training complete notifier + * @netdev: Netdev struct phylink device attached to + * + * When the link first comes up the PMD will have a period of 2 to 3 seconds + * where the link will flutter due to link training. To avoid spamming the + * kernel log with messages about this we add a delay of 4 seconds from the + * time of the last PCS report of link so that we can guarantee we are unlikely + * to see any further link loss events due to link training. + **/ +void fbnic_phylink_pmd_training_complete_notify(struct net_device *netdev) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_dev *fbd = fbn->fbd; + + if (fbd->pmd_state != FBNIC_PMD_TRAINING) + return; + + /* Prevent reading end_of_pmd_training until we verified state */ + smp_rmb(); + + if (!time_before(READ_ONCE(fbd->end_of_pmd_training), jiffies)) + return; + + /* At this point we have verified that the link has been up for + * the full training duration. As a first step we will try + * transitioning to link ready. + */ + if (cmpxchg(&fbd->pmd_state, FBNIC_PMD_TRAINING, + FBNIC_PMD_LINK_READY) != FBNIC_PMD_TRAINING) + return; + + /* Perform a follow-up check to verify that the link didn't flap + * just before our transition by rechecking the training timer. + */ + if (!time_before(READ_ONCE(fbd->end_of_pmd_training), jiffies)) + return; + + /* The training timeout has been completed. We are good to swap out + * link_ready for send_data assuming no other events have occurred + * that would have pulled us back into initialization or training. + */ + if (cmpxchg(&fbd->pmd_state, FBNIC_PMD_LINK_READY, + FBNIC_PMD_SEND_DATA) != FBNIC_PMD_LINK_READY) + return; + + phylink_pcs_change(fbn->pcs, false); +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_time.c b/drivers/net/ethernet/meta/fbnic/fbnic_time.c index 39d99677b71e..db7748189f45 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_time.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_time.c @@ -253,7 +253,7 @@ static void fbnic_ptp_reset(struct fbnic_dev *fbd) void fbnic_time_init(struct fbnic_net *fbn) { - /* This is not really a statistic, but the lockng primitive fits + /* This is not really a statistic, but the locking primitive fits * our usecase perfectly, we need an atomic 8 bytes READ_ONCE() / * WRITE_ONCE() behavior. */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h b/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h index c34bf87eeec9..3508b46ebdd0 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h @@ -80,7 +80,7 @@ struct fbnic_tlv_index { enum fbnic_tlv_type type; }; -#define TLV_MAX_DATA (PAGE_SIZE - 512) +#define TLV_MAX_DATA ((PAGE_SIZE - 512) & 0xFFFF) #define FBNIC_TLV_ATTR_ID_UNKNOWN USHRT_MAX #define FBNIC_TLV_ATTR_STRING(id, len) { id, len, FBNIC_TLV_STRING } #define FBNIC_TLV_ATTR_FLAG(id) { id, 0, FBNIC_TLV_FLAG } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index b1e8ce89870f..13d508ce637f 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -653,7 +653,8 @@ static void fbnic_clean_twq1(struct fbnic_napi_vector *nv, bool pp_allow_direct, FBNIC_TWD_TYPE_AL; total_bytes += FIELD_GET(FBNIC_TWD_LEN_MASK, twd); - page_pool_put_page(page->pp, page, -1, pp_allow_direct); + page_pool_put_page(pp_page_to_nmdesc(page)->pp, page, -1, + pp_allow_direct); next_desc: head++; head &= ring->size_mask; @@ -887,6 +888,7 @@ static void fbnic_bd_prep(struct fbnic_ring *bdq, u16 id, netmem_ref netmem) *bdq_desc = cpu_to_le64(bd); bd += FIELD_PREP(FBNIC_BD_DESC_ADDR_MASK, 1) | FIELD_PREP(FBNIC_BD_DESC_ID_MASK, 1); + bdq_desc++; } while (--i); } @@ -1806,7 +1808,7 @@ int fbnic_alloc_napi_vectors(struct fbnic_net *fbn) free_vectors: fbnic_free_napi_vectors(fbn); - return -ENOMEM; + return err; } static void fbnic_free_ring_resources(struct device *dev, @@ -2573,11 +2575,15 @@ write_ctl: } static void fbnic_config_drop_mode_rcq(struct fbnic_napi_vector *nv, - struct fbnic_ring *rcq) + struct fbnic_ring *rcq, bool tx_pause) { + struct fbnic_net *fbn = netdev_priv(nv->napi.dev); u32 drop_mode, rcq_ctl; - drop_mode = FBNIC_QUEUE_RDE_CTL0_DROP_IMMEDIATE; + if (!tx_pause && fbn->num_rx_queues > 1) + drop_mode = FBNIC_QUEUE_RDE_CTL0_DROP_IMMEDIATE; + else + drop_mode = FBNIC_QUEUE_RDE_CTL0_DROP_NEVER; /* Specify packet layout */ rcq_ctl = FIELD_PREP(FBNIC_QUEUE_RDE_CTL0_DROP_MODE_MASK, drop_mode) | @@ -2587,6 +2593,21 @@ static void fbnic_config_drop_mode_rcq(struct fbnic_napi_vector *nv, fbnic_ring_wr32(rcq, FBNIC_QUEUE_RDE_CTL0, rcq_ctl); } +void fbnic_config_drop_mode(struct fbnic_net *fbn, bool tx_pause) +{ + int i, t; + + for (i = 0; i < fbn->num_napi; i++) { + struct fbnic_napi_vector *nv = fbn->napi[i]; + + for (t = 0; t < nv->rxt_count; t++) { + struct fbnic_q_triad *qt = &nv->qt[nv->txt_count + t]; + + fbnic_config_drop_mode_rcq(nv, &qt->cmpl, tx_pause); + } + } +} + static void fbnic_config_rim_threshold(struct fbnic_ring *rcq, u16 nv_idx, u32 rx_desc) { u32 threshold; @@ -2636,7 +2657,7 @@ static void fbnic_enable_rcq(struct fbnic_napi_vector *nv, u32 hds_thresh = fbn->hds_thresh; u32 rcq_ctl = 0; - fbnic_config_drop_mode_rcq(nv, rcq); + fbnic_config_drop_mode_rcq(nv, rcq, fbn->tx_pause); /* Force lower bound on MAX_HEADER_BYTES. Below this, all frames should * be split at L4. It would also result in the frames being split at @@ -2699,7 +2720,6 @@ static void __fbnic_nv_enable(struct fbnic_napi_vector *nv) &nv->napi); fbnic_enable_bdq(&qt->sub0, &qt->sub1); - fbnic_config_drop_mode_rcq(nv, &qt->cmpl); fbnic_enable_rcq(nv, &qt->cmpl); } } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h index ca37da5a0b17..27776e844e29 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h @@ -184,6 +184,7 @@ void fbnic_reset_netif_queues(struct fbnic_net *fbn); irqreturn_t fbnic_msix_clean_rings(int irq, void *data); void fbnic_napi_enable(struct fbnic_net *fbn); void fbnic_napi_disable(struct fbnic_net *fbn); +void fbnic_config_drop_mode(struct fbnic_net *fbn, bool tx_pause); void fbnic_enable(struct fbnic_net *fbn); void fbnic_disable(struct fbnic_net *fbn); void fbnic_flush(struct fbnic_net *fbn); |
