Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1763908AbdDSNrC (ORCPT ); Wed, 19 Apr 2017 09:47:02 -0400 Received: from mail-lf0-f65.google.com ([209.85.215.65]:35108 "EHLO mail-lf0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1763799AbdDSNrA (ORCPT ); Wed, 19 Apr 2017 09:47:00 -0400 From: Alexander Kochetkov To: Florian Fainelli , netdev@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Alexander Kochetkov Subject: [PATCH] net: phy: fix auto-negotiation stall due to unavailable interrupt Date: Wed, 19 Apr 2017 16:46:44 +0300 Message-Id: <1492609604-16359-1-git-send-email-al.kochet@gmail.com> X-Mailer: git-send-email 1.7.9.5 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 1784 Lines: 44 The problem I fix related to SMSC LAN8710/LAN8720 PHY handled using interrupts. During power-up cycle the PHY do auto-negotiation, generate interrupt and set BMSR_ANEGCOMPLETE flag. Interrupt is handled by PHY state machine but doesn't update link because PHY is in PHY_READY state. After some time MAC bring up and connect with PHY. It start PHY using phy_start(). During startup PHY change state to PHY_AN but doesn't set BMCR_ANRESTART flag due to genphy_config_aneg() doesn't update MII_BMCR because there no new to advertising. As a result, state machine wait for interrupt from PHY and nether get "link is up". Because BMSR_ANEGCOMPLETE already set the patch schedule check link without waiting interrupt. In case genphy_config_aneg() update MII_BMCR and set BMCR_ANRESTART flag, BMSR_ANEGCOMPLETE will be cleared and state machine will continue on auto-negotiation interrupt. Signed-off-by: Alexander Kochetkov --- drivers/net/phy/phy.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7cc1b7d..da8f03d 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1169,6 +1169,18 @@ void phy_state_machine(struct work_struct *work) if (phydev->irq == PHY_POLL) queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, PHY_STATE_TIME * HZ); + + /* Re-schedule a PHY state machine to check PHY status because + * negotiation already done and aneg interrupt may not be generated. + */ + if (needs_aneg && (phydev->irq > 0) && (phydev->state == PHY_AN)) { + err = phy_aneg_done(phydev); + if (err > 0) + queue_delayed_work(system_power_efficient_wq, + &phydev->state_queue, 0); + if (err < 0) + phy_error(phydev); + } } /** -- 1.7.9.5