@@ -20,6 +20,7 @@ struct fbnic_dev {
const struct fbnic_mac *mac;
struct msix_entry *msix_entries;
unsigned int fw_msix_vector;
+ unsigned int mac_msix_vector;
unsigned short num_irqs;
struct delayed_work service_task;
@@ -37,6 +38,13 @@ struct fbnic_dev {
u32 mps;
u32 readrq;
+ /* Tri-state value indicating state of link.
+ * 0 - Up
+ * 1 - Down
+ * 2 - Event - Requires checking as link state may have changed
+ */
+ s8 link_state;
+
/* Number of TCQs/RCQs available on hardware */
u16 max_num_queues;
};
@@ -48,6 +56,7 @@ struct fbnic_dev {
*/
enum {
FBNIC_FW_MSIX_ENTRY,
+ FBNIC_MAC_MSIX_ENTRY,
FBNIC_NON_NAPI_VECTORS
};
@@ -89,6 +98,11 @@ void fbnic_fw_wr32(struct fbnic_dev *fbd, u32 reg, u32 val);
#define fw_wr32(reg, val) fbnic_fw_wr32(fbd, reg, val)
#define fw_wrfl() fbnic_fw_rd32(fbd, 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;
@@ -104,6 +118,10 @@ 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_mac_get_link(struct fbnic_dev *fbd, bool *link);
+int fbnic_mac_enable(struct fbnic_dev *fbd);
+void fbnic_mac_disable(struct fbnic_dev *fbd);
+
void fbnic_free_irqs(struct fbnic_dev *fbd);
int fbnic_alloc_irqs(struct fbnic_dev *fbd);
@@ -58,6 +58,10 @@
#define FBNIC_INTR_MSIX_CTRL(n) (0x00040 + (n)) /* 0x00100 + 4*n */
#define FBNIC_INTR_MSIX_CTRL_VECTOR_MASK CSR_GENMASK(7, 0)
#define FBNIC_INTR_MSIX_CTRL_ENABLE CSR_BIT(31)
+enum {
+ FBNIC_INTR_MSIX_CTRL_MAC_IDX = 6,
+ FBNIC_INTR_MSIX_CTRL_PCS_IDX = 34,
+};
#define FBNIC_CSR_END_INTR 0x0005f /* CSR section delimiter */
@@ -392,6 +396,145 @@ enum {
#define FBNIC_MASTER_SPARE_0 0x0C41B /* 0x3106c */
#define FBNIC_CSR_END_MASTER 0x0C41E /* CSR section delimiter */
+/* MAC PCS registers */
+#define FBNIC_CSR_START_PCS 0x10000 /* CSR section delimiter */
+#define FBNIC_PCS_CONTROL1_0 0x10000 /* 0x40000 */
+#define FBNIC_PCS_CONTROL1_RESET CSR_BIT(15)
+#define FBNIC_PCS_CONTROL1_LOOPBACK CSR_BIT(14)
+#define FBNIC_PCS_CONTROL1_SPEED_SELECT_ALWAYS CSR_BIT(13)
+#define FBNIC_PCS_CONTROL1_SPEED_ALWAYS CSR_BIT(6)
+#define FBNIC_PCS_VENDOR_VL_INTVL_0 0x10202 /* 0x40808 */
+#define FBNIC_PCS_VL0_0_CHAN_0 0x10208 /* 0x40820 */
+#define FBNIC_PCS_VL0_1_CHAN_0 0x10209 /* 0x40824 */
+#define FBNIC_PCS_VL1_0_CHAN_0 0x1020a /* 0x40828 */
+#define FBNIC_PCS_VL1_1_CHAN_0 0x1020b /* 0x4082c */
+#define FBNIC_PCS_VL2_0_CHAN_0 0x1020c /* 0x40830 */
+#define FBNIC_PCS_VL2_1_CHAN_0 0x1020d /* 0x40834 */
+#define FBNIC_PCS_VL3_0_CHAN_0 0x1020e /* 0x40838 */
+#define FBNIC_PCS_VL3_1_CHAN_0 0x1020f /* 0x4083c */
+#define FBNIC_PCS_MODE_VL_CHAN_0 0x10210 /* 0x40840 */
+#define FBNIC_PCS_MODE_HI_BER25 CSR_BIT(2)
+#define FBNIC_PCS_MODE_DISABLE_MLD CSR_BIT(1)
+#define FBNIC_PCS_MODE_ENA_CLAUSE49 CSR_BIT(0)
+#define FBNIC_PCS_CONTROL1_1 0x10400 /* 0x41000 */
+#define FBNIC_PCS_VENDOR_VL_INTVL_1 0x10602 /* 0x41808 */
+#define FBNIC_PCS_VL0_0_CHAN_1 0x10608 /* 0x41820 */
+#define FBNIC_PCS_VL0_1_CHAN_1 0x10609 /* 0x41824 */
+#define FBNIC_PCS_VL1_0_CHAN_1 0x1060a /* 0x41828 */
+#define FBNIC_PCS_VL1_1_CHAN_1 0x1060b /* 0x4182c */
+#define FBNIC_PCS_VL2_0_CHAN_1 0x1060c /* 0x41830 */
+#define FBNIC_PCS_VL2_1_CHAN_1 0x1060d /* 0x41834 */
+#define FBNIC_PCS_VL3_0_CHAN_1 0x1060e /* 0x41838 */
+#define FBNIC_PCS_VL3_1_CHAN_1 0x1060f /* 0x4183c */
+#define FBNIC_PCS_MODE_VL_CHAN_1 0x10610 /* 0x41840 */
+#define FBNIC_CSR_END_PCS 0x10668 /* CSR section delimiter */
+
+#define FBNIC_CSR_START_RSFEC 0x10800 /* CSR section delimiter */
+#define FBNIC_RSFEC_CONTROL(n)\
+ (0x10800 + 8 * (n)) /* 0x42000 + 32*n */
+#define FBNIC_RSFEC_CONTROL_AM16_COPY_DIS CSR_BIT(3)
+#define FBNIC_RSFEC_CONTROL_KP_ENABLE CSR_BIT(8)
+#define FBNIC_RSFEC_CONTROL_TC_PAD_ALTER CSR_BIT(10)
+#define FBNIC_RSFEC_MAX_LANES 4
+#define FBNIC_RSFEC_CCW_LO(n) \
+ (0x10802 + 8 * (n)) /* 0x42008 + 32*n */
+#define FBNIC_RSFEC_CCW_HI(n) \
+ (0x10803 + 8 * (n)) /* 0x4200c + 32*n */
+#define FBNIC_RSFEC_NCCW_LO(n) \
+ (0x10804 + 8 * (n)) /* 0x42010 + 32*n */
+#define FBNIC_RSFEC_NCCW_HI(n) \
+ (0x10805 + 8 * (n)) /* 0x42014 + 32*n */
+#define FBNIC_RSFEC_SYMBLERR_LO(n) \
+ (0x10880 + 8 * (n)) /* 0x42200 + 32*n */
+#define FBNIC_RSFEC_SYMBLERR_HI(n) \
+ (0x10881 + 8 * (n)) /* 0x42204 + 32*n */
+#define FBNIC_CSR_END_RSFEC 0x108c8 /* CSR section delimiter */
+
+/* MAC MAC registers */
+#define FBNIC_CSR_START_MAC_MAC 0x11000 /* CSR section delimiter */
+#define FBNIC_MAC_COMMAND_CONFIG 0x11002 /* 0x44008 */
+#define FBNIC_MAC_COMMAND_CONFIG_RX_PAUSE_DIS CSR_BIT(29)
+#define FBNIC_MAC_COMMAND_CONFIG_TX_PAUSE_DIS CSR_BIT(28)
+#define FBNIC_MAC_COMMAND_CONFIG_FLT_HDL_DIS CSR_BIT(27)
+#define FBNIC_MAC_COMMAND_CONFIG_TX_PAD_EN CSR_BIT(11)
+#define FBNIC_MAC_COMMAND_CONFIG_LOOPBACK_EN CSR_BIT(10)
+#define FBNIC_MAC_COMMAND_CONFIG_PROMISC_EN CSR_BIT(4)
+#define FBNIC_MAC_COMMAND_CONFIG_RX_ENA CSR_BIT(1)
+#define FBNIC_MAC_COMMAND_CONFIG_TX_ENA CSR_BIT(0)
+#define FBNIC_MAC_FRM_LENGTH 0x11005 /* 0x44014 */
+#define FBNIC_MAC_TX_IPG_LENGTH 0x11011 /* 0x44044 */
+#define FBNIC_MAC_TX_IPG_LENGTH_COMP CSR_GENMASK(31, 16)
+#define FBNIC_MAC_TX_IPG_LENGTH_TXIPG CSR_GENMASK(5, 3)
+#define FBNIC_MAC_CL01_PAUSE_QUANTA 0x11015 /* 0x44054 */
+#define FBNIC_MAC_CL01_QUANTA_THRESH 0x11019 /* 0x44064 */
+#define FBNIC_MAC_XIF_MODE 0x11020 /* 0x44080 */
+#define FBNIC_MAC_XIF_MODE_TX_MAC_RS_ERR CSR_BIT(8)
+#define FBNIC_MAC_XIF_MODE_XGMII CSR_BIT(0)
+#define FBNIC_CSR_END_MAC_MAC 0x11028 /* CSR section delimiter */
+
+/* MAC CSR registers */
+#define FBNIC_CSR_START_MAC_CSR 0x11800 /* CSR section delimiter */
+#define FBNIC_MAC_CTRL 0x11800 /* 0x46000 */
+#define FBNIC_MAC_CTRL_RESET_FF_TX_CLK CSR_BIT(14)
+#define FBNIC_MAC_CTRL_RESET_FF_RX_CLK CSR_BIT(13)
+#define FBNIC_MAC_CTRL_RESET_TX_CLK CSR_BIT(12)
+#define FBNIC_MAC_CTRL_RESET_RX_CLK CSR_BIT(11)
+#define FBNIC_MAC_CTRL_TX_CRC CSR_BIT(8)
+#define FBNIC_MAC_CTRL_CFG_MODE128 CSR_BIT(10)
+#define FBNIC_MAC_SERDES_CTRL 0x11807 /* 0x4601c */
+#define FBNIC_MAC_SERDES_CTRL_RESET_PCS_REF_CLK CSR_BIT(26)
+#define FBNIC_MAC_SERDES_CTRL_RESET_F91_REF_CLK CSR_BIT(25)
+#define FBNIC_MAC_SERDES_CTRL_RESET_SD_TX_CLK CSR_GENMASK(24, 23)
+#define FBNIC_MAC_SERDES_CTRL_RESET_SD_RX_CLK CSR_GENMASK(22, 21)
+#define FBNIC_MAC_SERDES_CTRL_SD_8X CSR_GENMASK(18, 17)
+#define FBNIC_MAC_SERDES_CTRL_F91_1LANE_IN0 CSR_BIT(9)
+#define FBNIC_MAC_SERDES_CTRL_RXLAUI_ENA_IN0 CSR_BIT(7)
+#define FBNIC_MAC_SERDES_CTRL_PCS100_ENA_IN0 CSR_BIT(6)
+#define FBNIC_MAC_SERDES_CTRL_PACER_10G_MASK CSR_GENMASK(1, 0)
+#define FBNIC_MAC_PCS_STS0 0x11808 /* 0x46020 */
+#define FBNIC_MAC_PCS_STS0_LINK CSR_BIT(27)
+#define FBNIC_MAC_PCS_STS0_BLOCK_LOCK CSR_GENMASK(24, 5)
+#define FBNIC_MAC_PCS_STS0_AMPS_LOCK CSR_GENMASK(4, 1)
+#define FBNIC_MAC_PCS_STS1 0x11809 /* 0x46024 */
+#define FBNIC_MAC_PCS_STS1_FCFEC_LOCK CSR_GENMASK(11, 8)
+#define FBNIC_MAC_PCS_INTR_STS 0x11814 /* 0x46050 */
+#define FBNIC_MAC_PCS_INTR_LINK_DOWN CSR_BIT(1)
+#define FBNIC_MAC_PCS_INTR_LINK_UP CSR_BIT(0)
+#define FBNIC_MAC_PCS_INTR_MASK 0x11816 /* 0x46058 */
+#define FBNIC_MAC_ENET_LED 0x11820 /* 0x46080 */
+#define FBNIC_MAC_ENET_LED_OVERRIDE_EN CSR_GENMASK(2, 0)
+#define FBNIC_MAC_ENET_LED_OVERRIDE_VAL CSR_GENMASK(6, 4)
+enum {
+ FBNIC_MAC_ENET_LED_OVERRIDE_ACTIVITY = 0x1,
+ FBNIC_MAC_ENET_LED_OVERRIDE_AMBER = 0x2,
+ FBNIC_MAC_ENET_LED_OVERRIDE_BLUE = 0x4,
+};
+
+#define FBNIC_MAC_ENET_LED_BLINK_RATE_MASK CSR_GENMASK(11, 8)
+enum {
+ FBNIC_MAC_ENET_LED_BLINK_RATE_5HZ = 0xf,
+};
+
+#define FBNIC_MAC_ENET_LED_BLUE_MASK CSR_GENMASK(18, 16)
+enum {
+ FBNIC_MAC_ENET_LED_BLUE_50G = 0x2,
+ FBNIC_MAC_ENET_LED_BLUE_100G = 0x4,
+};
+
+#define FBNIC_MAC_ENET_LED_AMBER_MASK CSR_GENMASK(21, 20)
+enum {
+ FBNIC_MAC_ENET_LED_AMBER_25G = 0x1,
+ FBNIC_MAC_ENET_LED_AMBER_50G = 0x2,
+};
+
+#define FBNIC_MAC_ENET_SIG_DETECT 0x11824 /* 0x46090 */
+#define FBNIC_MAC_ENET_SIG_DETECT_PCS_MASK CSR_GENMASK(1, 0)
+#define FBNIC_MAC_ENET_FEC_CTRL 0x11825 /* 0x46094 */
+#define FBNIC_MAC_ENET_FEC_CTRL_FEC_ENA CSR_GENMASK(27, 24)
+#define FBNIC_MAC_ENET_FEC_CTRL_KP_MODE_ENA CSR_GENMASK(11, 8)
+#define FBNIC_MAC_ENET_FEC_CTRL_F91_ENA CSR_GENMASK(3, 0)
+#define FBNIC_CSR_END_MAC_CSR 0x1184e /* CSR section delimiter */
+
/* PUL User Registers */
#define FBNIC_CSR_START_PUL_USER 0x31000 /* CSR section delimiter */
#define FBNIC_PUL_OB_TLP_HDR_AW_CFG 0x3103d /* 0xc40f4 */
@@ -643,6 +643,63 @@ void fbnic_fw_check_heartbeat(struct fbnic_dev *fbd)
dev_warn(fbd->dev, "Failed to send heartbeat message\n");
}
+/**
+ * fbnic_fw_xmit_comphy_set_msg - Create and transmit a comphy set request
+ *
+ * @fbd: FBNIC device structure
+ * @speed: Indicates link speed, composed of modulation and number of lanes
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * Asks the firmware to reconfigure the comphy for this slice to the target
+ * speed.
+ */
+int fbnic_fw_xmit_comphy_set_msg(struct fbnic_dev *fbd, u32 speed)
+{
+ struct fbnic_tlv_msg *msg;
+ int err = 0;
+
+ if (!fbnic_fw_present(fbd))
+ return -ENODEV;
+
+ msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_COMPHY_SET_REQ);
+ if (!msg)
+ return -ENOMEM;
+
+ err = fbnic_tlv_attr_put_int(msg, FBNIC_COMPHY_SET_PAM4,
+ !!(speed & FBNIC_LINK_MODE_PAM4));
+ if (err)
+ goto free_message;
+
+ err = fbnic_mbx_map_tlv_msg(fbd, msg);
+ if (err)
+ goto free_message;
+
+ return 0;
+
+free_message:
+ free_page((unsigned long)msg);
+ return err;
+}
+
+static const struct fbnic_tlv_index fbnic_comphy_set_resp_index[] = {
+ FBNIC_TLV_ATTR_S32(FBNIC_COMPHY_SET_ERROR),
+ FBNIC_TLV_ATTR_LAST
+};
+
+static int fbnic_fw_parse_comphy_set_resp(void *opaque,
+ struct fbnic_tlv_msg **results)
+{
+ struct fbnic_dev *fbd = (struct fbnic_dev *)opaque;
+ int err_resp = 0;
+
+ get_signed_result(FBNIC_COMPHY_SET_ERROR, err_resp);
+ if (err_resp)
+ dev_err(fbd->dev, "COMPHY_SET returned %d\n", err_resp);
+
+ return 0;
+}
+
static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = {
FBNIC_TLV_PARSER(FW_CAP_RESP, fbnic_fw_cap_resp_index,
fbnic_fw_parse_cap_resp),
@@ -650,6 +707,9 @@ static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = {
fbnic_fw_parse_ownership_resp),
FBNIC_TLV_PARSER(HEARTBEAT_RESP, fbnic_heartbeat_resp_index,
fbnic_fw_parse_heartbeat_resp),
+ FBNIC_TLV_PARSER(COMPHY_SET_RESP,
+ fbnic_comphy_set_resp_index,
+ fbnic_fw_parse_comphy_set_resp),
FBNIC_TLV_MSG_ERROR
};
@@ -52,6 +52,7 @@ 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);
+int fbnic_fw_xmit_comphy_set_msg(struct fbnic_dev *fbd, u32 speed);
#define fbnic_mk_full_fw_ver_str(_rev_id, _delim, _commit, _str) \
do { \
@@ -76,6 +77,8 @@ enum {
FBNIC_TLV_MSG_ID_OWNERSHIP_RESP = 0x13,
FBNIC_TLV_MSG_ID_HEARTBEAT_REQ = 0x14,
FBNIC_TLV_MSG_ID_HEARTBEAT_RESP = 0x15,
+ FBNIC_TLV_MSG_ID_COMPHY_SET_REQ = 0x3E,
+ FBNIC_TLV_MSG_ID_COMPHY_SET_RESP = 0x3F,
};
#define FBNIC_FW_CAP_RESP_VERSION_MAJOR CSR_GENMASK(31, 24)
@@ -104,8 +107,27 @@ enum {
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
};
+
+enum {
+ FBNIC_COMPHY_SET_PAM4 = 0x0,
+ FBNIC_COMPHY_SET_ERROR = 0x1,
+ FBNIC_COMPHY_SET_MSG_MAX
+};
#endif /* _FBNIC_FW_H_ */
@@ -84,11 +84,127 @@ void fbnic_fw_disable_mbx(struct fbnic_dev *fbd)
fbnic_mbx_clean(fbd);
}
+static irqreturn_t fbnic_mac_msix_intr(int __always_unused irq, void *data)
+{
+ struct fbnic_dev *fbd = data;
+
+ if (fbd->mac->get_link_event(fbd))
+ fbd->link_state = FBNIC_LINK_EVENT;
+ else
+ wr32(FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_MAC_MSIX_ENTRY);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * fbnic_mac_get_link - Retrieve the current link state of the MAC
+ * @fbd: Device to retrieve the link state of
+ * @link: pointer to boolean value that will store link state
+ *
+ * This function will query the hardware to determine the state of the
+ * hardware to determine the link status of the device. If it is unable to
+ * communicate with the device it will return ENODEV and return false
+ * indicating the link is down.
+ **/
+int fbnic_mac_get_link(struct fbnic_dev *fbd, bool *link)
+{
+ const struct fbnic_mac *mac = fbd->mac;
+
+ *link = true;
+
+ /* In an interrupt driven setup we can just skip the check if
+ * the link is up as the interrupt should toggle it to the EVENT
+ * state if the link has changed state at any time since the last
+ * check.
+ */
+ if (fbd->link_state == FBNIC_LINK_UP)
+ goto skip_check;
+
+ *link = mac->get_link(fbd);
+
+ wr32(FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_MAC_MSIX_ENTRY);
+skip_check:
+ if (!fbnic_present(fbd)) {
+ *link = false;
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * fbnic_mac_enable - Configure the MAC to enable it to advertise link
+ * @fbd: Pointer to device to initialize
+ *
+ * This function provides basic bringup for the CMAC and sets the link
+ * state to FBNIC_LINK_EVENT which tells the link state check that the
+ * current state is unknown and that interrupts must be enabled after the
+ * check is completed.
+ **/
+int fbnic_mac_enable(struct fbnic_dev *fbd)
+{
+ const struct fbnic_mac *mac = fbd->mac;
+ u32 vector = fbd->mac_msix_vector;
+ int err;
+
+ /* Request the IRQ for MAC link vector.
+ * Map MAC cause to it, and unmask it
+ */
+ err = request_irq(vector, &fbnic_mac_msix_intr, 0,
+ fbd->netdev->name, fbd);
+ if (err)
+ return err;
+
+ wr32(FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
+ FBNIC_MAC_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE);
+
+ err = mac->enable(fbd);
+ if (err) {
+ /* Disable interrupt */
+ wr32(FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
+ FBNIC_MAC_MSIX_ENTRY);
+ wr32(FBNIC_INTR_MASK_SET(0), 1u << FBNIC_MAC_MSIX_ENTRY);
+
+ /* Free the vector */
+ free_irq(fbd->mac_msix_vector, fbd);
+ }
+
+ return err;
+}
+
+/**
+ * fbnic_mac_disable - Teardown the MAC to prepare for stopping
+ * @fbd: Pointer to device that is stopping
+ *
+ * This function undoes the work done in fbnic_mac_enable and prepares the
+ * device to no longer receive traffic on the host interface.
+ **/
+void fbnic_mac_disable(struct fbnic_dev *fbd)
+{
+ const struct fbnic_mac *mac = fbd->mac;
+
+ /* Nothing to do if link is already disabled */
+ if (fbd->link_state == FBNIC_LINK_DISABLED)
+ return;
+
+ mac->disable(fbd);
+
+ /* Disable interrupt */
+ wr32(FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
+ FBNIC_MAC_MSIX_ENTRY);
+ wr32(FBNIC_INTR_MASK_SET(0), 1u << FBNIC_MAC_MSIX_ENTRY);
+
+ /* Free the vector */
+ free_irq(fbd->mac_msix_vector, fbd);
+}
+
void fbnic_free_irqs(struct fbnic_dev *fbd)
{
struct pci_dev *pdev = to_pci_dev(fbd->dev);
+ fbd->mac_msix_vector = 0;
fbd->fw_msix_vector = 0;
+
fbd->num_irqs = 0;
pci_disable_msix(pdev);
@@ -128,6 +244,8 @@ int fbnic_alloc_irqs(struct fbnic_dev *fbd)
fbd->msix_entries = msix_entries;
fbd->num_irqs = num_irqs;
+ fbd->mac_msix_vector = msix_entries[FBNIC_MAC_MSIX_ENTRY].vector;
fbd->fw_msix_vector = msix_entries[FBNIC_FW_MSIX_ENTRY].vector;
+
return 0;
}
@@ -2,10 +2,12 @@
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#include <linux/bitfield.h>
+#include <linux/iopoll.h>
#include <net/tcp.h>
#include "fbnic.h"
#include "fbnic_mac.h"
+#include "fbnic_netdev.h"
static void fbnic_init_readrq(struct fbnic_dev *fbd, unsigned int offset,
unsigned int cls, unsigned int readrq)
@@ -415,8 +417,593 @@ static void fbnic_mac_init_regs(struct fbnic_dev *fbd)
fbnic_mac_init_txb(fbd);
}
+static int fbnic_mac_get_link_event_asic(struct fbnic_dev *fbd)
+{
+ u32 pcs_intr_mask = rd32(FBNIC_MAC_PCS_INTR_STS);
+
+ if (pcs_intr_mask & FBNIC_MAC_PCS_INTR_LINK_DOWN)
+ return -1;
+
+ return (pcs_intr_mask & FBNIC_MAC_PCS_INTR_LINK_UP) ? 1 : 0;
+}
+
+static u32 __fbnic_mac_config_asic(struct fbnic_dev *fbd)
+{
+ /* Enable MAC Promiscuous mode and Tx padding */
+ u32 command_config = FBNIC_MAC_COMMAND_CONFIG_TX_PAD_EN |
+ FBNIC_MAC_COMMAND_CONFIG_PROMISC_EN;
+ struct fbnic_net *fbn = netdev_priv(fbd->netdev);
+ u32 rxb_pause_ctrl;
+
+ /* Set class 0 Quanta and refresh */
+ wr32(FBNIC_MAC_CL01_PAUSE_QUANTA, 0xffff);
+ wr32(FBNIC_MAC_CL01_QUANTA_THRESH, 0x7fff);
+
+ /* Enable generation of pause frames if enabled */
+ rxb_pause_ctrl = rd32(FBNIC_RXB_PAUSE_DROP_CTRL);
+ rxb_pause_ctrl &= ~FBNIC_RXB_PAUSE_DROP_CTRL_PAUSE_ENABLE;
+ if (!fbn->tx_pause)
+ command_config |= FBNIC_MAC_COMMAND_CONFIG_TX_PAUSE_DIS;
+ else
+ rxb_pause_ctrl |=
+ FIELD_PREP(FBNIC_RXB_PAUSE_DROP_CTRL_PAUSE_ENABLE,
+ FBNIC_PAUSE_EN_MASK);
+ wr32(FBNIC_RXB_PAUSE_DROP_CTRL, rxb_pause_ctrl);
+
+ if (!fbn->rx_pause)
+ command_config |= FBNIC_MAC_COMMAND_CONFIG_RX_PAUSE_DIS;
+
+ /* Disable fault handling if no FEC is requested */
+ if ((fbn->fec & FBNIC_FEC_MODE_MASK) == FBNIC_FEC_OFF)
+ command_config |= FBNIC_MAC_COMMAND_CONFIG_FLT_HDL_DIS;
+
+ return command_config;
+}
+
+static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd)
+{
+ struct fbnic_net *fbn = netdev_priv(fbd->netdev);
+ u32 pcs_status, lane_mask = ~0;
+
+ pcs_status = rd32(FBNIC_MAC_PCS_STS0);
+ if (!(pcs_status & FBNIC_MAC_PCS_STS0_LINK))
+ return false;
+
+ /* Define the expected lane mask for the status bits we need to check */
+ switch (fbn->link_mode & FBNIC_LINK_MODE_MASK) {
+ case FBNIC_LINK_100R2:
+ lane_mask = 0xf;
+ break;
+ case FBNIC_LINK_50R1:
+ lane_mask = 3;
+ break;
+ case FBNIC_LINK_50R2:
+ switch (fbn->fec & FBNIC_FEC_MODE_MASK) {
+ case FBNIC_FEC_OFF:
+ lane_mask = 0x63;
+ break;
+ case FBNIC_FEC_RS:
+ lane_mask = 5;
+ break;
+ case FBNIC_FEC_BASER:
+ lane_mask = 0xf;
+ break;
+ }
+ break;
+ case FBNIC_LINK_25R1:
+ lane_mask = 1;
+ break;
+ }
+
+ /* Use an XOR to remove the bits we expect to see set */
+ switch (fbn->fec & FBNIC_FEC_MODE_MASK) {
+ case FBNIC_FEC_OFF:
+ lane_mask ^= FIELD_GET(FBNIC_MAC_PCS_STS0_BLOCK_LOCK,
+ pcs_status);
+ break;
+ case FBNIC_FEC_RS:
+ lane_mask ^= FIELD_GET(FBNIC_MAC_PCS_STS0_AMPS_LOCK,
+ pcs_status);
+ break;
+ case FBNIC_FEC_BASER:
+ lane_mask ^= FIELD_GET(FBNIC_MAC_PCS_STS1_FCFEC_LOCK,
+ rd32(FBNIC_MAC_PCS_STS1));
+ break;
+ }
+
+ /* If all lanes cancelled then we have a lock on all lanes */
+ return !lane_mask;
+}
+
+#define FBNIC_MAC_ENET_LED_DEFAULT \
+ (FIELD_PREP(FBNIC_MAC_ENET_LED_AMBER_MASK, \
+ FBNIC_MAC_ENET_LED_AMBER_50G | \
+ FBNIC_MAC_ENET_LED_AMBER_25G) | \
+ FIELD_PREP(FBNIC_MAC_ENET_LED_BLUE_MASK, \
+ FBNIC_MAC_ENET_LED_BLUE_100G | \
+ FBNIC_MAC_ENET_LED_BLUE_50G))
+#define FBNIC_MAC_ENET_LED_ACTIVITY_DEFAULT \
+ FIELD_PREP(FBNIC_MAC_ENET_LED_BLINK_RATE_MASK, \
+ FBNIC_MAC_ENET_LED_BLINK_RATE_5HZ)
+#define FBNIC_MAC_ENET_LED_ACTIVITY_ON \
+ FIELD_PREP(FBNIC_MAC_ENET_LED_OVERRIDE_EN, \
+ FBNIC_MAC_ENET_LED_OVERRIDE_ACTIVITY)
+#define FBNIC_MAC_ENET_LED_AMBER \
+ (FIELD_PREP(FBNIC_MAC_ENET_LED_OVERRIDE_EN, \
+ FBNIC_MAC_ENET_LED_OVERRIDE_BLUE | \
+ FBNIC_MAC_ENET_LED_OVERRIDE_AMBER) | \
+ FIELD_PREP(FBNIC_MAC_ENET_LED_OVERRIDE_VAL, \
+ FBNIC_MAC_ENET_LED_OVERRIDE_AMBER))
+#define FBNIC_MAC_ENET_LED_BLUE \
+ (FIELD_PREP(FBNIC_MAC_ENET_LED_OVERRIDE_EN, \
+ FBNIC_MAC_ENET_LED_OVERRIDE_BLUE | \
+ FBNIC_MAC_ENET_LED_OVERRIDE_AMBER) | \
+ FIELD_PREP(FBNIC_MAC_ENET_LED_OVERRIDE_VAL, \
+ FBNIC_MAC_ENET_LED_OVERRIDE_BLUE))
+
+static void fbnic_set_led_state_asic(struct fbnic_dev *fbd, int state)
+{
+ struct fbnic_net *fbn = netdev_priv(fbd->netdev);
+ u32 led_csr = FBNIC_MAC_ENET_LED_DEFAULT;
+
+ switch (state) {
+ case FBNIC_LED_OFF:
+ led_csr |= FBNIC_MAC_ENET_LED_AMBER |
+ FBNIC_MAC_ENET_LED_ACTIVITY_ON;
+ break;
+ case FBNIC_LED_ON:
+ led_csr |= FBNIC_MAC_ENET_LED_BLUE |
+ FBNIC_MAC_ENET_LED_ACTIVITY_ON;
+ break;
+ case FBNIC_LED_RESTORE:
+ led_csr |= FBNIC_MAC_ENET_LED_ACTIVITY_DEFAULT;
+
+ /* Don't set LEDs on if link isn't up */
+ if (fbd->link_state != FBNIC_LINK_UP)
+ break;
+ /* Don't set LEDs for supported autoneg modes */
+ if ((fbn->link_mode & FBNIC_LINK_AUTO) &&
+ (fbn->link_mode & FBNIC_LINK_MODE_MASK) != FBNIC_LINK_50R2)
+ break;
+
+ /* Set LEDs based on link speed
+ * 100G Blue,
+ * 50G Blue & Amber
+ * 25G Amber
+ */
+ switch (fbn->link_mode & FBNIC_LINK_MODE_MASK) {
+ case FBNIC_LINK_100R2:
+ led_csr |= FBNIC_MAC_ENET_LED_BLUE;
+ break;
+ case FBNIC_LINK_50R1:
+ case FBNIC_LINK_50R2:
+ led_csr |= FBNIC_MAC_ENET_LED_BLUE;
+ fallthrough;
+ case FBNIC_LINK_25R1:
+ led_csr |= FBNIC_MAC_ENET_LED_AMBER;
+ break;
+ }
+ break;
+ default:
+ return;
+ }
+
+ wr32(FBNIC_MAC_ENET_LED, led_csr);
+}
+
+static bool fbnic_mac_get_link_asic(struct fbnic_dev *fbd)
+{
+ u32 cmd_cfg, mac_ctrl;
+ int link_direction;
+ bool link;
+
+ /* If disabled do not update link_state nor change settings */
+ if (fbd->link_state == FBNIC_LINK_DISABLED)
+ return false;
+
+ link_direction = fbnic_mac_get_link_event_asic(fbd);
+
+ /* Clear interrupt state due to recent changes. */
+ wr32(FBNIC_MAC_PCS_INTR_STS,
+ FBNIC_MAC_PCS_INTR_LINK_DOWN | FBNIC_MAC_PCS_INTR_LINK_UP);
+
+ /* If link bounced down clear the PCS_STS bit related to link */
+ if (link_direction < 0) {
+ wr32(FBNIC_MAC_PCS_STS0, FBNIC_MAC_PCS_STS0_LINK |
+ FBNIC_MAC_PCS_STS0_BLOCK_LOCK |
+ FBNIC_MAC_PCS_STS0_AMPS_LOCK);
+ wr32(FBNIC_MAC_PCS_STS1, FBNIC_MAC_PCS_STS1_FCFEC_LOCK);
+ }
+
+ link = fbnic_mac_get_pcs_link_status(fbd);
+ cmd_cfg = __fbnic_mac_config_asic(fbd);
+ mac_ctrl = rd32(FBNIC_MAC_CTRL);
+
+ /* Depending on the event we will unmask the cause that will force a
+ * transition, and update the Tx to reflect our status to the remote
+ * link partner.
+ */
+ if (link) {
+ mac_ctrl &= ~(FBNIC_MAC_CTRL_RESET_FF_TX_CLK |
+ FBNIC_MAC_CTRL_RESET_TX_CLK |
+ FBNIC_MAC_CTRL_RESET_FF_RX_CLK |
+ FBNIC_MAC_CTRL_RESET_RX_CLK);
+ cmd_cfg |= FBNIC_MAC_COMMAND_CONFIG_RX_ENA |
+ FBNIC_MAC_COMMAND_CONFIG_TX_ENA;
+ fbd->link_state = FBNIC_LINK_UP;
+ } else {
+ mac_ctrl |= FBNIC_MAC_CTRL_RESET_FF_TX_CLK |
+ FBNIC_MAC_CTRL_RESET_TX_CLK |
+ FBNIC_MAC_CTRL_RESET_FF_RX_CLK |
+ FBNIC_MAC_CTRL_RESET_RX_CLK;
+ fbd->link_state = FBNIC_LINK_DOWN;
+ }
+
+ wr32(FBNIC_MAC_CTRL, mac_ctrl);
+ wr32(FBNIC_MAC_COMMAND_CONFIG, cmd_cfg);
+
+ /* Toggle LED settings to enable LEDs manually if necessary */
+ fbnic_set_led_state_asic(fbd, FBNIC_LED_RESTORE);
+
+ if (link_direction)
+ wr32(FBNIC_MAC_PCS_INTR_MASK,
+ link ? ~FBNIC_MAC_PCS_INTR_LINK_DOWN :
+ ~FBNIC_MAC_PCS_INTR_LINK_UP);
+
+ return link;
+}
+
+static void fbnic_mac_pre_config(struct fbnic_dev *fbd)
+{
+ u32 serdes_ctrl, mac_ctrl, xif_mode, enet_fec_ctrl = 0;
+ struct fbnic_net *fbn = netdev_priv(fbd->netdev);
+
+ /* set reset bits and enable appending of Tx CRC */
+ mac_ctrl = FBNIC_MAC_CTRL_RESET_FF_TX_CLK |
+ FBNIC_MAC_CTRL_RESET_FF_RX_CLK |
+ FBNIC_MAC_CTRL_RESET_TX_CLK |
+ FBNIC_MAC_CTRL_RESET_RX_CLK |
+ FBNIC_MAC_CTRL_TX_CRC;
+ serdes_ctrl = FBNIC_MAC_SERDES_CTRL_RESET_PCS_REF_CLK |
+ FBNIC_MAC_SERDES_CTRL_RESET_F91_REF_CLK |
+ FBNIC_MAC_SERDES_CTRL_RESET_SD_TX_CLK |
+ FBNIC_MAC_SERDES_CTRL_RESET_SD_RX_CLK;
+ xif_mode = FBNIC_MAC_XIF_MODE_TX_MAC_RS_ERR;
+
+ switch (fbn->link_mode & FBNIC_LINK_MODE_MASK) {
+ case FBNIC_LINK_25R1:
+ /* Enable XGMII to run w/ 10G pacer */
+ xif_mode |= FBNIC_MAC_XIF_MODE_XGMII;
+ serdes_ctrl |= FBNIC_MAC_SERDES_CTRL_PACER_10G_MASK;
+ if (fbn->fec & FBNIC_FEC_RS)
+ serdes_ctrl |= FBNIC_MAC_SERDES_CTRL_F91_1LANE_IN0;
+ break;
+ case FBNIC_LINK_50R2:
+ if (!(fbn->fec & FBNIC_FEC_RS))
+ serdes_ctrl |= FBNIC_MAC_SERDES_CTRL_RXLAUI_ENA_IN0;
+ break;
+ case FBNIC_LINK_100R2:
+ mac_ctrl |= FBNIC_MAC_CTRL_CFG_MODE128;
+ serdes_ctrl |= FBNIC_MAC_SERDES_CTRL_PCS100_ENA_IN0;
+ enet_fec_ctrl |= FBNIC_MAC_ENET_FEC_CTRL_KP_MODE_ENA;
+ fallthrough;
+ case FBNIC_LINK_50R1:
+ serdes_ctrl |= FBNIC_MAC_SERDES_CTRL_SD_8X;
+ if (fbn->fec & FBNIC_FEC_AUTO)
+ fbn->fec = FBNIC_FEC_AUTO | FBNIC_FEC_RS;
+ break;
+ }
+
+ switch (fbn->fec & FBNIC_FEC_MODE_MASK) {
+ case FBNIC_FEC_RS:
+ enet_fec_ctrl |= FBNIC_MAC_ENET_FEC_CTRL_F91_ENA;
+ break;
+ case FBNIC_FEC_BASER:
+ enet_fec_ctrl |= FBNIC_MAC_ENET_FEC_CTRL_FEC_ENA;
+ break;
+ case FBNIC_FEC_OFF:
+ break;
+ default:
+ dev_err(fbd->dev, "Unsupported FEC mode detected");
+ }
+
+ /* Store updated config to MAC */
+ wr32(FBNIC_MAC_CTRL, mac_ctrl);
+ wr32(FBNIC_MAC_SERDES_CTRL, serdes_ctrl);
+ wr32(FBNIC_MAC_XIF_MODE, xif_mode);
+ wr32(FBNIC_MAC_ENET_FEC_CTRL, enet_fec_ctrl);
+
+ /* flush writes to allow time for MAC to go into resets */
+ wrfl();
+
+ /* Set signal detect for all lanes */
+ wr32(FBNIC_MAC_ENET_SIG_DETECT, FBNIC_MAC_ENET_SIG_DETECT_PCS_MASK);
+}
+
+static void fbnic_mac_pcs_config(struct fbnic_dev *fbd)
+{
+ u32 pcs_mode = 0, rsfec_ctrl = 0, vl_intvl = 0;
+ struct fbnic_net *fbn = netdev_priv(fbd->netdev);
+ int i;
+
+ /* Set link mode specific lane and FEC values */
+ switch (fbn->link_mode & FBNIC_LINK_MODE_MASK) {
+ case FBNIC_LINK_25R1:
+ if (fbn->fec & FBNIC_FEC_RS)
+ vl_intvl = 20479;
+ else
+ pcs_mode |= FBNIC_PCS_MODE_DISABLE_MLD;
+ pcs_mode |= FBNIC_PCS_MODE_HI_BER25 |
+ FBNIC_PCS_MODE_ENA_CLAUSE49;
+ break;
+ case FBNIC_LINK_50R1:
+ rsfec_ctrl |= FBNIC_RSFEC_CONTROL_KP_ENABLE;
+ fallthrough;
+ case FBNIC_LINK_50R2:
+ rsfec_ctrl |= FBNIC_RSFEC_CONTROL_TC_PAD_ALTER;
+ vl_intvl = 20479;
+ break;
+ case FBNIC_LINK_100R2:
+ rsfec_ctrl |= FBNIC_RSFEC_CONTROL_AM16_COPY_DIS |
+ FBNIC_RSFEC_CONTROL_KP_ENABLE;
+ pcs_mode |= FBNIC_PCS_MODE_DISABLE_MLD;
+ vl_intvl = 16383;
+ break;
+ }
+
+ for (i = 0; i < 4; i++)
+ wr32(FBNIC_RSFEC_CONTROL(i), rsfec_ctrl);
+
+ wr32(FBNIC_PCS_MODE_VL_CHAN_0, pcs_mode);
+ wr32(FBNIC_PCS_MODE_VL_CHAN_1, pcs_mode);
+
+ wr32(FBNIC_PCS_VENDOR_VL_INTVL_0, vl_intvl);
+ wr32(FBNIC_PCS_VENDOR_VL_INTVL_1, vl_intvl);
+
+ /* Update IPG to account for vl_intvl */
+ wr32(FBNIC_MAC_TX_IPG_LENGTH,
+ FIELD_PREP(FBNIC_MAC_TX_IPG_LENGTH_COMP, vl_intvl) | 0xc);
+
+ /* Program lane markers indicating which lanes are in use
+ * and what speeds we are transmitting at.
+ */
+ switch (fbn->link_mode & FBNIC_LINK_MODE_MASK) {
+ case FBNIC_LINK_100R2:
+ wr32(FBNIC_PCS_VL0_0_CHAN_0, 0x68c1);
+ wr32(FBNIC_PCS_VL0_1_CHAN_0, 0x21);
+ wr32(FBNIC_PCS_VL1_0_CHAN_0, 0x719d);
+ wr32(FBNIC_PCS_VL1_1_CHAN_0, 0x8e);
+ wr32(FBNIC_PCS_VL2_0_CHAN_0, 0x4b59);
+ wr32(FBNIC_PCS_VL2_1_CHAN_0, 0xe8);
+ wr32(FBNIC_PCS_VL3_0_CHAN_0, 0x954d);
+ wr32(FBNIC_PCS_VL3_1_CHAN_0, 0x7b);
+ wr32(FBNIC_PCS_VL0_0_CHAN_1, 0x68c1);
+ wr32(FBNIC_PCS_VL0_1_CHAN_1, 0x21);
+ wr32(FBNIC_PCS_VL1_0_CHAN_1, 0x719d);
+ wr32(FBNIC_PCS_VL1_1_CHAN_1, 0x8e);
+ wr32(FBNIC_PCS_VL2_0_CHAN_1, 0x4b59);
+ wr32(FBNIC_PCS_VL2_1_CHAN_1, 0xe8);
+ wr32(FBNIC_PCS_VL3_0_CHAN_1, 0x954d);
+ wr32(FBNIC_PCS_VL3_1_CHAN_1, 0x7b);
+ break;
+ case FBNIC_LINK_50R2:
+ wr32(FBNIC_PCS_VL0_0_CHAN_1, 0x7690);
+ wr32(FBNIC_PCS_VL0_1_CHAN_1, 0x47);
+ wr32(FBNIC_PCS_VL1_0_CHAN_1, 0xc4f0);
+ wr32(FBNIC_PCS_VL1_1_CHAN_1, 0xe6);
+ wr32(FBNIC_PCS_VL2_0_CHAN_1, 0x65c5);
+ wr32(FBNIC_PCS_VL2_1_CHAN_1, 0x9b);
+ wr32(FBNIC_PCS_VL3_0_CHAN_1, 0x79a2);
+ wr32(FBNIC_PCS_VL3_1_CHAN_1, 0x3d);
+ fallthrough;
+ case FBNIC_LINK_50R1:
+ wr32(FBNIC_PCS_VL0_0_CHAN_0, 0x7690);
+ wr32(FBNIC_PCS_VL0_1_CHAN_0, 0x47);
+ wr32(FBNIC_PCS_VL1_0_CHAN_0, 0xc4f0);
+ wr32(FBNIC_PCS_VL1_1_CHAN_0, 0xe6);
+ wr32(FBNIC_PCS_VL2_0_CHAN_0, 0x65c5);
+ wr32(FBNIC_PCS_VL2_1_CHAN_0, 0x9b);
+ wr32(FBNIC_PCS_VL3_0_CHAN_0, 0x79a2);
+ wr32(FBNIC_PCS_VL3_1_CHAN_0, 0x3d);
+ break;
+ case FBNIC_LINK_25R1:
+ wr32(FBNIC_PCS_VL0_0_CHAN_0, 0x68c1);
+ wr32(FBNIC_PCS_VL0_1_CHAN_0, 0x21);
+ wr32(FBNIC_PCS_VL1_0_CHAN_0, 0xc4f0);
+ wr32(FBNIC_PCS_VL1_1_CHAN_0, 0xe6);
+ wr32(FBNIC_PCS_VL2_0_CHAN_0, 0x65c5);
+ wr32(FBNIC_PCS_VL2_1_CHAN_0, 0x9b);
+ wr32(FBNIC_PCS_VL3_0_CHAN_0, 0x79a2);
+ wr32(FBNIC_PCS_VL3_1_CHAN_0, 0x3d);
+ break;
+ }
+}
+
+static bool fbnic_mac_pcs_reset_complete(struct fbnic_dev *fbd)
+{
+ return !(rd32(FBNIC_PCS_CONTROL1_0) & FBNIC_PCS_CONTROL1_RESET) &&
+ !(rd32(FBNIC_PCS_CONTROL1_1) & FBNIC_PCS_CONTROL1_RESET);
+}
+
+static int fbnic_mac_post_config(struct fbnic_dev *fbd)
+{
+ struct fbnic_net *fbn = netdev_priv(fbd->netdev);
+ u32 serdes_ctrl, reset_complete, lane_mask;
+ int err;
+
+ /* Clear resets for XPCS and F91 reference clocks */
+ serdes_ctrl = rd32(FBNIC_MAC_SERDES_CTRL);
+ serdes_ctrl &= ~FBNIC_MAC_SERDES_CTRL_RESET_PCS_REF_CLK;
+ if (fbn->fec & FBNIC_FEC_RS)
+ serdes_ctrl &= ~FBNIC_MAC_SERDES_CTRL_RESET_F91_REF_CLK;
+ wr32(FBNIC_MAC_SERDES_CTRL, serdes_ctrl);
+
+ /* Reset PCS and flush reset value */
+ wr32(FBNIC_PCS_CONTROL1_0,
+ FBNIC_PCS_CONTROL1_RESET |
+ FBNIC_PCS_CONTROL1_SPEED_SELECT_ALWAYS |
+ FBNIC_PCS_CONTROL1_SPEED_ALWAYS);
+ wr32(FBNIC_PCS_CONTROL1_1,
+ FBNIC_PCS_CONTROL1_RESET |
+ FBNIC_PCS_CONTROL1_SPEED_SELECT_ALWAYS |
+ FBNIC_PCS_CONTROL1_SPEED_ALWAYS);
+
+ /* poll for completion of reset */
+ err = readx_poll_timeout(fbnic_mac_pcs_reset_complete, fbd,
+ reset_complete, reset_complete,
+ 1000, 150000);
+ if (err)
+ return err;
+
+ /* Flush any stale link status info */
+ wr32(FBNIC_MAC_PCS_STS0, FBNIC_MAC_PCS_STS0_LINK |
+ FBNIC_MAC_PCS_STS0_BLOCK_LOCK |
+ FBNIC_MAC_PCS_STS0_AMPS_LOCK);
+
+ /* Report starting state as "Link Event" to force detection of link */
+ fbd->link_state = FBNIC_LINK_EVENT;
+
+ /* Force link down to allow for link detection */
+ netif_carrier_off(fbn->netdev);
+
+ /* create simple bitmask for 2 or 1 lane setups */
+ lane_mask = (fbn->link_mode & FBNIC_LINK_MODE_R2) ? 3 : 1;
+
+ /* release the brakes and allow Tx/Rx to come out of reset */
+ serdes_ctrl &=
+ ~(FIELD_PREP(FBNIC_MAC_SERDES_CTRL_RESET_SD_TX_CLK, lane_mask) |
+ FIELD_PREP(FBNIC_MAC_SERDES_CTRL_RESET_SD_RX_CLK, lane_mask));
+ wr32(FBNIC_MAC_SERDES_CTRL, serdes_ctrl);
+
+ fbn->link_mode &= ~FBNIC_LINK_AUTO;
+
+ /* Ask firmware to configure the PHY for the correct encoding mode */
+ return fbnic_fw_xmit_comphy_set_msg(fbd,
+ fbn->link_mode &
+ FBNIC_LINK_MODE_MASK);
+}
+
+static void fbnic_mac_get_fw_settings(struct fbnic_dev *fbd)
+{
+ struct fbnic_net *fbn = netdev_priv(fbd->netdev);
+ u8 fec = fbn->fec;
+ u8 link_mode;
+
+ /* Update FEC first to reflect FW current mode */
+ if (fbn->fec & FBNIC_FEC_AUTO) {
+ switch (fbd->fw_cap.link_fec) {
+ case FBNIC_FW_LINK_FEC_NONE:
+ fec = FBNIC_FEC_OFF;
+ break;
+ case FBNIC_FW_LINK_FEC_RS:
+ fec = FBNIC_FEC_RS;
+ break;
+ case FBNIC_FW_LINK_FEC_BASER:
+ fec = FBNIC_FEC_BASER;
+ break;
+ default:
+ return;
+ }
+ }
+
+ /* Do nothing if AUTO mode is not engaged */
+ if (fbn->link_mode & FBNIC_LINK_AUTO) {
+ switch (fbd->fw_cap.link_speed) {
+ case FBNIC_FW_LINK_SPEED_25R1:
+ link_mode = FBNIC_LINK_25R1;
+ break;
+ case FBNIC_FW_LINK_SPEED_50R2:
+ link_mode = FBNIC_LINK_50R2;
+ break;
+ case FBNIC_FW_LINK_SPEED_50R1:
+ link_mode = FBNIC_LINK_50R1;
+ fec = FBNIC_FEC_RS;
+ break;
+ case FBNIC_FW_LINK_SPEED_100R2:
+ link_mode = FBNIC_LINK_100R2;
+ fec = FBNIC_FEC_RS;
+ break;
+ default:
+ return;
+ }
+
+ fbn->link_mode = link_mode;
+ fbn->fec = fec;
+ }
+}
+
+static int fbnic_mac_enable_asic(struct fbnic_dev *fbd)
+{
+ /* Mask and clear the PCS interrupt, will be enabled by link handler */
+ wr32(FBNIC_MAC_PCS_INTR_MASK, ~0);
+ wr32(FBNIC_MAC_PCS_INTR_STS, ~0);
+
+ /* Pull in settings from FW */
+ fbnic_mac_get_fw_settings(fbd);
+
+ /* Configure MAC registers */
+ fbnic_mac_pre_config(fbd);
+
+ /* Configure PCS block */
+ fbnic_mac_pcs_config(fbd);
+
+ /* Configure flow control and error correction */
+ wr32(FBNIC_MAC_COMMAND_CONFIG, __fbnic_mac_config_asic(fbd));
+
+ /* Configure maximum frame size */
+ wr32(FBNIC_MAC_FRM_LENGTH, FBNIC_MAX_JUMBO_FRAME_SIZE);
+
+ /* Configure LED defaults */
+ fbnic_set_led_state_asic(fbd, FBNIC_LED_RESTORE);
+
+ return fbnic_mac_post_config(fbd);
+}
+
+static void fbnic_mac_disable_asic(struct fbnic_dev *fbd)
+{
+ u32 mask = FBNIC_MAC_COMMAND_CONFIG_LOOPBACK_EN;
+ u32 cmd_cfg = rd32(FBNIC_MAC_COMMAND_CONFIG);
+ u32 mac_ctrl = rd32(FBNIC_MAC_CTRL);
+
+ /* Clear link state to disable any further transitions */
+ fbd->link_state = FBNIC_LINK_DISABLED;
+
+ /* Clear Tx and Rx enable bits to disable MAC, ignore other values */
+ if (!fbnic_bmc_present(fbd)) {
+ mask |= FBNIC_MAC_COMMAND_CONFIG_RX_ENA |
+ FBNIC_MAC_COMMAND_CONFIG_TX_ENA;
+ mac_ctrl |= FBNIC_MAC_CTRL_RESET_FF_TX_CLK |
+ FBNIC_MAC_CTRL_RESET_TX_CLK |
+ FBNIC_MAC_CTRL_RESET_FF_RX_CLK |
+ FBNIC_MAC_CTRL_RESET_RX_CLK;
+
+ /* Restore LED defaults */
+ fbnic_set_led_state_asic(fbd, FBNIC_LED_RESTORE);
+ }
+
+ /* Check mask for enabled bits, if any set clear and write back */
+ if (mask & cmd_cfg) {
+ wr32(FBNIC_MAC_COMMAND_CONFIG, cmd_cfg & ~mask);
+ wr32(FBNIC_MAC_CTRL, mac_ctrl);
+ }
+
+ /* Disable loopback, and flush write */
+ wr32(FBNIC_PCS_CONTROL1_0,
+ FBNIC_PCS_CONTROL1_RESET |
+ FBNIC_PCS_CONTROL1_SPEED_SELECT_ALWAYS |
+ FBNIC_PCS_CONTROL1_SPEED_ALWAYS);
+ wr32(FBNIC_PCS_CONTROL1_1,
+ FBNIC_PCS_CONTROL1_RESET |
+ FBNIC_PCS_CONTROL1_SPEED_SELECT_ALWAYS |
+ FBNIC_PCS_CONTROL1_SPEED_ALWAYS);
+}
+
static const struct fbnic_mac fbnic_mac_asic = {
+ .enable = fbnic_mac_enable_asic,
+ .disable = fbnic_mac_disable_asic,
.init_regs = fbnic_mac_init_regs,
+ .get_link = fbnic_mac_get_link_asic,
+ .get_link_event = fbnic_mac_get_link_event_asic,
};
/**
@@ -10,14 +10,72 @@ struct fbnic_dev;
#define FBNIC_MAX_JUMBO_FRAME_SIZE 9742
+enum {
+ FBNIC_LINK_DISABLED = 0,
+ FBNIC_LINK_DOWN = 1,
+ FBNIC_LINK_UP = 2,
+ FBNIC_LINK_EVENT = 3,
+};
+
+enum {
+ FBNIC_LED_STROBE_INIT,
+ FBNIC_LED_ON,
+ FBNIC_LED_OFF,
+ FBNIC_LED_RESTORE,
+};
+
+/* Treat the FEC bits as a bitmask laid out as follows:
+ * Bit 0: RS Enabled
+ * Bit 1: BASER(Firecode) Enabled
+ * Bit 2: Autoneg FEC
+ */
+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 moldulation/lanes bitmask:
+ * Bit 0: Lane Count, 0 = R1, 1 = R2
+ * Bit 1: Modulation, 0 = NRZ, 1 = PAM4
+ * Bit 2: Autoneg Modulation/Lane Configuration
+ */
+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.
*
+ * bool (*get_link)(struct fbnic_dev *fbd);
+ * Get the current link state for the MAC.
+ * int (*get_link_event)(struct fbnic_dev *fbd)
+ * Get the current link event status, reports true if link has
+ * changed to either up (1) or down (-1).
+ * void (*enable)(struct fbnic_dev *fbd);
+ * Configure and enable MAC to enable link if not already enabled
+ * void (*disable)(struct fbnic_dev *fbd);
+ * Shutdown the link if we are the only consumer of it.
* void (*init_regs)(struct fbnic_dev *fbd);
* Initialize MAC registers to enable Tx/Rx paths and FIFOs.
*/
struct fbnic_mac {
+ bool (*get_link)(struct fbnic_dev *fbd);
+ int (*get_link_event)(struct fbnic_dev *fbd);
+ int (*enable)(struct fbnic_dev *fbd);
+ void (*disable)(struct fbnic_dev *fbd);
void (*init_regs)(struct fbnic_dev *fbd);
};
@@ -45,6 +45,10 @@ int __fbnic_open(struct fbnic_net *fbn)
if (err)
goto release_ownership;
+ err = fbnic_mac_enable(fbd);
+ if (err)
+ goto release_ownership;
+
return 0;
release_ownership:
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
@@ -72,6 +76,7 @@ static int fbnic_stop(struct net_device *netdev)
struct fbnic_net *fbn = netdev_priv(netdev);
fbnic_down(fbn);
+ fbnic_mac_disable(fbn->fbd);
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
@@ -146,6 +151,13 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd)
netdev->min_mtu = IPV6_MIN_MTU;
netdev->max_mtu = FBNIC_MAX_JUMBO_FRAME_SIZE - ETH_HLEN;
+ /* Default to accept pause frames w/ attempt to autoneg the value */
+ fbn->autoneg_pause = true;
+ fbn->rx_pause = true;
+ fbn->tx_pause = false;
+
+ fbn->fec = FBNIC_FEC_AUTO | FBNIC_FEC_RS;
+ fbn->link_mode = FBNIC_LINK_AUTO | FBNIC_LINK_50R2;
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
@@ -22,9 +22,16 @@ struct fbnic_net {
u16 num_napi;
+ u8 autoneg_pause;
+ u8 tx_pause;
+ u8 rx_pause;
+ u8 fec;
+ u8 link_mode;
+
u16 num_tx_queues;
u16 num_rx_queues;
+ u64 link_down_events;
struct list_head napis;
};
@@ -160,6 +160,78 @@ void fbnic_down(struct fbnic_net *fbn)
fbnic_flush(fbn);
}
+static char *fbnic_report_fec(struct fbnic_dev *fbd)
+{
+ struct fbnic_net *fbn = netdev_priv(fbd->netdev);
+
+ if (fbn->link_mode & FBNIC_LINK_MODE_PAM4)
+ return "Clause 91 RS(544,514)";
+
+ switch (fbn->fec & FBNIC_FEC_MODE_MASK) {
+ case FBNIC_FEC_OFF:
+ return "Off";
+ case FBNIC_FEC_BASER:
+ return "Clause 74 BaseR";
+ case FBNIC_FEC_RS:
+ return "Clause 91 RS(528,514)";
+ }
+
+ return "Unknown";
+}
+
+static void fbnic_link_check(struct fbnic_dev *fbd)
+{
+ struct net_device *netdev = fbd->netdev;
+ bool link_found = false;
+ int err;
+
+ err = fbnic_mac_get_link(fbd, &link_found);
+ if (err) {
+ /* TBD: For now we do nothing. In the future we should have
+ * the link_check function request a reset.
+ *
+ * We would do this here as the reset will likely involve
+ * us having to tear down the interface which will require
+ * us taking the RTNL lock in order to coordinate the
+ * teardown and bringup before and after the reset.
+ */
+ return;
+ }
+
+ if (!link_found) {
+ if (netif_carrier_ok(netdev)) {
+ struct fbnic_net *fbn = netdev_priv(netdev);
+
+ netdev_err(netdev, "NIC Link is Down\n");
+ fbn->link_down_events++;
+ }
+ netif_carrier_off(netdev);
+ return;
+ }
+
+ if (!netif_carrier_ok(netdev)) {
+ struct fbnic_net *fbn = netdev_priv(netdev);
+
+ netdev_info(netdev,
+ "NIC Link is Up, %d Mbps (%s), Flow control: %s\n",
+ ((fbn->link_mode & FBNIC_LINK_MODE_PAM4) ?
+ 50000 : 25000) *
+ ((fbn->link_mode & FBNIC_LINK_MODE_R2) ?
+ 2 : 1),
+ (fbn->link_mode & FBNIC_LINK_MODE_PAM4) ?
+ "PAM4" : "NRZ",
+ (fbn->rx_pause ?
+ (fbn->tx_pause ? "ON - Tx/Rx" : "ON - Rx") :
+ (fbn->tx_pause ? "ON - Tx" : "OFF")));
+ netdev_info(netdev, "FEC autoselect %s encoding: %s\n",
+ (fbn->fec & FBNIC_FEC_AUTO) ?
+ "enabled" : "disabled",
+ fbnic_report_fec(fbd));
+ fbnic_config_drop_mode(fbn);
+ }
+ netif_carrier_on(netdev);
+}
+
static void fbnic_health_check(struct fbnic_dev *fbd)
{
struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
@@ -192,6 +264,7 @@ static void fbnic_service_task(struct work_struct *work)
rtnl_lock();
fbnic_fw_check_heartbeat(fbd);
+ fbnic_link_check(fbd);
fbnic_health_check(fbd);
@@ -1031,9 +1031,14 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq)
static void fbnic_config_drop_mode_rcq(struct fbnic_napi_vector *nv,
struct fbnic_ring *rcq)
{
+ struct fbnic_net *fbn = netdev_priv(nv->napi.dev);
u32 drop_mode, rcq_ctl;
- drop_mode = FBNIC_QUEUE_RDE_CTL0_DROP_IMMEDIATE;
+ /* Drop mode is only supported on when flow control is disabled */
+ if (!fbn->tx_pause)
+ 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) |
@@ -1043,6 +1048,20 @@ 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)
+{
+ struct fbnic_napi_vector *nv;
+ int i;
+
+ list_for_each_entry(nv, &fbn->napis, napis) {
+ for (i = 0; i < nv->rxt_count; i++) {
+ struct fbnic_q_triad *qt = &nv->qt[nv->txt_count + i];
+
+ fbnic_config_drop_mode_rcq(nv, &qt->cmpl);
+ }
+ }
+}
+
static void fbnic_enable_rcq(struct fbnic_napi_vector *nv,
struct fbnic_ring *rcq)
{
@@ -99,6 +99,7 @@ void fbnic_napi_enable(struct fbnic_net *fbn);
void fbnic_napi_disable(struct fbnic_net *fbn);
void fbnic_enable(struct fbnic_net *fbn);
void fbnic_disable(struct fbnic_net *fbn);
+void fbnic_config_drop_mode(struct fbnic_net *fbn);
void fbnic_flush(struct fbnic_net *fbn);
void fbnic_fill(struct fbnic_net *fbn);