-
Notifications
You must be signed in to change notification settings - Fork 169
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[igb]: Support forcing speed and get info for Broadcom 54616 PHY
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 <[email protected]>
- Loading branch information
1 parent
34b5061
commit d58dd75
Showing
2 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
283 changes: 283 additions & 0 deletions
283
patch/0012-igb-setup-Broadcom-54616-PHY-when-no-EEPROM-present.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <[email protected]> | ||
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 <[email protected]> | ||
Cc: John W Linville <[email protected]> | ||
Signed-off-by: Sergey Popovich <[email protected]> | ||
--- | ||
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters