From patchwork Wed Jan 22 05:09:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dominic Chen X-Patchwork-Id: 65023 X-Patchwork-Delegate: qi.z.zhang@intel.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 0C791A0525; Wed, 22 Jan 2020 06:09:51 +0100 (CET) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 750E32A62; Wed, 22 Jan 2020 06:09:50 +0100 (CET) Received: from mail-qv1-f67.google.com (mail-qv1-f67.google.com [209.85.219.67]) by dpdk.org (Postfix) with ESMTP id 485DDF04 for ; Wed, 22 Jan 2020 06:09:48 +0100 (CET) Received: by mail-qv1-f67.google.com with SMTP id z3so2687221qvn.0 for ; Tue, 21 Jan 2020 21:09:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=6ziJdUY4ZzQnItMii0aM9M1XTXegPLFcGgiqWH6Qlqg=; b=dbV3OJexHaHZsmDa9beLzv8fmJYjFhEi3FwJmwo/w9Frj4aTYNeAU6vcKpFVmhnbAz XYcJaseW6TfKiW0S2B0GT4laZYQx+Xc8lQzQ1h/yPfi/P5pXQpzlRjNgM2KedVrfHybQ mf6amTDtLtrusfet2hnwB44epkphutWk2Ek6+ZL4Q9SRBbnnMGXRW1k9sjUw2xOm3ZGz 88cbHqchpIG5FzJJRy52ysQ7JH8XCzK+uByJEZ7pfilmUwHLwzc/lCc+0ZMoB5LeLVGB qaW2X4k9JNxl5utkIIpNfwdR3iXmS23RUk64d3/XDpVTw3VijsLyZsfV5bkcY5eTAYZV cMyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=6ziJdUY4ZzQnItMii0aM9M1XTXegPLFcGgiqWH6Qlqg=; b=hJ1WCK9f4wtZBIBN99bP02YsdJ2/MN0NNlVeeQK7mAFTtlm2nUMKAPhKR4Pir7NvYk kVvVdzArbFtxqiba6tNcX9QzmJHSPT0d6u2CU72BWr+vF0aHsV7cB8Ntegt3RH6pnoZq 6i77MRhiiH59IxMxZZeB34AjP8DiwHK9NZ/pULrvSK0HNP2/p6m9PFHSk8MXfCygGODa pXCHAZx4pI+wS/6LABIKeezp+zTwl2Q6URMhO/UR1HIjp3tLky8ti74mZN2kUlhYWgAw r0yPlj64voPDI/DT9WiQh2HMBnp9OJ6Dc9zi4GbdhhhyRtQcsNFO4dfa4o+qs2h8GvG3 SSfg== X-Gm-Message-State: APjAAAXCQe0S34i9JpWCmoYAuW0q56198mbz7gi2SNbmW/DhZmaoYsCS DmYDV2+Ct+8vAxw1DBlkeBE= X-Google-Smtp-Source: APXvYqxZL3Q4HiIQhwAlhu3/gMYUMdzfcAGkmMiZeIjS2tuNhEyx29DW0plcKKLale0F8tgIZl6Vbg== X-Received: by 2002:a0c:aa8a:: with SMTP id f10mr8775682qvb.200.1579669787403; Tue, 21 Jan 2020 21:09:47 -0800 (PST) Received: from localhost.localdomain (pool-71-182-232-12.pitbpa.fios.verizon.net. [71.182.232.12]) by smtp.gmail.com with ESMTPSA id v24sm20266955qtq.14.2020.01.21.21.09.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jan 2020 21:09:46 -0800 (PST) From: Dominic Chen To: Wenzhuo Lu Cc: dev@dpdk.org, Dominic Chen Date: Wed, 22 Jan 2020 00:09:38 -0500 Message-Id: <20200122050940.4695-1-d.c.ddcc@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [dpdk-dev] [PATCH 1/2] net/e1000: add support for NIC loopback 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" Check the lpbk_mode field of struct rte_eth_conf to determine if loopback should be enabled. Works with 82540EM and 82574L in QEMU 4.0.0. Signed-off-by: Dominic Chen --- drivers/net/e1000/base/e1000_api.c | 42 +++++++ drivers/net/e1000/base/e1000_api.h | 1 + drivers/net/e1000/base/e1000_hw.h | 1 + drivers/net/e1000/base/e1000_ich8lan.h | 4 + drivers/net/e1000/base/e1000_phy.c | 158 +++++++++++++++++++++++++ drivers/net/e1000/base/e1000_phy.h | 3 + drivers/net/e1000/em_ethdev.c | 5 + drivers/net/e1000/em_rxtx.c | 9 +- drivers/net/e1000/igb_ethdev.c | 5 + drivers/net/e1000/igb_rxtx.c | 9 +- 10 files changed, 233 insertions(+), 4 deletions(-) diff --git a/drivers/net/e1000/base/e1000_api.c b/drivers/net/e1000/base/e1000_api.c index 718952801..f7f883889 100644 --- a/drivers/net/e1000/base/e1000_api.c +++ b/drivers/net/e1000/base/e1000_api.c @@ -671,6 +671,48 @@ s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex) return -E1000_ERR_CONFIG; } +/** + * e1000_set_loopback - Set adapter in loopback mode + * @hw: pointer to the HW structure + * + * This sets the MAC and/or PHY into loopback mode. + **/ +s32 e1000_set_loopback(struct e1000_hw *hw) +{ + s32 ret_val = 0; + + if (hw->phy.media_type == e1000_media_type_fiber || + hw->phy.media_type == e1000_media_type_internal_serdes) { + switch (hw->mac.type) { + case e1000_80003es2lan: + case e1000_82571: + case e1000_82572: + /* not implemented */ + return -E1000_ERR_PHY_TYPE; + + case e1000_82545: + case e1000_82546: + case e1000_82545_rev_3: + case e1000_82546_rev_3: + ret_val = e1000_set_phy_loopback(hw); + if (ret_val) + return ret_val; + break; + + default: + /* use transceiver loopback */ + break; + } + } else if (hw->phy.media_type == e1000_media_type_copper) { + ret_val = e1000_set_phy_loopback(hw); + if (ret_val) + return ret_val; + } + + hw->mac.loopback = true; + return E1000_SUCCESS; +} + /** * e1000_setup_led - Configures SW controllable LED * @hw: pointer to the HW structure diff --git a/drivers/net/e1000/base/e1000_api.h b/drivers/net/e1000/base/e1000_api.h index 3054d5b9d..a1b0653d3 100644 --- a/drivers/net/e1000/base/e1000_api.h +++ b/drivers/net/e1000/base/e1000_api.h @@ -37,6 +37,7 @@ s32 e1000_reset_hw(struct e1000_hw *hw); s32 e1000_init_hw(struct e1000_hw *hw); s32 e1000_setup_link(struct e1000_hw *hw); s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex); +s32 e1000_set_loopback(struct e1000_hw *hw); s32 e1000_disable_pcie_master(struct e1000_hw *hw); void e1000_config_collision_dist(struct e1000_hw *hw); int e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index); diff --git a/drivers/net/e1000/base/e1000_hw.h b/drivers/net/e1000/base/e1000_hw.h index 9793b724e..820b5cf88 100644 --- a/drivers/net/e1000/base/e1000_hw.h +++ b/drivers/net/e1000/base/e1000_hw.h @@ -789,6 +789,7 @@ struct e1000_mac_info { enum e1000_serdes_link_state serdes_link_state; bool serdes_has_link; bool tx_pkt_filtering; + bool loopback; }; struct e1000_phy_info { diff --git a/drivers/net/e1000/base/e1000_ich8lan.h b/drivers/net/e1000/base/e1000_ich8lan.h index 699a92c4b..d3f955c22 100644 --- a/drivers/net/e1000/base/e1000_ich8lan.h +++ b/drivers/net/e1000/base/e1000_ich8lan.h @@ -144,6 +144,7 @@ #define HV_LED_CONFIG PHY_REG(768, 30) /* LED Configuration */ #define HV_MUX_DATA_CTRL PHY_REG(776, 16) #define HV_MUX_DATA_CTRL_GEN_TO_MAC 0x0400 +#define HV_MUX_DATA_CTRL_SET_LINK_UP 0x0040 /* Set Link Up */ #define HV_MUX_DATA_CTRL_FORCE_SPEED 0x0004 #define HV_STATS_PAGE 778 /* Half-duplex collision counts */ @@ -213,6 +214,8 @@ /* KMRN Mode Control */ #define HV_KMRN_MODE_CTRL PHY_REG(769, 16) +#define HV_KMRN_FORCE_FD 0x000C /* Force Full-Duplex */ +#define HV_KMRN_FORCE_LINK 0x0040 /* Force Link */ #define HV_KMRN_MDIO_SLOW 0x0400 /* KMRN FIFO Control and Status */ @@ -248,6 +251,7 @@ /* 82579 DFT Control */ #define I82579_DFT_CTRL PHY_REG(769, 20) #define I82579_DFT_CTRL_GATE_PHY_RESET 0x0040 /* Gate PHY Reset on MAC Reset */ +#define I82579_DFT_CTRL_EARLY_LINK_ENABLE 0x0400 /* Early Link Enable */ /* Extended Management Interface (EMI) Registers */ #define I82579_EMI_ADDR 0x10 diff --git a/drivers/net/e1000/base/e1000_phy.c b/drivers/net/e1000/base/e1000_phy.c index 956c06747..0ec4bef5d 100644 --- a/drivers/net/e1000/base/e1000_phy.c +++ b/drivers/net/e1000/base/e1000_phy.c @@ -2784,6 +2784,162 @@ s32 e1000_get_phy_info_ife(struct e1000_hw *hw) return E1000_SUCCESS; } +/** + * e1000_set_phy_loopback - set M88 PHY in loopback mode + * @hw: pointer to the HW structure + **/ +s32 e1000_set_phy_loopback_m88(struct e1000_hw *hw) +{ + s32 ret_val; + + if (hw->phy.media_type == e1000_media_type_copper) { + uint32_t ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_ILOS); + } + + /* Disable the transceiver */ + ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x001F); + if (ret_val) + return ret_val; + ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x8FFC); + if (ret_val) + return ret_val; + ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x001A); + if (ret_val) + return ret_val; + ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x8FF0); + if (ret_val) + return ret_val; + + return E1000_SUCCESS; +} + +/** + * e1000_set_phy_loopback - set BM PHY in loopback mode + * @hw: pointer to the HW structure + **/ +s32 e1000_set_phy_loopback_bm(struct e1000_hw *hw) +{ + u16 data; + s32 ret_val; + + /* Force full duplex */ + ret_val = hw->phy.ops.read_reg(hw, HV_KMRN_MODE_CTRL, &data); + if (ret_val) + return ret_val; + data |= HV_KMRN_FORCE_FD; + ret_val = hw->phy.ops.write_reg(hw, HV_KMRN_MODE_CTRL, data); + if (ret_val) + return ret_val; + + /* Set link up */ + ret_val = hw->phy.ops.read_reg(hw, HV_MUX_DATA_CTRL, &data); + if (ret_val) + return ret_val; + data |= HV_MUX_DATA_CTRL_SET_LINK_UP; + ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL, data); + if (ret_val) + return ret_val; + + /* Force link */ + ret_val = hw->phy.ops.read_reg(hw, HV_KMRN_MODE_CTRL, &data); + if (ret_val) + return ret_val; + data |= HV_KMRN_FORCE_LINK; + ret_val = hw->phy.ops.write_reg(hw, HV_KMRN_MODE_CTRL, data); + if (ret_val) + return ret_val; + + /* Set early link enable */ + ret_val = hw->phy.ops.read_reg(hw, I82579_DFT_CTRL, &data); + if (ret_val) + return ret_val; + data |= I82579_DFT_CTRL_EARLY_LINK_ENABLE; + ret_val = hw->phy.ops.write_reg(hw, I82579_DFT_CTRL, data); + if (ret_val) + return ret_val; + + return E1000_SUCCESS; +} + +/** + * e1000_set_phy_loopback - set PHY in loopback mode + * @hw: pointer to the HW structure + **/ +s32 e1000_set_phy_loopback(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_ctrl; + + DEBUGFUNC("e1000_set_phy_loopback"); + + if (phy->type != e1000_phy_none && phy->type != e1000_phy_ife && + phy->type != e1000_phy_m88 && phy->type != e1000_phy_bm) { + /* not implemented */ + return -E1000_ERR_PHY_TYPE; + } + + /* must be forced 1000 full-duplex */ + if (mac->autoneg || mac->forced_speed_duplex != ADVERTISE_1000_FULL || + phy->autoneg_advertised != ADVERTISE_1000_FULL) + return -E1000_ERR_CONFIG; + + /* set the PHY basic mode control register */ + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + return ret_val; + + phy_ctrl |= MII_CR_LOOPBACK; + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + return ret_val; + + /* put the PHY in loopback */ + switch (mac->type) { + case e1000_82543: + if (phy->media_type == e1000_media_type_copper) + ret_val = e1000_set_phy_loopback_m88(hw); + break; + + case e1000_82540: + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82544: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + case e1000_82547: + case e1000_82547_rev_2: + /* certain M88 registers are not implemented in QEMU. for now, + * try to set them but don't check the return value. + */ + e1000_set_phy_loopback_m88(hw); + break; + + case e1000_ich8lan: + case e1000_82574: + case e1000_82583: + /* datasheet doesn't mention more than 7 pages and they aren't + * implemented in QEMU, but they appear to be used. for now, try + * to set them but don't check the return value. + */ + e1000_set_phy_loopback_bm(hw); + break; + + case e1000_pch_spt: + ret_val = e1000_set_phy_loopback_bm(hw); + break; + + default: + break; + } + + return ret_val; +} + /** * e1000_phy_sw_reset_generic - PHY software reset * @hw: pointer to the HW structure @@ -2805,6 +2961,8 @@ s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw) if (ret_val) return ret_val; + if (!hw->mac.loopback) + phy_ctrl &= ~MII_CR_LOOPBACK; phy_ctrl |= MII_CR_RESET; ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_ctrl); if (ret_val) diff --git a/drivers/net/e1000/base/e1000_phy.h b/drivers/net/e1000/base/e1000_phy.h index 2c71e64c8..967e1e387 100644 --- a/drivers/net/e1000/base/e1000_phy.h +++ b/drivers/net/e1000/base/e1000_phy.h @@ -36,6 +36,9 @@ s32 e1000_get_phy_id(struct e1000_hw *hw); s32 e1000_get_phy_info_igp(struct e1000_hw *hw); s32 e1000_get_phy_info_m88(struct e1000_hw *hw); s32 e1000_get_phy_info_ife(struct e1000_hw *hw); +s32 e1000_set_phy_loopback_m88(struct e1000_hw *hw); +s32 e1000_set_phy_loopback_bm(struct e1000_hw *hw); +s32 e1000_set_phy_loopback(struct e1000_hw *hw); s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw); void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl); s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw); diff --git a/drivers/net/e1000/em_ethdev.c b/drivers/net/e1000/em_ethdev.c index 080cbe2df..c1ffa862c 100644 --- a/drivers/net/e1000/em_ethdev.c +++ b/drivers/net/e1000/em_ethdev.c @@ -666,6 +666,11 @@ eth_em_start(struct rte_eth_dev *dev) } } + /* Set up loopback */ + if (dev->data->dev_conf.lpbk_mode) + if (e1000_set_loopback(hw)) + goto error_invalid_config; + e1000_setup_link(hw); if (rte_intr_allow_others(intr_handle)) { diff --git a/drivers/net/e1000/em_rxtx.c b/drivers/net/e1000/em_rxtx.c index 49c53712a..0bfe6b1e3 100644 --- a/drivers/net/e1000/em_rxtx.c +++ b/drivers/net/e1000/em_rxtx.c @@ -1892,10 +1892,15 @@ eth_em_rx_init(struct rte_eth_dev *dev) rctl |= E1000_RCTL_SECRC; /* Strip Ethernet CRC. */ rctl &= ~(3 << E1000_RCTL_MO_SHIFT); - rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO | - E1000_RCTL_RDMTS_HALF | + rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_RDMTS_HALF | (hw->mac.mc_filter_type << E1000_RCTL_MO_SHIFT); + /* Set up transceiver loopback */ + if (hw->mac.loopback) + rctl |= E1000_RCTL_LBM_TCVR; + else + rctl |= E1000_RCTL_LBM_NO; + /* Make sure VLAN Filters are off. */ rctl &= ~E1000_RCTL_VFE; /* Don't store bad packets. */ diff --git a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c index 647d5504f..c738fb127 100644 --- a/drivers/net/e1000/igb_ethdev.c +++ b/drivers/net/e1000/igb_ethdev.c @@ -1400,6 +1400,11 @@ eth_igb_start(struct rte_eth_dev *dev) } } + /* Set up loopback */ + if (dev->data->dev_conf.lpbk_mode) + if (e1000_set_loopback(hw)) + goto error_invalid_config; + e1000_setup_link(hw); if (rte_intr_allow_others(intr_handle)) { diff --git a/drivers/net/e1000/igb_rxtx.c b/drivers/net/e1000/igb_rxtx.c index 684fa4ad8..5f206fa02 100644 --- a/drivers/net/e1000/igb_rxtx.c +++ b/drivers/net/e1000/igb_rxtx.c @@ -2548,10 +2548,15 @@ eth_igb_rx_init(struct rte_eth_dev *dev) } rctl &= ~(3 << E1000_RCTL_MO_SHIFT); - rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO | - E1000_RCTL_RDMTS_HALF | + rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_RDMTS_HALF | (hw->mac.mc_filter_type << E1000_RCTL_MO_SHIFT); + /* Set up transceiver loopback */ + if (hw->mac.loopback) + rctl |= E1000_RCTL_LBM_TCVR; + else + rctl |= E1000_RCTL_LBM_NO; + /* Make sure VLAN Filters are off. */ if (dev->data->dev_conf.rxmode.mq_mode != ETH_MQ_RX_VMDQ_ONLY) rctl &= ~E1000_RCTL_VFE;