@@ -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
@@ -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);
@@ -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 {
@@ -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
@@ -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)
@@ -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);
@@ -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)) {
@@ -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. */
@@ -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)) {
@@ -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;