Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 321beec5047a ("net: phy: Use interrupts when available in NOLINK state")
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
Cc: stable <stable@vger.kernel.org> # v4.9+
Tested-by: Roger Quadros <rogerq@ti.com>
Tested-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Alexander Kochetkov and committed by
David S. Miller
f555f34f fd2c83b3

+37 -4
+36 -4
drivers/net/phy/phy.c
··· 591 591 EXPORT_SYMBOL(phy_mii_ioctl); 592 592 593 593 /** 594 - * phy_start_aneg - start auto-negotiation for this PHY device 594 + * phy_start_aneg_priv - start auto-negotiation for this PHY device 595 595 * @phydev: the phy_device struct 596 + * @sync: indicate whether we should wait for the workqueue cancelation 596 597 * 597 598 * Description: Sanitizes the settings (if we're not autonegotiating 598 599 * them), and then calls the driver's config_aneg function. 599 600 * If the PHYCONTROL Layer is operating, we change the state to 600 601 * reflect the beginning of Auto-negotiation or forcing. 601 602 */ 602 - int phy_start_aneg(struct phy_device *phydev) 603 + static int phy_start_aneg_priv(struct phy_device *phydev, bool sync) 603 604 { 605 + bool trigger = 0; 604 606 int err; 605 607 606 608 if (!phydev->drv) ··· 630 628 } 631 629 } 632 630 631 + /* Re-schedule a PHY state machine to check PHY status because 632 + * negotiation may already be done and aneg interrupt may not be 633 + * generated. 634 + */ 635 + if (phy_interrupt_is_valid(phydev) && (phydev->state == PHY_AN)) { 636 + err = phy_aneg_done(phydev); 637 + if (err > 0) { 638 + trigger = true; 639 + err = 0; 640 + } 641 + } 642 + 633 643 out_unlock: 634 644 mutex_unlock(&phydev->lock); 645 + 646 + if (trigger) 647 + phy_trigger_machine(phydev, sync); 648 + 635 649 return err; 650 + } 651 + 652 + /** 653 + * phy_start_aneg - start auto-negotiation for this PHY device 654 + * @phydev: the phy_device struct 655 + * 656 + * Description: Sanitizes the settings (if we're not autonegotiating 657 + * them), and then calls the driver's config_aneg function. 658 + * If the PHYCONTROL Layer is operating, we change the state to 659 + * reflect the beginning of Auto-negotiation or forcing. 660 + */ 661 + int phy_start_aneg(struct phy_device *phydev) 662 + { 663 + return phy_start_aneg_priv(phydev, true); 636 664 } 637 665 EXPORT_SYMBOL(phy_start_aneg); 638 666 ··· 691 659 * state machine runs. 692 660 */ 693 661 694 - static void phy_trigger_machine(struct phy_device *phydev, bool sync) 662 + void phy_trigger_machine(struct phy_device *phydev, bool sync) 695 663 { 696 664 if (sync) 697 665 cancel_delayed_work_sync(&phydev->state_queue); ··· 1186 1154 mutex_unlock(&phydev->lock); 1187 1155 1188 1156 if (needs_aneg) 1189 - err = phy_start_aneg(phydev); 1157 + err = phy_start_aneg_priv(phydev, false); 1190 1158 else if (do_suspend) 1191 1159 phy_suspend(phydev); 1192 1160
+1
include/linux/phy.h
··· 852 852 void phy_mac_interrupt(struct phy_device *phydev, int new_link); 853 853 void phy_start_machine(struct phy_device *phydev); 854 854 void phy_stop_machine(struct phy_device *phydev); 855 + void phy_trigger_machine(struct phy_device *phydev, bool sync); 855 856 int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); 856 857 int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); 857 858 int phy_ethtool_ksettings_get(struct phy_device *phydev,