|
@@ -37,6 +37,9 @@ static s32 e1000_wait_autoneg(struct e1000_hw *hw);
|
|
|
static u32 e1000_get_phy_addr_for_bm_page(u32 page, u32 reg);
|
|
|
static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
|
|
|
u16 *data, bool read);
|
|
|
+static u32 e1000_get_phy_addr_for_hv_page(u32 page);
|
|
|
+static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
|
|
|
+ u16 *data, bool read);
|
|
|
|
|
|
/* Cable length tables */
|
|
|
static const u16 e1000_m88_cable_length_table[] =
|
|
@@ -54,6 +57,55 @@ static const u16 e1000_igp_2_cable_length_table[] =
|
|
|
#define IGP02E1000_CABLE_LENGTH_TABLE_SIZE \
|
|
|
ARRAY_SIZE(e1000_igp_2_cable_length_table)
|
|
|
|
|
|
+#define BM_PHY_REG_PAGE(offset) \
|
|
|
+ ((u16)(((offset) >> PHY_PAGE_SHIFT) & 0xFFFF))
|
|
|
+#define BM_PHY_REG_NUM(offset) \
|
|
|
+ ((u16)(((offset) & MAX_PHY_REG_ADDRESS) |\
|
|
|
+ (((offset) >> (PHY_UPPER_SHIFT - PHY_PAGE_SHIFT)) &\
|
|
|
+ ~MAX_PHY_REG_ADDRESS)))
|
|
|
+
|
|
|
+#define HV_INTC_FC_PAGE_START 768
|
|
|
+#define I82578_ADDR_REG 29
|
|
|
+#define I82577_ADDR_REG 16
|
|
|
+#define I82577_CFG_REG 22
|
|
|
+#define I82577_CFG_ASSERT_CRS_ON_TX (1 << 15)
|
|
|
+#define I82577_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift 100/10 */
|
|
|
+#define I82577_CTRL_REG 23
|
|
|
+#define I82577_CTRL_DOWNSHIFT_MASK (7 << 10)
|
|
|
+
|
|
|
+/* 82577 specific PHY registers */
|
|
|
+#define I82577_PHY_CTRL_2 18
|
|
|
+#define I82577_PHY_STATUS_2 26
|
|
|
+#define I82577_PHY_DIAG_STATUS 31
|
|
|
+
|
|
|
+/* I82577 PHY Status 2 */
|
|
|
+#define I82577_PHY_STATUS2_REV_POLARITY 0x0400
|
|
|
+#define I82577_PHY_STATUS2_MDIX 0x0800
|
|
|
+#define I82577_PHY_STATUS2_SPEED_MASK 0x0300
|
|
|
+#define I82577_PHY_STATUS2_SPEED_1000MBPS 0x0200
|
|
|
+
|
|
|
+/* I82577 PHY Control 2 */
|
|
|
+#define I82577_PHY_CTRL2_AUTO_MDIX 0x0400
|
|
|
+#define I82577_PHY_CTRL2_FORCE_MDI_MDIX 0x0200
|
|
|
+
|
|
|
+/* I82577 PHY Diagnostics Status */
|
|
|
+#define I82577_DSTATUS_CABLE_LENGTH 0x03FC
|
|
|
+#define I82577_DSTATUS_CABLE_LENGTH_SHIFT 2
|
|
|
+
|
|
|
+/* BM PHY Copper Specific Control 1 */
|
|
|
+#define BM_CS_CTRL1 16
|
|
|
+
|
|
|
+/* BM PHY Copper Specific Status */
|
|
|
+#define BM_CS_STATUS 17
|
|
|
+#define BM_CS_STATUS_LINK_UP 0x0400
|
|
|
+#define BM_CS_STATUS_RESOLVED 0x0800
|
|
|
+#define BM_CS_STATUS_SPEED_MASK 0xC000
|
|
|
+#define BM_CS_STATUS_SPEED_1000 0x8000
|
|
|
+
|
|
|
+#define HV_MUX_DATA_CTRL PHY_REG(776, 16)
|
|
|
+#define HV_MUX_DATA_CTRL_GEN_TO_MAC 0x0400
|
|
|
+#define HV_MUX_DATA_CTRL_FORCE_SPEED 0x0004
|
|
|
+
|
|
|
/**
|
|
|
* e1000e_check_reset_block_generic - Check if PHY reset is blocked
|
|
|
* @hw: pointer to the HW structure
|
|
@@ -82,23 +134,48 @@ s32 e1000e_check_reset_block_generic(struct e1000_hw *hw)
|
|
|
s32 e1000e_get_phy_id(struct e1000_hw *hw)
|
|
|
{
|
|
|
struct e1000_phy_info *phy = &hw->phy;
|
|
|
- s32 ret_val;
|
|
|
+ s32 ret_val = 0;
|
|
|
u16 phy_id;
|
|
|
+ u16 retry_count = 0;
|
|
|
|
|
|
- ret_val = e1e_rphy(hw, PHY_ID1, &phy_id);
|
|
|
- if (ret_val)
|
|
|
- return ret_val;
|
|
|
+ if (!(phy->ops.read_phy_reg))
|
|
|
+ goto out;
|
|
|
|
|
|
- phy->id = (u32)(phy_id << 16);
|
|
|
- udelay(20);
|
|
|
- ret_val = e1e_rphy(hw, PHY_ID2, &phy_id);
|
|
|
- if (ret_val)
|
|
|
- return ret_val;
|
|
|
+ while (retry_count < 2) {
|
|
|
+ ret_val = e1e_rphy(hw, PHY_ID1, &phy_id);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
|
|
|
- phy->id |= (u32)(phy_id & PHY_REVISION_MASK);
|
|
|
- phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK);
|
|
|
+ phy->id = (u32)(phy_id << 16);
|
|
|
+ udelay(20);
|
|
|
+ ret_val = e1e_rphy(hw, PHY_ID2, &phy_id);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
|
|
|
- return 0;
|
|
|
+ phy->id |= (u32)(phy_id & PHY_REVISION_MASK);
|
|
|
+ phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK);
|
|
|
+
|
|
|
+ if (phy->id != 0 && phy->id != PHY_REVISION_MASK)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the PHY ID is still unknown, we may have an 82577i
|
|
|
+ * without link. We will try again after setting Slow
|
|
|
+ * MDIC mode. No harm in trying again in this case since
|
|
|
+ * the PHY ID is unknown at this point anyway
|
|
|
+ */
|
|
|
+ ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ retry_count++;
|
|
|
+ }
|
|
|
+out:
|
|
|
+ /* Revert to MDIO fast mode, if applicable */
|
|
|
+ if (retry_count)
|
|
|
+ ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
|
|
|
+
|
|
|
+ return ret_val;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -409,6 +486,43 @@ s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data)
|
|
|
return ret_val;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ *
|
|
|
+ * Sets up Carrier-sense on Transmit and downshift values.
|
|
|
+ **/
|
|
|
+s32 e1000_copper_link_setup_82577(struct e1000_hw *hw)
|
|
|
+{
|
|
|
+ struct e1000_phy_info *phy = &hw->phy;
|
|
|
+ s32 ret_val;
|
|
|
+ u16 phy_data;
|
|
|
+
|
|
|
+ /* Enable CRS on TX. This must be set for half-duplex operation. */
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, I82577_CFG_REG, &phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ phy_data |= I82577_CFG_ASSERT_CRS_ON_TX;
|
|
|
+
|
|
|
+ /* Enable downshift */
|
|
|
+ phy_data |= I82577_CFG_ENABLE_DOWNSHIFT;
|
|
|
+
|
|
|
+ ret_val = phy->ops.write_phy_reg(hw, I82577_CFG_REG, phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Set number of link attempts before downshift */
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, I82577_CTRL_REG, &phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+ phy_data &= ~I82577_CTRL_DOWNSHIFT_MASK;
|
|
|
+ ret_val = phy->ops.write_phy_reg(hw, I82577_CTRL_REG, phy_data);
|
|
|
+
|
|
|
+out:
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* e1000e_copper_link_setup_m88 - Setup m88 PHY's for copper link
|
|
|
* @hw: pointer to the HW structure
|
|
@@ -427,8 +541,8 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw)
|
|
|
if (ret_val)
|
|
|
return ret_val;
|
|
|
|
|
|
- /* For newer PHYs this bit is downshift enable */
|
|
|
- if (phy->type == e1000_phy_m88)
|
|
|
+ /* For BM PHY this bit is downshift enable */
|
|
|
+ if (phy->type != e1000_phy_bm)
|
|
|
phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
|
|
|
|
|
|
/*
|
|
@@ -520,10 +634,27 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw)
|
|
|
|
|
|
/* Commit the changes. */
|
|
|
ret_val = e1000e_commit_phy(hw);
|
|
|
- if (ret_val)
|
|
|
+ if (ret_val) {
|
|
|
hw_dbg(hw, "Error committing the PHY changes\n");
|
|
|
+ return ret_val;
|
|
|
+ }
|
|
|
|
|
|
- return ret_val;
|
|
|
+ if (phy->type == e1000_phy_82578) {
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
|
|
|
+ &phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ return ret_val;
|
|
|
+
|
|
|
+ /* 82578 PHY - set the downshift count to 1x. */
|
|
|
+ phy_data |= I82578_EPSCR_DOWNSHIFT_ENABLE;
|
|
|
+ phy_data &= ~I82578_EPSCR_DOWNSHIFT_COUNTER_MASK;
|
|
|
+ ret_val = phy->ops.write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
|
|
|
+ phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ return ret_val;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1251,6 +1382,8 @@ s32 e1000e_check_downshift(struct e1000_hw *hw)
|
|
|
switch (phy->type) {
|
|
|
case e1000_phy_m88:
|
|
|
case e1000_phy_gg82563:
|
|
|
+ case e1000_phy_82578:
|
|
|
+ case e1000_phy_82577:
|
|
|
offset = M88E1000_PHY_SPEC_STATUS;
|
|
|
mask = M88E1000_PSSR_DOWNSHIFT;
|
|
|
break;
|
|
@@ -1886,6 +2019,12 @@ enum e1000_phy_type e1000e_get_phy_type_from_id(u32 phy_id)
|
|
|
case BME1000_E_PHY_ID_R2:
|
|
|
phy_type = e1000_phy_bm;
|
|
|
break;
|
|
|
+ case I82578_E_PHY_ID:
|
|
|
+ phy_type = e1000_phy_82578;
|
|
|
+ break;
|
|
|
+ case I82577_E_PHY_ID:
|
|
|
+ phy_type = e1000_phy_82577;
|
|
|
+ break;
|
|
|
default:
|
|
|
phy_type = e1000_phy_unknown;
|
|
|
break;
|
|
@@ -2181,11 +2320,16 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
|
|
|
u16 *data, bool read)
|
|
|
{
|
|
|
s32 ret_val;
|
|
|
- u16 reg = ((u16)offset) & PHY_REG_MASK;
|
|
|
+ u16 reg = BM_PHY_REG_NUM(offset);
|
|
|
u16 phy_reg = 0;
|
|
|
u8 phy_acquired = 1;
|
|
|
|
|
|
|
|
|
+ /* Gig must be disabled for MDIO accesses to page 800 */
|
|
|
+ if ((hw->mac.type == e1000_pchlan) &&
|
|
|
+ (!(er32(PHY_CTRL) & E1000_PHY_CTRL_GBE_DISABLE)))
|
|
|
+ hw_dbg(hw, "Attempting to access page 800 while gig enabled\n");
|
|
|
+
|
|
|
ret_val = hw->phy.ops.acquire_phy(hw);
|
|
|
if (ret_val) {
|
|
|
phy_acquired = 0;
|
|
@@ -2289,3 +2433,524 @@ static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active)
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow)
|
|
|
+{
|
|
|
+ s32 ret_val = 0;
|
|
|
+ u16 data = 0;
|
|
|
+
|
|
|
+ ret_val = hw->phy.ops.acquire_phy(hw);
|
|
|
+ if (ret_val)
|
|
|
+ return ret_val;
|
|
|
+
|
|
|
+ /* Set MDIO mode - page 769, register 16: 0x2580==slow, 0x2180==fast */
|
|
|
+ hw->phy.addr = 1;
|
|
|
+ ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
|
|
|
+ (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT));
|
|
|
+ if (ret_val) {
|
|
|
+ hw->phy.ops.release_phy(hw);
|
|
|
+ return ret_val;
|
|
|
+ }
|
|
|
+ ret_val = e1000e_write_phy_reg_mdic(hw, BM_CS_CTRL1,
|
|
|
+ (0x2180 | (slow << 10)));
|
|
|
+
|
|
|
+ /* dummy read when reverting to fast mode - throw away result */
|
|
|
+ if (!slow)
|
|
|
+ e1000e_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data);
|
|
|
+
|
|
|
+ hw->phy.ops.release_phy(hw);
|
|
|
+
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_read_phy_reg_hv - Read HV PHY register
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ * @offset: register offset to be read
|
|
|
+ * @data: pointer to the read data
|
|
|
+ *
|
|
|
+ * Acquires semaphore, if necessary, then reads the PHY register at offset
|
|
|
+ * and storing the retrieved information in data. Release any acquired
|
|
|
+ * semaphore before exiting.
|
|
|
+ **/
|
|
|
+s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
|
|
|
+{
|
|
|
+ s32 ret_val;
|
|
|
+ u16 page = BM_PHY_REG_PAGE(offset);
|
|
|
+ u16 reg = BM_PHY_REG_NUM(offset);
|
|
|
+ bool in_slow_mode = false;
|
|
|
+
|
|
|
+ /* Workaround failure in MDIO access while cable is disconnected */
|
|
|
+ if ((hw->phy.type == e1000_phy_82577) &&
|
|
|
+ !(er32(STATUS) & E1000_STATUS_LU)) {
|
|
|
+ ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ in_slow_mode = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Page 800 works differently than the rest so it has its own func */
|
|
|
+ if (page == BM_WUC_PAGE) {
|
|
|
+ ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
|
|
|
+ data, true);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (page > 0 && page < HV_INTC_FC_PAGE_START) {
|
|
|
+ ret_val = e1000_access_phy_debug_regs_hv(hw, offset,
|
|
|
+ data, true);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret_val = hw->phy.ops.acquire_phy(hw);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
|
|
|
+
|
|
|
+ if (page == HV_INTC_FC_PAGE_START)
|
|
|
+ page = 0;
|
|
|
+
|
|
|
+ if (reg > MAX_PHY_MULTI_PAGE_REG) {
|
|
|
+ if ((hw->phy.type != e1000_phy_82578) ||
|
|
|
+ ((reg != I82578_ADDR_REG) &&
|
|
|
+ (reg != I82578_ADDR_REG + 1))) {
|
|
|
+ u32 phy_addr = hw->phy.addr;
|
|
|
+
|
|
|
+ hw->phy.addr = 1;
|
|
|
+
|
|
|
+ /* Page is shifted left, PHY expects (page x 32) */
|
|
|
+ ret_val = e1000e_write_phy_reg_mdic(hw,
|
|
|
+ IGP01E1000_PHY_PAGE_SELECT,
|
|
|
+ (page << IGP_PAGE_SHIFT));
|
|
|
+ if (ret_val) {
|
|
|
+ hw->phy.ops.release_phy(hw);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ hw->phy.addr = phy_addr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
|
|
|
+ data);
|
|
|
+ hw->phy.ops.release_phy(hw);
|
|
|
+
|
|
|
+out:
|
|
|
+ /* Revert to MDIO fast mode, if applicable */
|
|
|
+ if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
|
|
|
+ ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
|
|
|
+
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_write_phy_reg_hv - Write HV PHY register
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ * @offset: register offset to write to
|
|
|
+ * @data: data to write at register offset
|
|
|
+ *
|
|
|
+ * Acquires semaphore, if necessary, then writes the data to PHY register
|
|
|
+ * at the offset. Release any acquired semaphores before exiting.
|
|
|
+ **/
|
|
|
+s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
|
|
|
+{
|
|
|
+ s32 ret_val;
|
|
|
+ u16 page = BM_PHY_REG_PAGE(offset);
|
|
|
+ u16 reg = BM_PHY_REG_NUM(offset);
|
|
|
+ bool in_slow_mode = false;
|
|
|
+
|
|
|
+ /* Workaround failure in MDIO access while cable is disconnected */
|
|
|
+ if ((hw->phy.type == e1000_phy_82577) &&
|
|
|
+ !(er32(STATUS) & E1000_STATUS_LU)) {
|
|
|
+ ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ in_slow_mode = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Page 800 works differently than the rest so it has its own func */
|
|
|
+ if (page == BM_WUC_PAGE) {
|
|
|
+ ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
|
|
|
+ &data, false);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (page > 0 && page < HV_INTC_FC_PAGE_START) {
|
|
|
+ ret_val = e1000_access_phy_debug_regs_hv(hw, offset,
|
|
|
+ &data, false);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret_val = hw->phy.ops.acquire_phy(hw);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
|
|
|
+
|
|
|
+ if (page == HV_INTC_FC_PAGE_START)
|
|
|
+ page = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Workaround MDIO accesses being disabled after entering IEEE Power
|
|
|
+ * Down (whenever bit 11 of the PHY Control register is set)
|
|
|
+ */
|
|
|
+ if ((hw->phy.type == e1000_phy_82578) &&
|
|
|
+ (hw->phy.revision >= 1) &&
|
|
|
+ (hw->phy.addr == 2) &&
|
|
|
+ ((MAX_PHY_REG_ADDRESS & reg) == 0) &&
|
|
|
+ (data & (1 << 11))) {
|
|
|
+ u16 data2 = 0x7EFF;
|
|
|
+ hw->phy.ops.release_phy(hw);
|
|
|
+ ret_val = e1000_access_phy_debug_regs_hv(hw, (1 << 6) | 0x3,
|
|
|
+ &data2, false);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret_val = hw->phy.ops.acquire_phy(hw);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (reg > MAX_PHY_MULTI_PAGE_REG) {
|
|
|
+ if ((hw->phy.type != e1000_phy_82578) ||
|
|
|
+ ((reg != I82578_ADDR_REG) &&
|
|
|
+ (reg != I82578_ADDR_REG + 1))) {
|
|
|
+ u32 phy_addr = hw->phy.addr;
|
|
|
+
|
|
|
+ hw->phy.addr = 1;
|
|
|
+
|
|
|
+ /* Page is shifted left, PHY expects (page x 32) */
|
|
|
+ ret_val = e1000e_write_phy_reg_mdic(hw,
|
|
|
+ IGP01E1000_PHY_PAGE_SELECT,
|
|
|
+ (page << IGP_PAGE_SHIFT));
|
|
|
+ if (ret_val) {
|
|
|
+ hw->phy.ops.release_phy(hw);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ hw->phy.addr = phy_addr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
|
|
|
+ data);
|
|
|
+ hw->phy.ops.release_phy(hw);
|
|
|
+
|
|
|
+out:
|
|
|
+ /* Revert to MDIO fast mode, if applicable */
|
|
|
+ if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
|
|
|
+ ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
|
|
|
+
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_get_phy_addr_for_hv_page - Get PHY adrress based on page
|
|
|
+ * @page: page to be accessed
|
|
|
+ **/
|
|
|
+static u32 e1000_get_phy_addr_for_hv_page(u32 page)
|
|
|
+{
|
|
|
+ u32 phy_addr = 2;
|
|
|
+
|
|
|
+ if (page >= HV_INTC_FC_PAGE_START)
|
|
|
+ phy_addr = 1;
|
|
|
+
|
|
|
+ return phy_addr;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_access_phy_debug_regs_hv - Read HV PHY vendor specific high registers
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ * @offset: register offset to be read or written
|
|
|
+ * @data: pointer to the data to be read or written
|
|
|
+ * @read: determines if operation is read or written
|
|
|
+ *
|
|
|
+ * Acquires semaphore, if necessary, then reads the PHY register at offset
|
|
|
+ * and storing the retreived information in data. Release any acquired
|
|
|
+ * semaphores before exiting. Note that the procedure to read these regs
|
|
|
+ * uses the address port and data port to read/write.
|
|
|
+ **/
|
|
|
+static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
|
|
|
+ u16 *data, bool read)
|
|
|
+{
|
|
|
+ s32 ret_val;
|
|
|
+ u32 addr_reg = 0;
|
|
|
+ u32 data_reg = 0;
|
|
|
+ u8 phy_acquired = 1;
|
|
|
+
|
|
|
+ /* This takes care of the difference with desktop vs mobile phy */
|
|
|
+ addr_reg = (hw->phy.type == e1000_phy_82578) ?
|
|
|
+ I82578_ADDR_REG : I82577_ADDR_REG;
|
|
|
+ data_reg = addr_reg + 1;
|
|
|
+
|
|
|
+ ret_val = hw->phy.ops.acquire_phy(hw);
|
|
|
+ if (ret_val) {
|
|
|
+ hw_dbg(hw, "Could not acquire PHY\n");
|
|
|
+ phy_acquired = 0;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* All operations in this function are phy address 2 */
|
|
|
+ hw->phy.addr = 2;
|
|
|
+
|
|
|
+ /* masking with 0x3F to remove the page from offset */
|
|
|
+ ret_val = e1000e_write_phy_reg_mdic(hw, addr_reg, (u16)offset & 0x3F);
|
|
|
+ if (ret_val) {
|
|
|
+ hw_dbg(hw, "Could not write PHY the HV address register\n");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Read or write the data value next */
|
|
|
+ if (read)
|
|
|
+ ret_val = e1000e_read_phy_reg_mdic(hw, data_reg, data);
|
|
|
+ else
|
|
|
+ ret_val = e1000e_write_phy_reg_mdic(hw, data_reg, *data);
|
|
|
+
|
|
|
+ if (ret_val) {
|
|
|
+ hw_dbg(hw, "Could not read data value from HV data register\n");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ if (phy_acquired == 1)
|
|
|
+ hw->phy.ops.release_phy(hw);
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_link_stall_workaround_hv - Si workaround
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ *
|
|
|
+ * This function works around a Si bug where the link partner can get
|
|
|
+ * a link up indication before the PHY does. If small packets are sent
|
|
|
+ * by the link partner they can be placed in the packet buffer without
|
|
|
+ * being properly accounted for by the PHY and will stall preventing
|
|
|
+ * further packets from being received. The workaround is to clear the
|
|
|
+ * packet buffer after the PHY detects link up.
|
|
|
+ **/
|
|
|
+s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw)
|
|
|
+{
|
|
|
+ s32 ret_val = 0;
|
|
|
+ u16 data;
|
|
|
+
|
|
|
+ if (hw->phy.type != e1000_phy_82578)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* check if link is up and at 1Gbps */
|
|
|
+ ret_val = hw->phy.ops.read_phy_reg(hw, BM_CS_STATUS, &data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ data &= BM_CS_STATUS_LINK_UP |
|
|
|
+ BM_CS_STATUS_RESOLVED |
|
|
|
+ BM_CS_STATUS_SPEED_MASK;
|
|
|
+
|
|
|
+ if (data != (BM_CS_STATUS_LINK_UP |
|
|
|
+ BM_CS_STATUS_RESOLVED |
|
|
|
+ BM_CS_STATUS_SPEED_1000))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ mdelay(200);
|
|
|
+
|
|
|
+ /* flush the packets in the fifo buffer */
|
|
|
+ ret_val = hw->phy.ops.write_phy_reg(hw, HV_MUX_DATA_CTRL,
|
|
|
+ HV_MUX_DATA_CTRL_GEN_TO_MAC |
|
|
|
+ HV_MUX_DATA_CTRL_FORCE_SPEED);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret_val = hw->phy.ops.write_phy_reg(hw, HV_MUX_DATA_CTRL,
|
|
|
+ HV_MUX_DATA_CTRL_GEN_TO_MAC);
|
|
|
+
|
|
|
+out:
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_check_polarity_82577 - Checks the polarity.
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ *
|
|
|
+ * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
|
|
|
+ *
|
|
|
+ * Polarity is determined based on the PHY specific status register.
|
|
|
+ **/
|
|
|
+s32 e1000_check_polarity_82577(struct e1000_hw *hw)
|
|
|
+{
|
|
|
+ struct e1000_phy_info *phy = &hw->phy;
|
|
|
+ s32 ret_val;
|
|
|
+ u16 data;
|
|
|
+
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_STATUS_2, &data);
|
|
|
+
|
|
|
+ if (!ret_val)
|
|
|
+ phy->cable_polarity = (data & I82577_PHY_STATUS2_REV_POLARITY)
|
|
|
+ ? e1000_rev_polarity_reversed
|
|
|
+ : e1000_rev_polarity_normal;
|
|
|
+
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_phy_force_speed_duplex_82577 - Force speed/duplex for I82577 PHY
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ *
|
|
|
+ * Calls the PHY setup function to force speed and duplex. Clears the
|
|
|
+ * auto-crossover to force MDI manually. Waits for link and returns
|
|
|
+ * successful if link up is successful, else -E1000_ERR_PHY (-2).
|
|
|
+ **/
|
|
|
+s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw)
|
|
|
+{
|
|
|
+ struct e1000_phy_info *phy = &hw->phy;
|
|
|
+ s32 ret_val;
|
|
|
+ u16 phy_data;
|
|
|
+ bool link;
|
|
|
+
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, PHY_CONTROL, &phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ e1000e_phy_force_speed_duplex_setup(hw, &phy_data);
|
|
|
+
|
|
|
+ ret_val = phy->ops.write_phy_reg(hw, PHY_CONTROL, phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Clear Auto-Crossover to force MDI manually. 82577 requires MDI
|
|
|
+ * forced whenever speed and duplex are forced.
|
|
|
+ */
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_CTRL_2, &phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ phy_data &= ~I82577_PHY_CTRL2_AUTO_MDIX;
|
|
|
+ phy_data &= ~I82577_PHY_CTRL2_FORCE_MDI_MDIX;
|
|
|
+
|
|
|
+ ret_val = phy->ops.write_phy_reg(hw, I82577_PHY_CTRL_2, phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ hw_dbg(hw, "I82577_PHY_CTRL_2: %X\n", phy_data);
|
|
|
+
|
|
|
+ udelay(1);
|
|
|
+
|
|
|
+ if (phy->autoneg_wait_to_complete) {
|
|
|
+ hw_dbg(hw, "Waiting for forced speed/duplex link on 82577 phy\n");
|
|
|
+
|
|
|
+ ret_val = e1000e_phy_has_link_generic(hw,
|
|
|
+ PHY_FORCE_LIMIT,
|
|
|
+ 100000,
|
|
|
+ &link);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (!link)
|
|
|
+ hw_dbg(hw, "Link taking longer than expected.\n");
|
|
|
+
|
|
|
+ /* Try once more */
|
|
|
+ ret_val = e1000e_phy_has_link_generic(hw,
|
|
|
+ PHY_FORCE_LIMIT,
|
|
|
+ 100000,
|
|
|
+ &link);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_get_phy_info_82577 - Retrieve I82577 PHY information
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ *
|
|
|
+ * Read PHY status to determine if link is up. If link is up, then
|
|
|
+ * set/determine 10base-T extended distance and polarity correction. Read
|
|
|
+ * PHY port status to determine MDI/MDIx and speed. Based on the speed,
|
|
|
+ * determine on the cable length, local and remote receiver.
|
|
|
+ **/
|
|
|
+s32 e1000_get_phy_info_82577(struct e1000_hw *hw)
|
|
|
+{
|
|
|
+ struct e1000_phy_info *phy = &hw->phy;
|
|
|
+ s32 ret_val;
|
|
|
+ u16 data;
|
|
|
+ bool link;
|
|
|
+
|
|
|
+ ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (!link) {
|
|
|
+ hw_dbg(hw, "Phy info is only valid if link is up\n");
|
|
|
+ ret_val = -E1000_ERR_CONFIG;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ phy->polarity_correction = true;
|
|
|
+
|
|
|
+ ret_val = e1000_check_polarity_82577(hw);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_STATUS_2, &data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ phy->is_mdix = (data & I82577_PHY_STATUS2_MDIX) ? true : false;
|
|
|
+
|
|
|
+ if ((data & I82577_PHY_STATUS2_SPEED_MASK) ==
|
|
|
+ I82577_PHY_STATUS2_SPEED_1000MBPS) {
|
|
|
+ ret_val = hw->phy.ops.get_cable_length(hw);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, PHY_1000T_STATUS, &data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS)
|
|
|
+ ? e1000_1000t_rx_status_ok
|
|
|
+ : e1000_1000t_rx_status_not_ok;
|
|
|
+
|
|
|
+ phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS)
|
|
|
+ ? e1000_1000t_rx_status_ok
|
|
|
+ : e1000_1000t_rx_status_not_ok;
|
|
|
+ } else {
|
|
|
+ phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
|
|
|
+ phy->local_rx = e1000_1000t_rx_status_undefined;
|
|
|
+ phy->remote_rx = e1000_1000t_rx_status_undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * e1000_get_cable_length_82577 - Determine cable length for 82577 PHY
|
|
|
+ * @hw: pointer to the HW structure
|
|
|
+ *
|
|
|
+ * Reads the diagnostic status register and verifies result is valid before
|
|
|
+ * placing it in the phy_cable_length field.
|
|
|
+ **/
|
|
|
+s32 e1000_get_cable_length_82577(struct e1000_hw *hw)
|
|
|
+{
|
|
|
+ struct e1000_phy_info *phy = &hw->phy;
|
|
|
+ s32 ret_val;
|
|
|
+ u16 phy_data, length;
|
|
|
+
|
|
|
+ ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data);
|
|
|
+ if (ret_val)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ length = (phy_data & I82577_DSTATUS_CABLE_LENGTH) >>
|
|
|
+ I82577_DSTATUS_CABLE_LENGTH_SHIFT;
|
|
|
+
|
|
|
+ if (length == E1000_CABLE_LENGTH_UNDEFINED)
|
|
|
+ ret_val = E1000_ERR_PHY;
|
|
|
+
|
|
|
+ phy->cable_length = length;
|
|
|
+
|
|
|
+out:
|
|
|
+ return ret_val;
|
|
|
+}
|