From: Alan Liebthal <al...@cumulusnetworks.com>

The Quanta LY8 Ethernet management port uses a Broadcom 5461S chip for
the PHY layer. This adds support for this PHY to the Intel igb driver.

Signed-off-by: Alan Liebthal <al...@cumulusnetworks.com>
Signed-off-by: Jonathan Toppins <jtopp...@cumulusnetworks.com>
---
 drivers/net/ethernet/intel/igb/e1000_82575.c   |   20 +++++-
 drivers/net/ethernet/intel/igb/e1000_defines.h |    1 +
 drivers/net/ethernet/intel/igb/e1000_hw.h      |    1 +
 drivers/net/ethernet/intel/igb/e1000_phy.c     |   79 ++++++++++++++++++++++++
 drivers/net/ethernet/intel/igb/e1000_phy.h     |    2 +
 5 files changed, 102 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c 
b/drivers/net/ethernet/intel/igb/e1000_82575.c
index 2dcc808..e222804 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -178,7 +178,7 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
 
        ctrl_ext = rd32(E1000_CTRL_EXT);
 
-       if (igb_sgmii_active_82575(hw)) {
+       if (igb_sgmii_active_82575(hw) && phy->type != e1000_phy_bcm5461s) {
                phy->ops.reset = igb_phy_hw_reset_sgmii_82575;
                ctrl_ext |= E1000_CTRL_I2C_ENA;
        } else {
@@ -289,6 +289,11 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
        case BCM54616_E_PHY_ID:
                phy->type = e1000_phy_bcm54616;
                break;
+       case BCM5461S_E_PHY_ID:
+               phy->type                   = e1000_phy_bcm5461s;
+               phy->ops.get_phy_info       = igb_get_phy_info_5461s;
+               phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_82580;
+               break;
        default:
                ret_val = -E1000_ERR_PHY;
                goto out;
@@ -841,6 +846,15 @@ static s32 igb_get_phy_id_82575(struct e1000_hw *hw)
                        goto out;
                }
                ret_val = igb_get_phy_id(hw);
+               if (ret_val && hw->mac.type == e1000_i354) {
+                       /* We do a special check for bcm5461s phy by setting
+                        * the phy->addr to 5 and doing the phy check again.
+                        * This call will succeed and retrieve a valid phy
+                        * id if we have the bcm5461s phy.
+                        */
+                       phy->addr = 5;
+                       ret_val = igb_get_phy_id(hw);
+               }
                goto out;
        }
 
@@ -1221,6 +1235,9 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
            (hw->phy.type == e1000_phy_igp_3))
                igb_phy_init_script_igp3(hw);
 
+       if (hw->phy.type == e1000_phy_bcm5461s)
+               igb_phy_init_script_5461s(hw);
+
        return 0;
 }
 
@@ -1597,6 +1614,7 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw 
*hw)
                ret_val = igb_copper_link_setup_82580(hw);
                break;
        case e1000_phy_bcm54616:
+       case e1000_phy_bcm5461s:
                break;
        default:
                ret_val = -E1000_ERR_PHY;
diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h 
b/drivers/net/ethernet/intel/igb/e1000_defines.h
index 50d51e4..787224d 100644
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -861,6 +861,7 @@
 #define I210_I_PHY_ID        0x01410C00
 #define M88E1543_E_PHY_ID    0x01410EA0
 #define BCM54616_E_PHY_ID    0x03625D10
+#define BCM5461S_E_PHY_ID    0x002060C0
 
 /* M88E1000 Specific Registers */
 #define M88E1000_PHY_SPEC_CTRL     0x10  /* PHY Specific Control Register */
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h 
b/drivers/net/ethernet/intel/igb/e1000_hw.h
index d82c96b..408a3e5 100644
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -129,6 +129,7 @@ enum e1000_phy_type {
        e1000_phy_82580,
        e1000_phy_i210,
        e1000_phy_bcm54616,
+       e1000_phy_bcm5461s,
 };
 
 enum e1000_bus_type {
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c 
b/drivers/net/ethernet/intel/igb/e1000_phy.c
index c1bb64d..0f5a55b 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -148,6 +148,13 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, 
u16 *data)
         * Control register.  The MAC will take care of interfacing with the
         * PHY to retrieve the desired data.
         */
