From patchwork Thu Apr 20 11:00:04 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Kochetkov X-Patchwork-Id: 752747 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3w7ws00MDkz9s7B for ; Thu, 20 Apr 2017 21:00:52 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="g9P+exEK"; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1030253AbdDTLA2 (ORCPT ); Thu, 20 Apr 2017 07:00:28 -0400 Received: from mail-lf0-f65.google.com ([209.85.215.65]:34659 "EHLO mail-lf0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S943306AbdDTLAM (ORCPT ); Thu, 20 Apr 2017 07:00:12 -0400 Received: by mail-lf0-f65.google.com with SMTP id x72so5343977lfb.1; Thu, 20 Apr 2017 04:00:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=AdbnPZ4F6pxoCESlWO2GrTXtnOhkBy1Q0j++zR2DRj0=; b=g9P+exEKJ88PHQ0+iorRVVe6FGOpFvia4JeKhQzsZTHg2qvGcagHmKZmPLRUEKSa3c yb+rLDnqQetPYwjqiKFmnUptlUBGDBgbmOFstNoCyMOn/KyxLFDYL2uEo40GUAsMl+mp 1IjpILhTQWlzuQvqrXa0PVbxHaAau3Eal3BEMqWzrux2o0kodpZBxZpVp++TSyBIT++n uo+7PjOE3PQ1zYxXdmoJQ2rVtSOkxIdv1aVPJeTp+WEsnOU/nCWKc2SvYujJIsvo8ODg cOG1A63F7zvFyLV4gLbffVAkuoETusqZnrXBekg0Rlu2GB1TgODihbH6OOqfiiX2iYDX 3koQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=AdbnPZ4F6pxoCESlWO2GrTXtnOhkBy1Q0j++zR2DRj0=; b=pUqEArkC2FRSvj+76QgcmE41K8crNeRWXDmweCE5GMMhLjDdBo9601VLgw6ABTgz3y EXMmz9v184Vj51BsfkUi22tdSezxEqWtmnGkU67+Hb5//7mzaDmiakmiBGMPHXhL/SpE 9mGcnpSwIz77NHtS3LmFm73hPdAa8T6KGEvnge64mLJI9kPt+Hr2D+CZgNtqqsTT0ycZ r8gkzEvAAoR3DUFFbMadXWoL4UCm5m3f7BqUrBRk07k3GhpGzZYqL8VoLvQocW9v1fod mFm46CuBvSHMbP4fGTIb3WSZdAP5gNJv3UUfKebIBIuSXw0n99rDzhMg7rzdmVmK0Snw jhWg== X-Gm-Message-State: AN3rC/7yBg4LFVpBLGt+QcoJNe6uD0S7dAFNzBN5LLj2V34Kk9DhvOru 0tFDX6G+wEsHvw== X-Received: by 10.25.159.84 with SMTP id i81mr2682760lfe.15.1492686010389; Thu, 20 Apr 2017 04:00:10 -0700 (PDT) Received: from ubuntu.lintech.local ([185.35.119.87]) by smtp.gmail.com with ESMTPSA id x1sm976569lfb.37.2017.04.20.04.00.09 (version=TLS1_1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 20 Apr 2017 04:00:09 -0700 (PDT) From: Alexander Kochetkov To: Florian Fainelli , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Sergei Shtylyov , Roger Quadros , Madalin Bucur Cc: Alexander Kochetkov Subject: [PATCH v1] net: phy: fix auto-negotiation stall due to unavailable interrupt Date: Thu, 20 Apr 2017 14:00:04 +0300 Message-Id: <1492686004-30527-2-git-send-email-al.kochet@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1492686004-30527-1-git-send-email-al.kochet@gmail.com> References: <1492686004-30527-1-git-send-email-al.kochet@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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. Signed-off-by: Alexander Kochetkov Cc: stable # v4.9+ Tested-by: Roger Quadros --- drivers/net/phy/phy.c | 40 ++++++++++++++++++++++++++++++++++++---- include/linux/phy.h | 1 + 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7cc1b7d..2d9975b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -591,16 +591,18 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) EXPORT_SYMBOL(phy_mii_ioctl); /** - * phy_start_aneg - start auto-negotiation for this PHY device + * phy_start_aneg_priv - start auto-negotiation for this PHY device * @phydev: the phy_device struct + * @sync: indicate whether we should wait for the workqueue cancelation * * Description: Sanitizes the settings (if we're not autonegotiating * them), and then calls the driver's config_aneg function. * If the PHYCONTROL Layer is operating, we change the state to * reflect the beginning of Auto-negotiation or forcing. */ -int phy_start_aneg(struct phy_device *phydev) +static int phy_start_aneg_priv(struct phy_device *phydev, bool sync) { + bool trigger = 0; int err; mutex_lock(&phydev->lock); @@ -625,10 +627,40 @@ int phy_start_aneg(struct phy_device *phydev) } } + /* Re-schedule a PHY state machine to check PHY status because + * negotiation may already be done and aneg interrupt may not be + * generated. + */ + if (phy_interrupt_is_valid(phydev) && (phydev->state == PHY_AN)) { + err = phy_aneg_done(phydev); + if (err > 0) { + trigger = true; + err = 0; + } + } + out_unlock: mutex_unlock(&phydev->lock); + + if (trigger) + phy_trigger_machine(phydev, sync); + return err; } + +/** + * phy_start_aneg - start auto-negotiation for this PHY device + * @phydev: the phy_device struct + * + * Description: Sanitizes the settings (if we're not autonegotiating + * them), and then calls the driver's config_aneg function. + * If the PHYCONTROL Layer is operating, we change the state to + * reflect the beginning of Auto-negotiation or forcing. + */ +int phy_start_aneg(struct phy_device *phydev) +{ + return phy_start_aneg_priv(phydev, true); +} EXPORT_SYMBOL(phy_start_aneg); /** @@ -656,7 +688,7 @@ void phy_start_machine(struct phy_device *phydev) * state machine runs. */ -static void phy_trigger_machine(struct phy_device *phydev, bool sync) +void phy_trigger_machine(struct phy_device *phydev, bool sync) { if (sync) cancel_delayed_work_sync(&phydev->state_queue); @@ -1151,7 +1183,7 @@ void phy_state_machine(struct work_struct *work) mutex_unlock(&phydev->lock); if (needs_aneg) - err = phy_start_aneg(phydev); + err = phy_start_aneg_priv(phydev, false); else if (do_suspend) phy_suspend(phydev); diff --git a/include/linux/phy.h b/include/linux/phy.h index 7fc1105..b19ae66 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -840,6 +840,7 @@ int phy_drivers_register(struct phy_driver *new_driver, int n, void phy_mac_interrupt(struct phy_device *phydev, int new_link); void phy_start_machine(struct phy_device *phydev); void phy_stop_machine(struct phy_device *phydev); +void phy_trigger_machine(struct phy_device *phydev, bool sync); int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); int phy_ethtool_ksettings_get(struct phy_device *phydev,