From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id B6042A04AC; Tue, 1 Sep 2020 13:58:30 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 15A861C2BC; Tue, 1 Sep 2020 13:51:58 +0200 (CEST) Received: from smtpbguseast2.qq.com (smtpbguseast2.qq.com [54.204.34.130]) by dpdk.org (Postfix) with ESMTP id 259DA1C23E for ; Tue, 1 Sep 2020 13:51:49 +0200 (CEST) X-QQ-mid: bizesmtp5t1598961104td42q2g3a Received: from localhost.localdomain.com (unknown [183.129.236.74]) by esmtp6.qq.com (ESMTP) with id ; Tue, 01 Sep 2020 19:51:44 +0800 (CST) X-QQ-SSF: 01400000000000B0C000000A0000000 X-QQ-FEAT: Gxd91G060N1mrO6T4c3qVKPR9Sczhas3fV7dy+rvmG1rtVgqsWdat3VwSevjl QVqzSxgBWzzYKz0WLz6jwneT+XRkGh1gxpEVOPQnWBw5volL4ifoTivcf3/thHqrHl27NmT J3fNGk8bu+mNTAj+B/+j4e+kvkMvomT7KQvZOBgX39g+QrMW7sJkQIWYdxXxlCiek15A1m6 fA08AZbOKg490LvaaLPQf0RRxPbuKyQ0xwGZf995bmME3RgdriTSskyFG03y8b412VhfSnR hW6/FGbBOJEanCfSaldblSXScqG+QdDZ215rzYNojB5YgRwSqJdtY2rffzC6rnEtiH+WbVp qecAsLHainjQvfBz5ZZovWFAqi5aw== X-QQ-GoodBg: 2 From: Jiawen Wu To: dev@dpdk.org Cc: Jiawen Wu Date: Tue, 1 Sep 2020 19:51:07 +0800 Message-Id: <20200901115113.1529675-36-jiawenwu@trustnetic.com> X-Mailer: git-send-email 2.18.4 In-Reply-To: <20200901115113.1529675-1-jiawenwu@trustnetic.com> References: <20200901115113.1529675-1-jiawenwu@trustnetic.com> X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:trustnetic.com:qybgforeign:qybgforeign7 X-QQ-Bgrelay: 1 Subject: [dpdk-dev] [PATCH v1 36/42] net/txgbe: add flow control support X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Add flow control support. Signed-off-by: Jiawen Wu --- drivers/net/txgbe/base/txgbe_hw.c | 426 ++++++++++++++++++++++++++++ drivers/net/txgbe/base/txgbe_hw.h | 6 + drivers/net/txgbe/base/txgbe_type.h | 24 ++ drivers/net/txgbe/txgbe_ethdev.c | 118 +++++++- drivers/net/txgbe/txgbe_ethdev.h | 8 + 5 files changed, 581 insertions(+), 1 deletion(-) diff --git a/drivers/net/txgbe/base/txgbe_hw.c b/drivers/net/txgbe/base/txgbe_hw.c index 80ecec34d..34e7c3d1e 100644 --- a/drivers/net/txgbe/base/txgbe_hw.c +++ b/drivers/net/txgbe/base/txgbe_hw.c @@ -22,6 +22,212 @@ STATIC s32 txgbe_mta_vector(struct txgbe_hw *hw, u8 *mc_addr); STATIC s32 txgbe_get_san_mac_addr_offset(struct txgbe_hw *hw, u16 *san_mac_offset); +/** + * txgbe_device_supports_autoneg_fc - Check if device supports autonegotiation + * of flow control + * @hw: pointer to hardware structure + * + * This function returns true if the device supports flow control + * autonegotiation, and false if it does not. + * + **/ +bool txgbe_device_supports_autoneg_fc(struct txgbe_hw *hw) +{ + bool supported = false; + u32 speed; + bool link_up; + + DEBUGFUNC("txgbe_device_supports_autoneg_fc"); + + switch (hw->phy.media_type) { + case txgbe_media_type_fiber_qsfp: + case txgbe_media_type_fiber: + hw->mac.check_link(hw, &speed, &link_up, false); + /* if link is down, assume supported */ + if (link_up) + supported = speed == TXGBE_LINK_SPEED_1GB_FULL ? + true : false; + else + supported = true; + + break; + case txgbe_media_type_backplane: + supported = true; + break; + case txgbe_media_type_copper: + /* only some copper devices support flow control autoneg */ + switch (hw->device_id) { + case TXGBE_DEV_ID_RAPTOR_XAUI: + case TXGBE_DEV_ID_RAPTOR_SGMII: + supported = true; + break; + default: + supported = false; + } + default: + break; + } + + if (!supported) + DEBUGOUT("Device %x does not support flow control autoneg", + hw->device_id); + return supported; +} + +/** + * txgbe_setup_fc - Set up flow control + * @hw: pointer to hardware structure + * + * Called at init time to set up flow control. + **/ +s32 txgbe_setup_fc(struct txgbe_hw *hw) +{ + s32 err = 0; + u32 reg = 0; + u16 reg_cu = 0; + u32 value = 0; + u64 reg_bp = 0; + bool locked = false; + + DEBUGFUNC("txgbe_setup_fc"); + + /* Validate the requested mode */ + if (hw->fc.strict_ieee && hw->fc.requested_mode == txgbe_fc_rx_pause) { + DEBUGOUT("txgbe_fc_rx_pause not valid in strict IEEE mode\n"); + err = TXGBE_ERR_INVALID_LINK_SETTINGS; + goto out; + } + + /* + * 10gig parts do not have a word in the EEPROM to determine the + * default flow control setting, so we explicitly set it to full. + */ + if (hw->fc.requested_mode == txgbe_fc_default) + hw->fc.requested_mode = txgbe_fc_full; + + /* + * Set up the 1G and 10G flow control advertisement registers so the + * HW will be able to do fc autoneg once the cable is plugged in. If + * we link at 10G, the 1G advertisement is harmless and vice versa. + */ + switch (hw->phy.media_type) { + case txgbe_media_type_backplane: + /* some MAC's need RMW protection on AUTOC */ + err = hw->mac.prot_autoc_read(hw, &locked, ®_bp); + if (err != 0) + goto out; + + /* fall through - only backplane uses autoc */ + case txgbe_media_type_fiber_qsfp: + case txgbe_media_type_fiber: + case txgbe_media_type_copper: + hw->phy.read_reg(hw, TXGBE_MD_AUTO_NEG_ADVT, + TXGBE_MD_DEV_AUTO_NEG, ®_cu); + break; + default: + break; + } + + /* + * The possible values of fc.requested_mode are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but + * we do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + * other: Invalid. + */ + switch (hw->fc.requested_mode) { + case txgbe_fc_none: + /* Flow control completely disabled by software override. */ + reg &= ~(SR_MII_MMD_AN_ADV_PAUSE_SYM | + SR_MII_MMD_AN_ADV_PAUSE_ASM); + if (hw->phy.media_type == txgbe_media_type_backplane) + reg_bp &= ~(TXGBE_AUTOC_SYM_PAUSE | + TXGBE_AUTOC_ASM_PAUSE); + else if (hw->phy.media_type == txgbe_media_type_copper) + reg_cu &= ~(TXGBE_TAF_SYM_PAUSE | TXGBE_TAF_ASM_PAUSE); + break; + case txgbe_fc_tx_pause: + /* + * Tx Flow control is enabled, and Rx Flow control is + * disabled by software override. + */ + reg |= SR_MII_MMD_AN_ADV_PAUSE_ASM; + reg &= ~SR_MII_MMD_AN_ADV_PAUSE_SYM; + if (hw->phy.media_type == txgbe_media_type_backplane) { + reg_bp |= TXGBE_AUTOC_ASM_PAUSE; + reg_bp &= ~TXGBE_AUTOC_SYM_PAUSE; + } else if (hw->phy.media_type == txgbe_media_type_copper) { + reg_cu |= TXGBE_TAF_ASM_PAUSE; + reg_cu &= ~TXGBE_TAF_SYM_PAUSE; + } + reg |= SR_MII_MMD_AN_ADV_PAUSE_ASM; + reg_bp |= SR_AN_MMD_ADV_REG1_PAUSE_ASM; + break; + case txgbe_fc_rx_pause: + /* + * Rx Flow control is enabled and Tx Flow control is + * disabled by software override. Since there really + * isn't a way to advertise that we are capable of RX + * Pause ONLY, we will advertise that we support both + * symmetric and asymmetric Rx PAUSE, as such we fall + * through to the fc_full statement. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + case txgbe_fc_full: + /* Flow control (both Rx and Tx) is enabled by SW override. */ + reg |= SR_MII_MMD_AN_ADV_PAUSE_SYM | + SR_MII_MMD_AN_ADV_PAUSE_ASM; + if (hw->phy.media_type == txgbe_media_type_backplane) + reg_bp |= TXGBE_AUTOC_SYM_PAUSE | + TXGBE_AUTOC_ASM_PAUSE; + else if (hw->phy.media_type == txgbe_media_type_copper) + reg_cu |= TXGBE_TAF_SYM_PAUSE | TXGBE_TAF_ASM_PAUSE; + reg |= SR_MII_MMD_AN_ADV_PAUSE_SYM | + SR_MII_MMD_AN_ADV_PAUSE_ASM; + reg_bp |= SR_AN_MMD_ADV_REG1_PAUSE_SYM | + SR_AN_MMD_ADV_REG1_PAUSE_ASM; + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + err = TXGBE_ERR_CONFIG; + goto out; + break; + } + + /* + * Enable auto-negotiation between the MAC & PHY; + * the MAC will advertise clause 37 flow control. + */ + value = rd32_epcs(hw, SR_MII_MMD_AN_ADV); + value = (value & ~(SR_MII_MMD_AN_ADV_PAUSE_ASM | + SR_MII_MMD_AN_ADV_PAUSE_SYM)) | reg; + wr32_epcs(hw, SR_MII_MMD_AN_ADV, value); + + /* + * AUTOC restart handles negotiation of 1G and 10G on backplane + * and copper. There is no need to set the PCS1GCTL register. + * + */ + if (hw->phy.media_type == txgbe_media_type_backplane) { + value = rd32_epcs(hw, SR_AN_MMD_ADV_REG1); + value = (value & ~(SR_AN_MMD_ADV_REG1_PAUSE_ASM | + SR_AN_MMD_ADV_REG1_PAUSE_SYM)) | + reg_bp; + wr32_epcs(hw, SR_AN_MMD_ADV_REG1, value); + } else if ((hw->phy.media_type == txgbe_media_type_copper) && + (txgbe_device_supports_autoneg_fc(hw))) { + hw->phy.write_reg(hw, TXGBE_MD_AUTO_NEG_ADVT, + TXGBE_MD_DEV_AUTO_NEG, reg_cu); + } + + DEBUGOUT("Set up FC; reg = 0x%08X\n", reg); +out: + return err; +} + /** * txgbe_start_hw - Prepare hardware for Tx/Rx * @hw: pointer to hardware structure @@ -33,6 +239,7 @@ STATIC s32 txgbe_get_san_mac_addr_offset(struct txgbe_hw *hw, **/ s32 txgbe_start_hw(struct txgbe_hw *hw) { + s32 err; u16 device_caps; DEBUGFUNC("txgbe_start_hw"); @@ -43,6 +250,13 @@ s32 txgbe_start_hw(struct txgbe_hw *hw) /* Clear statistics registers */ hw->mac.clear_hw_cntrs(hw); + /* Setup flow control */ + err = txgbe_setup_fc(hw); + if (err != 0 && err != TXGBE_NOT_IMPLEMENTED) { + DEBUGOUT("Flow control setup failed, returning %d\n", err); + return err; + } + /* Cache bit indicating need for crosstalk fix */ switch (hw->mac.type) { case txgbe_mac_raptor: @@ -717,6 +931,136 @@ s32 txgbe_update_mc_addr_list(struct txgbe_hw *hw, u8 *mc_addr_list, return 0; } +/** + * txgbe_fc_enable - Enable flow control + * @hw: pointer to hardware structure + * + * Enable flow control according to the current settings. + **/ +s32 txgbe_fc_enable(struct txgbe_hw *hw) +{ + s32 err = 0; + u32 mflcn_reg, fccfg_reg; + u32 pause_time; + u32 fcrtl, fcrth; + int i; + + DEBUGFUNC("txgbe_fc_enable"); + + /* Validate the water mark configuration */ + if (!hw->fc.pause_time) { + err = TXGBE_ERR_INVALID_LINK_SETTINGS; + goto out; + } + + /* Low water mark of zero causes XOFF floods */ + for (i = 0; i < TXGBE_DCB_TC_MAX; i++) { + if ((hw->fc.current_mode & txgbe_fc_tx_pause) && + hw->fc.high_water[i]) { + if (!hw->fc.low_water[i] || + hw->fc.low_water[i] >= hw->fc.high_water[i]) { + DEBUGOUT("Invalid water mark configuration\n"); + err = TXGBE_ERR_INVALID_LINK_SETTINGS; + goto out; + } + } + } + + /* Negotiate the fc mode to use */ + hw->mac.fc_autoneg(hw); + + /* Disable any previous flow control settings */ + mflcn_reg = rd32(hw, TXGBE_RXFCCFG); + mflcn_reg &= ~(TXGBE_RXFCCFG_FC | TXGBE_RXFCCFG_PFC); + + fccfg_reg = rd32(hw, TXGBE_TXFCCFG); + fccfg_reg &= ~(TXGBE_TXFCCFG_FC | TXGBE_TXFCCFG_PFC); + + /* + * The possible values of fc.current_mode are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but + * we do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + * other: Invalid. + */ + switch (hw->fc.current_mode) { + case txgbe_fc_none: + /* + * Flow control is disabled by software override or autoneg. + * The code below will actually disable it in the HW. + */ + break; + case txgbe_fc_rx_pause: + /* + * Rx Flow control is enabled and Tx Flow control is + * disabled by software override. Since there really + * isn't a way to advertise that we are capable of RX + * Pause ONLY, we will advertise that we support both + * symmetric and asymmetric Rx PAUSE. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + mflcn_reg |= TXGBE_RXFCCFG_FC; + break; + case txgbe_fc_tx_pause: + /* + * Tx Flow control is enabled, and Rx Flow control is + * disabled by software override. + */ + fccfg_reg |= TXGBE_TXFCCFG_FC; + break; + case txgbe_fc_full: + /* Flow control (both Rx and Tx) is enabled by SW override. */ + mflcn_reg |= TXGBE_RXFCCFG_FC; + fccfg_reg |= TXGBE_TXFCCFG_FC; + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + err = TXGBE_ERR_CONFIG; + goto out; + } + + /* Set 802.3x based flow control settings. */ + wr32(hw, TXGBE_RXFCCFG, mflcn_reg); + wr32(hw, TXGBE_TXFCCFG, fccfg_reg); + + /* Set up and enable Rx high/low water mark thresholds, enable XON. */ + for (i = 0; i < TXGBE_DCB_TC_MAX; i++) { + if ((hw->fc.current_mode & txgbe_fc_tx_pause) && + hw->fc.high_water[i]) { + fcrtl = TXGBE_FCWTRLO_TH(hw->fc.low_water[i]) | + TXGBE_FCWTRLO_XON; + fcrth = TXGBE_FCWTRHI_TH(hw->fc.high_water[i]) | + TXGBE_FCWTRHI_XOFF; + } else { + /* + * In order to prevent Tx hangs when the internal Tx + * switch is enabled we must set the high water mark + * to the Rx packet buffer size - 24KB. This allows + * the Tx switch to function even under heavy Rx + * workloads. + */ + fcrtl = 0; + fcrth = rd32(hw, TXGBE_PBRXSIZE(i)) - 24576; + } + wr32(hw, TXGBE_FCWTRLO(i), fcrtl); + wr32(hw, TXGBE_FCWTRHI(i), fcrth); + } + + /* Configure pause time (2 TCs per register) */ + pause_time = TXGBE_RXFCFSH_TIME(hw->fc.pause_time); + for (i = 0; i < (TXGBE_DCB_TC_MAX / 2); i++) + wr32(hw, TXGBE_FCXOFFTM(i), pause_time * 0x00010001); + + /* Configure flow control refresh threshold value */ + wr32(hw, TXGBE_RXFCRFSH, hw->fc.pause_time / 2); + +out: + return err; +} + /** * txgbe_acquire_swfw_sync - Acquire SWFW semaphore * @hw: pointer to hardware structure @@ -1652,6 +1996,82 @@ s32 txgbe_setup_sfp_modules(struct txgbe_hw *hw) return err; } +/** + * txgbe_prot_autoc_read_raptor - Hides MAC differences needed for AUTOC read + * @hw: pointer to hardware structure + * @locked: Return the if we locked for this read. + * @value: Value we read from AUTOC + * + * For this part we need to wrap read-modify-writes with a possible + * FW/SW lock. It is assumed this lock will be freed with the next + * prot_autoc_write_raptor(). + */ +s32 txgbe_prot_autoc_read_raptor(struct txgbe_hw *hw, bool *locked, u64 *value) +{ + s32 err; + bool lock_state = false; + + /* If LESM is on then we need to hold the SW/FW semaphore. */ + if (txgbe_verify_lesm_fw_enabled_raptor(hw)) { + err = hw->mac.acquire_swfw_sync(hw, + TXGBE_MNGSEM_SWPHY); + if (err != 0) + return TXGBE_ERR_SWFW_SYNC; + + lock_state = true; + } + + if (locked) + *locked = lock_state; + + *value = txgbe_autoc_read(hw); + return 0; +} + +/** + * txgbe_prot_autoc_write_raptor - Hides MAC differences needed for AUTOC write + * @hw: pointer to hardware structure + * @autoc: value to write to AUTOC + * @locked: bool to indicate whether the SW/FW lock was already taken by + * previous prot_autoc_read_raptor. + * + * This part may need to hold the SW/FW lock around all writes to + * AUTOC. Likewise after a write we need to do a pipeline reset. + */ +s32 txgbe_prot_autoc_write_raptor(struct txgbe_hw *hw, bool locked, u64 autoc) +{ + int err = 0; + + /* Blocked by MNG FW so bail */ + if (txgbe_check_reset_blocked(hw)) + goto out; + + /* We only need to get the lock if: + * - We didn't do it already (in the read part of a read-modify-write) + * - LESM is enabled. + */ + if (!locked && txgbe_verify_lesm_fw_enabled_raptor(hw)) { + err = hw->mac.acquire_swfw_sync(hw, + TXGBE_MNGSEM_SWPHY); + if (err != 0) + return TXGBE_ERR_SWFW_SYNC; + + locked = true; + } + + txgbe_autoc_write(hw, autoc); + err = txgbe_reset_pipeline_raptor(hw); + +out: + /* Free the SW/FW semaphore as we either grabbed it here or + * already had it when this function was called. + */ + if (locked) + hw->mac.release_swfw_sync(hw, TXGBE_MNGSEM_SWPHY); + + return err; +} + /** * txgbe_init_ops_pf - Inits func ptrs and MAC type * @hw: pointer to hardware structure @@ -1712,6 +2132,8 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw) mac->get_wwn_prefix = txgbe_get_wwn_prefix; mac->autoc_read = txgbe_autoc_read; mac->autoc_write = txgbe_autoc_write; + mac->prot_autoc_read = txgbe_prot_autoc_read_raptor; + mac->prot_autoc_write = txgbe_prot_autoc_write_raptor; mac->set_rar = txgbe_set_rar; mac->clear_rar = txgbe_clear_rar; @@ -1720,6 +2142,10 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw) mac->disable_rx = txgbe_disable_rx; mac->init_uta_tables = txgbe_init_uta_tables; mac->setup_sfp = txgbe_setup_sfp_modules; + + /* Flow Control */ + mac->fc_enable = txgbe_fc_enable; + mac->setup_fc = txgbe_setup_fc; /* Link */ mac->get_link_capabilities = txgbe_get_link_capabilities_raptor; mac->check_link = txgbe_check_mac_link; diff --git a/drivers/net/txgbe/base/txgbe_hw.h b/drivers/net/txgbe/base/txgbe_hw.h index a5ee3ec0a..1604d1fca 100644 --- a/drivers/net/txgbe/base/txgbe_hw.h +++ b/drivers/net/txgbe/base/txgbe_hw.h @@ -32,6 +32,10 @@ s32 txgbe_enable_sec_rx_path(struct txgbe_hw *hw); s32 txgbe_disable_sec_tx_path(struct txgbe_hw *hw); s32 txgbe_enable_sec_tx_path(struct txgbe_hw *hw); +s32 txgbe_fc_enable(struct txgbe_hw *hw); +bool txgbe_device_supports_autoneg_fc(struct txgbe_hw *hw); +s32 txgbe_setup_fc(struct txgbe_hw *hw); + s32 txgbe_validate_mac_addr(u8 *mac_addr); s32 txgbe_acquire_swfw_sync(struct txgbe_hw *hw, u32 mask); void txgbe_release_swfw_sync(struct txgbe_hw *hw, u32 mask); @@ -82,5 +86,7 @@ s32 txgbe_reset_hw(struct txgbe_hw *hw); s32 txgbe_start_hw_raptor(struct txgbe_hw *hw); s32 txgbe_init_phy_raptor(struct txgbe_hw *hw); s32 txgbe_enable_rx_dma_raptor(struct txgbe_hw *hw, u32 regval); +s32 txgbe_prot_autoc_read_raptor(struct txgbe_hw *hw, bool *locked, u64 *value); +s32 txgbe_prot_autoc_write_raptor(struct txgbe_hw *hw, bool locked, u64 value); bool txgbe_verify_lesm_fw_enabled_raptor(struct txgbe_hw *hw); #endif /* _TXGBE_HW_H_ */ diff --git a/drivers/net/txgbe/base/txgbe_type.h b/drivers/net/txgbe/base/txgbe_type.h index 86fb6e259..4a30a99db 100644 --- a/drivers/net/txgbe/base/txgbe_type.h +++ b/drivers/net/txgbe/base/txgbe_type.h @@ -5,6 +5,7 @@ #ifndef _TXGBE_TYPE_H_ #define _TXGBE_TYPE_H_ +#define TXGBE_DCB_TC_MAX TXGBE_MAX_UP #define TXGBE_LINK_UP_TIME 90 /* 9.0 Seconds */ #define TXGBE_AUTO_NEG_TIME 45 /* 4.5 Seconds */ @@ -128,6 +129,14 @@ enum txgbe_media_type { txgbe_media_type_virtual }; +/* Flow Control Settings */ +enum txgbe_fc_mode { + txgbe_fc_none = 0, + txgbe_fc_rx_pause, + txgbe_fc_tx_pause, + txgbe_fc_full, + txgbe_fc_default +}; /* Smart Speed Settings */ #define TXGBE_SMARTSPEED_MAX_RETRIES 3 @@ -196,6 +205,20 @@ struct txgbe_bus_info { u8 lan_id; u16 instance_id; }; + +/* Flow control parameters */ +struct txgbe_fc_info { + u32 high_water[TXGBE_DCB_TC_MAX]; /* Flow Ctrl High-water */ + u32 low_water[TXGBE_DCB_TC_MAX]; /* Flow Ctrl Low-water */ + u16 pause_time; /* Flow Control Pause timer */ + bool send_xon; /* Flow control send XON */ + bool strict_ieee; /* Strict IEEE mode */ + bool disable_fc_autoneg; /* Do not autonegotiate FC */ + bool fc_was_autonegged; /* Is current_mode the result of autonegging? */ + enum txgbe_fc_mode current_mode; /* FC mode in effect */ + enum txgbe_fc_mode requested_mode; /* FC mode requested by caller */ +}; + /* Statistics counters collected by the MAC */ /* PB[] RxTx */ struct txgbe_pb_stats { @@ -597,6 +620,7 @@ struct txgbe_hw { void *back; struct txgbe_mac_info mac; struct txgbe_addr_filter_info addr_ctrl; + struct txgbe_fc_info fc; struct txgbe_phy_info phy; struct txgbe_link_info link; struct txgbe_rom_info rom; diff --git a/drivers/net/txgbe/txgbe_ethdev.c b/drivers/net/txgbe/txgbe_ethdev.c index 5e0b800ef..cab89f5f8 100644 --- a/drivers/net/txgbe/txgbe_ethdev.c +++ b/drivers/net/txgbe/txgbe_ethdev.c @@ -367,7 +367,7 @@ eth_txgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused) const struct rte_memzone *mz; uint32_t ctrl_ext; uint16_t csum; - int err; + int err, i; PMD_INIT_FUNC_TRACE(); @@ -427,6 +427,16 @@ eth_txgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused) /* Unlock any pending hardware semaphore */ txgbe_swfw_lock_reset(hw); + /* Get Hardware Flow Control setting */ + hw->fc.requested_mode = txgbe_fc_full; + hw->fc.current_mode = txgbe_fc_full; + hw->fc.pause_time = TXGBE_FC_PAUSE_TIME; + for (i = 0; i < TXGBE_DCB_TC_MAX; i++) { + hw->fc.low_water[i] = TXGBE_FC_XON_LOTH; + hw->fc.high_water[i] = TXGBE_FC_XOFF_HITH; + } + hw->fc.send_xon = 1; + err = hw->rom.init_params(hw); if (err != 0) { PMD_INIT_LOG(ERR, "The EEPROM init failed: %d", err); @@ -2438,6 +2448,110 @@ txgbe_dev_led_off(struct rte_eth_dev *dev) return txgbe_led_off(hw, 4) == 0 ? 0 : -ENOTSUP; } +static int +txgbe_flow_ctrl_get(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf) +{ + struct txgbe_hw *hw; + uint32_t mflcn_reg; + uint32_t fccfg_reg; + int rx_pause; + int tx_pause; + + hw = TXGBE_DEV_HW(dev); + + fc_conf->pause_time = hw->fc.pause_time; + fc_conf->high_water = hw->fc.high_water[0]; + fc_conf->low_water = hw->fc.low_water[0]; + fc_conf->send_xon = hw->fc.send_xon; + fc_conf->autoneg = !hw->fc.disable_fc_autoneg; + + /* + * Return rx_pause status according to actual setting of + * RXFCCFG register. + */ + mflcn_reg = rd32(hw, TXGBE_RXFCCFG); + if (mflcn_reg & (TXGBE_RXFCCFG_FC | TXGBE_RXFCCFG_PFC)) + rx_pause = 1; + else + rx_pause = 0; + + /* + * Return tx_pause status according to actual setting of + * TXFCCFG register. + */ + fccfg_reg = rd32(hw, TXGBE_TXFCCFG); + if (fccfg_reg & (TXGBE_TXFCCFG_FC | TXGBE_TXFCCFG_PFC)) + tx_pause = 1; + else + tx_pause = 0; + + if (rx_pause && tx_pause) + fc_conf->mode = RTE_FC_FULL; + else if (rx_pause) + fc_conf->mode = RTE_FC_RX_PAUSE; + else if (tx_pause) + fc_conf->mode = RTE_FC_TX_PAUSE; + else + fc_conf->mode = RTE_FC_NONE; + + return 0; +} + +static int +txgbe_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf) +{ + struct txgbe_hw *hw; + int err; + uint32_t rx_buf_size; + uint32_t max_high_water; + enum txgbe_fc_mode rte_fcmode_2_txgbe_fcmode[] = { + txgbe_fc_none, + txgbe_fc_rx_pause, + txgbe_fc_tx_pause, + txgbe_fc_full + }; + + PMD_INIT_FUNC_TRACE(); + + hw = TXGBE_DEV_HW(dev); + rx_buf_size = rd32(hw, TXGBE_PBRXSIZE(0)); + PMD_INIT_LOG(DEBUG, "Rx packet buffer size = 0x%x", rx_buf_size); + + /* + * At least reserve one Ethernet frame for watermark + * high_water/low_water in kilo bytes for txgbe + */ + max_high_water = (rx_buf_size - RTE_ETHER_MAX_LEN) >> 10; + if ((fc_conf->high_water > max_high_water) || + (fc_conf->high_water < fc_conf->low_water)) { + PMD_INIT_LOG(ERR, "Invalid high/low water setup value in KB"); + PMD_INIT_LOG(ERR, "High_water must <= 0x%x", max_high_water); + return -EINVAL; + } + + hw->fc.requested_mode = rte_fcmode_2_txgbe_fcmode[fc_conf->mode]; + hw->fc.pause_time = fc_conf->pause_time; + hw->fc.high_water[0] = fc_conf->high_water; + hw->fc.low_water[0] = fc_conf->low_water; + hw->fc.send_xon = fc_conf->send_xon; + hw->fc.disable_fc_autoneg = !fc_conf->autoneg; + + err = txgbe_fc_enable(hw); + + /* Not negotiated is not an error case */ + if ((err == 0) || (err == TXGBE_ERR_FC_NOT_NEGOTIATED)) { + wr32m(hw, TXGBE_MACRXFLT, TXGBE_MACRXFLT_CTL_MASK, + (fc_conf->mac_ctrl_frame_fwd + ? TXGBE_MACRXFLT_CTL_NOPS : TXGBE_MACRXFLT_CTL_DROP)); + txgbe_flush(hw); + + return 0; + } + + PMD_INIT_LOG(ERR, "txgbe_fc_enable = 0x%x", err); + return -EIO; +} + static int txgbe_add_rar(struct rte_eth_dev *dev, struct rte_ether_addr *mac_addr, uint32_t index, uint32_t pool) @@ -2816,6 +2930,8 @@ static const struct eth_dev_ops txgbe_eth_dev_ops = { .tx_queue_release = txgbe_dev_tx_queue_release, .dev_led_on = txgbe_dev_led_on, .dev_led_off = txgbe_dev_led_off, + .flow_ctrl_get = txgbe_flow_ctrl_get, + .flow_ctrl_set = txgbe_flow_ctrl_set, .mac_addr_add = txgbe_add_rar, .mac_addr_remove = txgbe_remove_rar, .mac_addr_set = txgbe_set_default_mac_addr, diff --git a/drivers/net/txgbe/txgbe_ethdev.h b/drivers/net/txgbe/txgbe_ethdev.h index 5319f42b3..667b11127 100644 --- a/drivers/net/txgbe/txgbe_ethdev.h +++ b/drivers/net/txgbe/txgbe_ethdev.h @@ -222,6 +222,14 @@ void txgbe_pf_mbx_process(struct rte_eth_dev *eth_dev); int txgbe_set_queue_rate_limit(struct rte_eth_dev *dev, uint16_t queue_idx, uint16_t tx_rate); +/* High threshold controlling when to start sending XOFF frames. */ +#define TXGBE_FC_XOFF_HITH 128 /*KB*/ +/* Low threshold controlling when to start sending XON frames. */ +#define TXGBE_FC_XON_LOTH 64 /*KB*/ + +/* Timer value included in XOFF frames. */ +#define TXGBE_FC_PAUSE_TIME 0x680 + #define TXGBE_LINK_DOWN_CHECK_TIMEOUT 4000 /* ms */ #define TXGBE_LINK_UP_CHECK_TIMEOUT 1000 /* ms */ #define TXGBE_VMDQ_NUM_UC_MAC 4096 /* Maximum nb. of UC MAC addr. */ -- 2.18.4