+       if (phy->type == e1000_phy_bcm5461s) {
+               mdic = rd32(E1000_MDICNFG);
+               mdic &= ~E1000_MDICNFG_PHY_MASK;
+               mdic |= (phy->addr << E1000_MDICNFG_PHY_SHIFT);
+               wr32(E1000_MDICNFG, mdic);
+       }
+
        mdic = ((offset << E1000_MDIC_REG_SHIFT) |
                (phy->addr << E1000_MDIC_PHY_SHIFT) |
                (E1000_MDIC_OP_READ));
@@ -204,6 +211,13 @@ s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 
offset, u16 data)
         * Control register.  The MAC will take care of interfacing with the
         * PHY to retrieve the desired data.
         */
+       if (phy->type == e1000_phy_bcm5461s) {
+               mdic = rd32(E1000_MDICNFG);
+               mdic &= ~E1000_MDICNFG_PHY_MASK;
+               mdic |= (phy->addr << E1000_MDICNFG_PHY_SHIFT);
+               wr32(E1000_MDICNFG, mdic);
+       }
+
        mdic = (((u32)data) |
                (offset << E1000_MDIC_REG_SHIFT) |
                (phy->addr << E1000_MDIC_PHY_SHIFT) |
@@ -2509,3 +2523,68 @@ static s32 igb_set_master_slave_mode(struct e1000_hw *hw)
 
        return hw->phy.ops.write_reg(hw, PHY_1000T_CTRL, phy_data);
 }
+
+/**
+ *  igb_phy_init_script_5461s - Inits the BCM5461S PHY
+ *  @hw: pointer to the HW structure
+ *
+ *  Initializes a Broadcom Gigabit PHY.
+ **/
+s32 igb_phy_init_script_5461s(struct e1000_hw *hw)
+{
+       u16 mii_reg_led = 0;
+
+       /* 1. Speed LED (Set the Link LED mode), Shadow 00010, 0x1C.bit2=1 */
+       hw->phy.ops.write_reg(hw, 0x1C, 0x0800);
+       hw->phy.ops.read_reg(hw, 0x1C, &mii_reg_led);
+       mii_reg_led |= 0x0004;
+       hw->phy.ops.write_reg(hw, 0x1C, mii_reg_led | 0x8000);
+
+       /* 2. Active LED (Set the Link LED mode), Shadow 01001, 0x1C.bit4=1,
+        *    0x10.bit5=0
+        */
+       hw->phy.ops.write_reg(hw, 0x1C, 0x2400);
+       hw->phy.ops.read_reg(hw, 0x1C, &mii_reg_led);
+       mii_reg_led |= 0x0010;
+       hw->phy.ops.write_reg(hw, 0x1C, mii_reg_led | 0x8000);
+       hw->phy.ops.read_reg(hw, 0x10, &mii_reg_led);
+       mii_reg_led &= 0xffdf;
+       hw->phy.ops.write_reg(hw, 0x10, mii_reg_led);
+
+       return 0;
+}
+
+/**
+ *  igb_get_phy_info_5461s - Retrieve 5461s 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 igb_get_phy_info_5461s(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+       bool link;
+
+       ret_val = igb_phy_has_link(hw, 1, 0, &link);
+       if (ret_val)
+               goto out;
+
+       if (!link) {
+               ret_val = -E1000_ERR_CONFIG;
+               goto out;
+       }
+
+       phy->polarity_correction = true;
+
+       phy->is_mdix = true;
+       phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
+       phy->local_rx = e1000_1000t_rx_status_ok;
+       phy->remote_rx = e1000_1000t_rx_status_ok;
+
+out:
+       return ret_val;
+}
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h 
b/drivers/net/ethernet/intel/igb/e1000_phy.h
index 7af4ffa..fcdd84c 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.h
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.h
@@ -61,6 +61,8 @@ s32  igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
 void igb_power_up_phy_copper(struct e1000_hw *hw);
 void igb_power_down_phy_copper(struct e1000_hw *hw);
 s32  igb_phy_init_script_igp3(struct e1000_hw *hw);
+s32  igb_phy_init_script_5461s(struct e1000_hw *hw);
+s32  igb_get_phy_info_5461s(struct e1000_hw *hw);
 s32  igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
 s32  igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
 s32  igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to