Skip to content

Commit f555f34

Browse files
akochetkovdavem330
authored andcommitted
net: phy: fix auto-negotiation stall due to unavailable interrupt
The Ethernet link on an interrupt driven PHY was not coming up if the Ethernet cable was plugged before the Ethernet interface was brought up. The patch trigger PHY state machine to update link state if PHY was requested to do auto-negotiation and auto-negotiation complete flag already set. During power-up cycle the PHY do auto-negotiation, generate interrupt and set auto-negotiation complete flag. Interrupt is handled by PHY state machine but doesn't update link state because PHY is in PHY_READY state. After some time MAC bring up, start and request PHY to do auto-negotiation. If there are no new settings to advertise genphy_config_aneg() doesn't start PHY auto-negotiation. PHY continue to stay in auto-negotiation complete state and doesn't fire interrupt. At the same time PHY state machine expect that PHY started auto-negotiation and is waiting for interrupt from PHY and it won't get it. Fixes: 321beec ("net: phy: Use interrupts when available in NOLINK state") Signed-off-by: Alexander Kochetkov <[email protected]> Cc: stable <[email protected]> # v4.9+ Tested-by: Roger Quadros <[email protected]> Tested-by: Alexandre Belloni <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent fd2c83b commit f555f34

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

drivers/net/phy/phy.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -591,16 +591,18 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
591591
EXPORT_SYMBOL(phy_mii_ioctl);
592592

593593
/**
594-
* phy_start_aneg - start auto-negotiation for this PHY device
594+
* phy_start_aneg_priv - start auto-negotiation for this PHY device
595595
* @phydev: the phy_device struct
596+
* @sync: indicate whether we should wait for the workqueue cancelation
596597
*
597598
* Description: Sanitizes the settings (if we're not autonegotiating
598599
* them), and then calls the driver's config_aneg function.
599600
* If the PHYCONTROL Layer is operating, we change the state to
600601
* reflect the beginning of Auto-negotiation or forcing.
601602
*/
602-
int phy_start_aneg(struct phy_device *phydev)
603+
static int phy_start_aneg_priv(struct phy_device *phydev, bool sync)
603604
{
605+
bool trigger = 0;
604606
int err;
605607

606608
if (!phydev->drv)
@@ -628,10 +630,40 @@ int phy_start_aneg(struct phy_device *phydev)
628630
}
629631
}
630632

633+
/* Re-schedule a PHY state machine to check PHY status because
634+
* negotiation may already be done and aneg interrupt may not be
635+
* generated.
636+
*/
637+
if (phy_interrupt_is_valid(phydev) && (phydev->state == PHY_AN)) {
638+
err = phy_aneg_done(phydev);
639+
if (err > 0) {
640+
trigger = true;
641+
err = 0;
642+
}
643+
}
644+
631645
out_unlock:
632646
mutex_unlock(&phydev->lock);
647+
648+
if (trigger)
649+
phy_trigger_machine(phydev, sync);
650+
633651
return err;
634652
}
653+
654+
/**
655+
* phy_start_aneg - start auto-negotiation for this PHY device
656+
* @phydev: the phy_device struct
657+
*
658+
* Description: Sanitizes the settings (if we're not autonegotiating
659+
* them), and then calls the driver's config_aneg function.
660+
* If the PHYCONTROL Layer is operating, we change the state to
661+
* reflect the beginning of Auto-negotiation or forcing.
662+
*/
663+
int phy_start_aneg(struct phy_device *phydev)
664+
{
665+
return phy_start_aneg_priv(phydev, true);
666+
}
635667
EXPORT_SYMBOL(phy_start_aneg);
636668

637669
/**
@@ -659,7 +691,7 @@ void phy_start_machine(struct phy_device *phydev)
659691
* state machine runs.
660692
*/
661693

662-
static void phy_trigger_machine(struct phy_device *phydev, bool sync)
694+
void phy_trigger_machine(struct phy_device *phydev, bool sync)
663695
{
664696
if (sync)
665697
cancel_delayed_work_sync(&phydev->state_queue);
@@ -1154,7 +1186,7 @@ void phy_state_machine(struct work_struct *work)
11541186
mutex_unlock(&phydev->lock);
11551187

11561188
if (needs_aneg)
1157-
err = phy_start_aneg(phydev);
1189+
err = phy_start_aneg_priv(phydev, false);
11581190
else if (do_suspend)
11591191
phy_suspend(phydev);
11601192

include/linux/phy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,7 @@ void phy_change_work(struct work_struct *work);
852852
void phy_mac_interrupt(struct phy_device *phydev, int new_link);
853853
void phy_start_machine(struct phy_device *phydev);
854854
void phy_stop_machine(struct phy_device *phydev);
855+
void phy_trigger_machine(struct phy_device *phydev, bool sync);
855856
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
856857
int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);
857858
int phy_ethtool_ksettings_get(struct phy_device *phydev,

0 commit comments

Comments
 (0)