summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeiner Kallweit <hkallweit1@gmail.com>2019-02-06 19:39:52 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-04-05 22:33:10 +0200
commit2dd699432958f8de930f3782d0a41cfe71e12698 (patch)
tree8f0c750cdb149857afa6c49892a01d2c6409ff38
parent5203cf8e29e57599138e7bfb61b786a7d84d3988 (diff)
net: phy: consider latched link-down status in polling mode
[ Upstream commit 93c0970493c71f264e6c3c7caf1ff24a9e1de786 ] The link status value latches link-down events. To get the current status we read the register twice in genphy_update_link(). There's a potential risk that we miss a link-down event in polling mode. This may cause issues if the user e.g. connects his machine to a different network. On the other hand reading the latched value may cause issues in interrupt mode. Following scenario: - After boot link goes up - phy_start() is called triggering an aneg restart, hence link goes down and link-down info is latched. - After aneg has finished link goes up and triggers an interrupt. Interrupt handler reads link status, means it reads the latched "link is down" info. But there won't be another interrupt as long as link stays up, therefore phylib will never recognize that link is up. Deal with both scenarios by reading the register twice in interrupt mode only. Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Sasha Levin <sashal@kernel.org>
-rw-r--r--drivers/net/phy/phy-c45.c10
-rw-r--r--drivers/net/phy/phy_device.c13
2 files changed, 17 insertions, 6 deletions
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index e1225545362d..0ba3607585bd 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -147,9 +147,15 @@ int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask)
mmd_mask &= ~BIT(devad);
/* The link state is latched low so that momentary link
- * drops can be detected. Do not double-read the status
- * register if the link is down.
+ * drops can be detected. Do not double-read the status
+ * in polling mode to detect such short link drops.
*/
+ if (!phy_polling_mode(phydev)) {
+ val = phy_read_mmd(phydev, devad, MDIO_STAT1);
+ if (val < 0)
+ return val;
+ }
+
val = phy_read_mmd(phydev, devad, MDIO_STAT1);
if (val < 0)
return val;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 2c32c795f5dd..8a96d985a52f 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1503,10 +1503,15 @@ int genphy_update_link(struct phy_device *phydev)
{
int status;
- /* Do a fake read */
- status = phy_read(phydev, MII_BMSR);
- if (status < 0)
- return status;
+ /* The link state is latched low so that momentary link
+ * drops can be detected. Do not double-read the status
+ * in polling mode to detect such short link drops.
+ */
+ if (!phy_polling_mode(phydev)) {
+ status = phy_read(phydev, MII_BMSR);
+ if (status < 0)
+ return status;
+ }
/* Read link and autonegotiation status */
status = phy_read(phydev, MII_BMSR);