Skip to content

Commit

Permalink
[igb]: Support forcing speed and get info for Broadcom 54616 PHY
Browse files Browse the repository at this point in the history
We have support for Broadcom 54616 backported from Linux Kernel upstream
in commit 3639697 ("[igb]: support broadcom 54616 phy for Intel igb
driver (#3)").

However that support doesn't account for the case when no EEPROM present
and we need to initialize PHY manually.

Signed-off-by: Sergey Popovich <sergey.popovich@ordnance.co>
  • Loading branch information
sergeypopovich-ord committed Dec 25, 2018
1 parent 34b5061 commit d58dd75
Show file tree
Hide file tree
Showing 2 changed files with 284 additions and 0 deletions.
283 changes: 283 additions & 0 deletions patch/0012-igb-setup-Broadcom-54616-PHY-when-no-EEPROM-present.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
From 0bd96ca66657b5e05eca6758613be614bee63a77 Mon Sep 17 00:00:00 2001
From: Sergey Popovich <sergey.popovich@ordnance.co>
Date: Tue, 25 Dec 2018 06:54:08 +0000
Subject: igb: Setup Broadcom 54616 PHY when no EEPROM present

While commit eeb0149660a2 ("igb: support BCM54616 PHY") adds basic
support for Broadcom 54616 PHY it does not handle case when EEPROM
is missing.

In that case we need initialize PHY manually by isolating it from
MII interface and providing methods to force speed and duplex.

Behaviour was observed on Netberg Aurora 420 switch management port
that uses igb MAC and Broadcom PHY.

Fixes: commit eeb0149660a2 ("igb: support BCM54616 PHY")
Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Cc: John W Linville <linville@tuxdriver.com>
Signed-off-by: Sergey Popovich <sergey.popovich@ordnance.co>
---
e1000_82575.c | 12 +++--
e1000_defines.h | 1 +
e1000_phy.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
e1000_phy.h | 4 ++
4 files changed, 158 insertions(+), 3 deletions(-)

diff --git a/e1000_82575.c b/e1000_82575.c
index d9aaa13..19a464d 100644
--- a/e1000_82575.c
+++ b/e1000_82575.c
@@ -342,6 +342,8 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
break;
case BCM54616_E_PHY_ID:
phy->type = e1000_phy_bcm54616;
+ phy->ops.get_phy_info = igb_get_phy_info_bcm54xx;
+ phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_bcm54xx;
break;
default:
ret_val = -E1000_ERR_PHY;
@@ -1285,9 +1287,12 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
hw_dbg("MNG configuration cycle has not completed.\n");

/* If EEPROM is not marked present, init the PHY manually */
- if (((rd32(E1000_EECD) & E1000_EECD_PRES) == 0) &&
- (hw->phy.type == e1000_phy_igp_3))
- igb_phy_init_script_igp3(hw);
+ if ((rd32(E1000_EECD) & E1000_EECD_PRES) == 0) {
+ if (hw->phy.type == e1000_phy_igp_3)
+ igb_phy_init_script_igp3(hw);
+ else if (hw->phy.type == e1000_phy_bcm54616)
+ igb_phy_init_script_bcm54xx(hw);
+ }

return 0;
}
@@ -1663,6 +1668,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:
+ ret_val = igb_copper_link_setup_bcm54xx(hw);
break;
default:
ret_val = -E1000_ERR_PHY;
diff --git a/e1000_defines.h b/e1000_defines.h
index ce95b7e..2a14819 100644
--- a/e1000_defines.h
+++ b/e1000_defines.h
@@ -632,6 +632,7 @@
/* PHY Control Register */
#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
+#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */
#define MII_CR_POWER_DOWN 0x0800 /* Power down */
#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */
diff --git a/e1000_phy.c b/e1000_phy.c
index 2788a54..4c9090a 100644
--- a/e1000_phy.c
+++ b/e1000_phy.c
@@ -23,6 +23,7 @@

#include <linux/if_ether.h>
#include <linux/delay.h>
+#include <linux/brcmphy.h>

#include "e1000_mac.h"
#include "e1000_phy.h"
@@ -467,6 +468,34 @@ out:
}

/**
+ * igb_copper_link_setup_bcm54xx - Setup BCM54xx PHY for copper link
+ * @hw: pointer to the HW structure
+ *
+ * Sets up copper link.
+ **/
+s32 igb_copper_link_setup_bcm54xx(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val = 0;
+ u16 phy_data;
+
+ if (phy->reset_disable)
+ return 0;
+
+ ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
+ if (ret_val)
+ goto out;
+
+ phy_data &= ~(MII_CR_ISOLATE);
+
+ ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
+ if (ret_val)
+ goto out;
+out:
+ return ret_val;
+}
+
+/**
* igb_copper_link_setup_82580 - Setup 82580 PHY for copper link
* @hw: pointer to the HW structure
*
@@ -1676,6 +1705,55 @@ s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
}

/**
+ * igb_phy_force_speed_duplex_bcm54xx - Force speed/duplex for BCM54xx PHY
+ * @hw: pointer to the HW structure
+ *
+ * Calls the PHY setup function to force speed and duplex. Waits
+ * for link and returns successful if link up is successful, else
+ * -E1000_ERR_PHY (-2).
+ **/
+s32 igb_phy_force_speed_duplex_bcm54xx(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 phy_data;
+ bool link;
+
+ ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
+ if (ret_val)
+ goto out;
+
+ igb_phy_force_speed_duplex_setup(hw, &phy_data);
+
+ phy_data &= ~(MII_CR_POWER_DOWN | MII_CR_ISOLATE);
+
+ ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
+ if (ret_val)
+ goto out;
+
+ udelay(1);
+
+ if (phy->autoneg_wait_to_complete) {
+ hw_dbg("Waiting for forced speed/duplex link on BCM phy.\n");
+
+ ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, 100000, &link);
+ if (ret_val)
+ goto out;
+
+ if (!link)
+ hw_dbg("Link taking longer than expected.\n");
+
+ /* Try once more */
+ ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, 100000, &link);
+ if (ret_val)
+ goto out;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
* igb_get_cable_length_m88 - Determine cable length for m88 PHY
* @hw: pointer to the HW structure
*
@@ -2058,6 +2136,39 @@ out:
}

/**
+ * igb_get_phy_info_bcm54xx - Retrieve PHY information
+ * @hw: pointer to the HW structure
+ *
+ * Valid for only copper links. Read the PHY status register (sticky read)
+ * to verify that link is up.
+ **/
+s32 igb_get_phy_info_bcm54xx(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ bool link;
+
+ if (phy->media_type != e1000_media_type_copper) {
+ hw_dbg("Phy info is only valid for copper media\n");
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ ret_val = igb_phy_has_link(hw, 1, 0, &link);
+ if (ret_val)
+ goto out;
+
+ if (!link) {
+ hw_dbg("Phy info is only valid if link is up\n");
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
* igb_phy_sw_reset - PHY software reset
* @hw: pointer to the HW structure
*
@@ -2215,6 +2326,39 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
}

/**
+ * igb_phy_init_script_bcm54xx - Initialize BCM54xx PHY
+ * @hw: pointer to the HW structure
+ *
+ * Initialize Broadcom 54xx to work correctly.
+ **/
+s32 igb_phy_init_script_bcm54xx(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ u16 data = 0;
+ s32 ret_val = 0;
+
+ ret_val = phy->ops.read_reg(hw, MII_BCM54XX_ECR, &data);
+ if (ret_val)
+ goto out;
+
+ /* Mask interrupts globally. */
+ data |= MII_BCM54XX_ECR_IM;
+ ret_val = phy->ops.write_reg(hw, MII_BCM54XX_ECR, data);
+ if (ret_val)
+ goto out;
+
+ /* Unmask events we are interested in. */
+ data = ~(MII_BCM54XX_INT_DUPLEX |
+ MII_BCM54XX_INT_SPEED |
+ MII_BCM54XX_INT_LINK);
+ ret_val = phy->ops.write_reg(hw, MII_BCM54XX_IMR, data);
+ if (ret_val)
+ goto out;
+out:
+ return ret_val;
+}
+
+/**
* igb_initialize_M88E1512_phy - Initialize M88E1512 PHY
* @hw: pointer to the HW structure
*
diff --git a/e1000_phy.h b/e1000_phy.h
index 9b622b3..dd55a10 100644
--- a/e1000_phy.h
+++ b/e1000_phy.h
@@ -61,6 +61,7 @@ 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_bcm54xx(struct e1000_hw *hw);
s32 igb_initialize_M88E1512_phy(struct e1000_hw *hw);
s32 igb_initialize_M88E1543_phy(struct e1000_hw *hw);
s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
@@ -75,6 +76,9 @@ s32 igb_get_cable_length_82580(struct e1000_hw *hw);
s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data);
s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_check_polarity_m88(struct e1000_hw *hw);
+s32 igb_copper_link_setup_bcm54xx(struct e1000_hw *hw);
+s32 igb_phy_force_speed_duplex_bcm54xx(struct e1000_hw *hw);
+s32 igb_get_phy_info_bcm54xx(struct e1000_hw *hw);

/* IGP01E1000 Specific Registers */
#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */
--
2.11.0

1 change: 1 addition & 0 deletions patch/series
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
0009-platform-mellonox-introduce-mlxreg-io-driver-and-add.patch
0010-config-mellanox-configuration.patch
0011-support-Broadcom-54616-Phy-for-Intel-igb-driver.patch
0012-igb-setup-Broadcom-54616-PHY-when-no-EEPROM-present.patch
0013-platform-mellanox-mlxreg-hotplug-driver-add-check-fo.patch
driver-arista-net-tg3-dma-mask-4g-sb800.patch
driver-arista-net-tg3-disallow-broadcom-default-mac.patch
Expand Down

0 comments on commit d58dd75

Please sign in to comment.