From patchwork Tue May 14 12:27:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Gessler X-Patchwork-Id: 13664048 X-Patchwork-Delegate: kuba@kernel.org Received: from mxout37.expurgate.net (mxout37.expurgate.net [194.37.255.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F0AD86BFA8; Tue, 14 May 2024 12:44:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=194.37.255.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715690679; cv=none; b=sMhBvOewhx5IW92vwHv/KLFXg1wbyIJ7jvhuX1pZqXf5ZQ9npm6rXSLAfd5uqlMO4PHUDOtF7cuop5RST1cm72GuvvBJ6UIJk/Fz9WN6c3RFvbGaZoPCemrtDusfv+2+rJC5T+h2rHIZeBBe70U90hiavjGkGrrSkNA6Cx19BDM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715690679; c=relaxed/simple; bh=yTLW0IJRaUkMcye7x1q7XLp3U8fuID9lCwQ20A94yiY=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=IdRDiqll9EDeCuGDgOCUMSNnbibiKrphPSNMb1Zes9Q7iG4fkNZ323dkzEJ/QVbm3iXO94iBuRQAuHEVqXttIt2oZOqiEaJjhQqd2iLwZ5z7R4P38MJoiNYtXQjTHwSrBc2WHbCeh0HXbBMWfdN+/aXSiQ1tLXwT7D0/xGV6Zx0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=brueckmann-gmbh.de; spf=pass smtp.mailfrom=brueckmann-gmbh.de; arc=none smtp.client-ip=194.37.255.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=brueckmann-gmbh.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=brueckmann-gmbh.de Received: from [127.0.0.1] (helo=localhost) by relay.expurgate.net with smtp (Exim 4.92) (envelope-from ) id 1s6rGx-00HQk6-BC; Tue, 14 May 2024 14:28:47 +0200 Received: from [217.239.223.202] (helo=zimbra.brueckmann-gmbh.de) by relay.expurgate.net with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1s6rGv-001xKj-Tw; Tue, 14 May 2024 14:28:45 +0200 Received: from zimbra.brueckmann-gmbh.de (localhost [127.0.0.1]) by zimbra.brueckmann-gmbh.de (Postfix) with ESMTPS id 3A336CA5AA8; Tue, 14 May 2024 14:28:45 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by zimbra.brueckmann-gmbh.de (Postfix) with ESMTP id 353E7CA5AF6; Tue, 14 May 2024 14:28:45 +0200 (CEST) Received: from zimbra.brueckmann-gmbh.de ([127.0.0.1]) by localhost (zimbra.brueckmann-gmbh.de [127.0.0.1]) (amavis, port 10026) with ESMTP id nar3tVrW8P2h; Tue, 14 May 2024 14:28:45 +0200 (CEST) Received: from ew-linux.ew (unknown [10.0.11.14]) by zimbra.brueckmann-gmbh.de (Postfix) with ESMTPSA id 1A1B8CA5AA8; Tue, 14 May 2024 14:28:45 +0200 (CEST) From: Thomas Gessler To: Andrew Lunn , Heiner Kallweit , Russell King , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Thomas Gessler , MD Danish Anwar , Ravi Gunasekaran Subject: [PATCH 1/2] net: phy: dp83869: Add PHY ID for chip revision 3 Date: Tue, 14 May 2024 14:27:27 +0200 Message-Id: <20240514122728.1490156-1-thomas.gessler@brueckmann-gmbh.de> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-purgate: clean X-purgate-type: clean X-purgate-ID: 151534::1715689726-CD4998D1-EB8F54D6/0/0 X-Patchwork-Delegate: kuba@kernel.org The recent silicon revision 3 of the DP83869 has a different PHY ID which has to be added to the driver in order for the PHY to be detected. There appear to be no documented differences between the revisions, although there are some discussions in the TI forum about different behavior for some registers. Signed-off-by: Thomas Gessler --- drivers/net/phy/dp83869.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index d7aaefb5226b..d248a13c1749 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -15,7 +15,8 @@ #include -#define DP83869_PHY_ID 0x2000a0f1 +#define DP83869REV1_PHY_ID 0x2000a0f1 +#define DP83869REV3_PHY_ID 0x2000a0f3 #define DP83561_PHY_ID 0x2000a1a4 #define DP83869_DEVADDR 0x1f @@ -909,14 +910,16 @@ static int dp83869_phy_reset(struct phy_device *phydev) } static struct phy_driver dp83869_driver[] = { - DP83869_PHY_DRIVER(DP83869_PHY_ID, "TI DP83869"), + DP83869_PHY_DRIVER(DP83869REV1_PHY_ID, "TI DP83869 Rev. 1"), + DP83869_PHY_DRIVER(DP83869REV3_PHY_ID, "TI DP83869 Rev. 3"), DP83869_PHY_DRIVER(DP83561_PHY_ID, "TI DP83561-SP"), }; module_phy_driver(dp83869_driver); static struct mdio_device_id __maybe_unused dp83869_tbl[] = { - { PHY_ID_MATCH_MODEL(DP83869_PHY_ID) }, + { PHY_ID_MATCH_MODEL(DP83869REV1_PHY_ID) }, + { PHY_ID_MATCH_MODEL(DP83869REV3_PHY_ID) }, { PHY_ID_MATCH_MODEL(DP83561_PHY_ID) }, { } }; From patchwork Tue May 14 12:27:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Gessler X-Patchwork-Id: 13664049 X-Patchwork-Delegate: kuba@kernel.org Received: from mxout37.expurgate.net (mxout37.expurgate.net [194.37.255.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 07B5D46430; Tue, 14 May 2024 12:45:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=194.37.255.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715690751; cv=none; b=YNylX59ksyOi2C+GeKopg8DOc/iT6t2Jb7ea2UGzyh2IluyqG0UuThWhd9VohMisWByILapI2SrjIJe24486NTOWjqTou5UabiEkz7vZjmaal4h2Lj+eCjEmaXfhnsaRNW76/Yc4644KXoJUWwhzt80ygKVfPkU7fNFVHH7yhcE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715690751; c=relaxed/simple; bh=NLL4HBEiAyQkrN0runhZY+Wcah1jVOEAdXj+xW2bWBw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=P2G2thJK82Mfc0XUrYN+Fzuu5Z5DmX/c3YrHglb1pedUrioIg26fe3gWqsmBhYm09/xcmDfU0tUESlXKyzt7/V7f4HCAl4fZhXCFtHwEpJmO9rSMjvfOsb6Ylvw8vGh45pHXpyqTSWP0ssXIpzYM+z03HpWjcZ+8uuzVRenCOH0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=brueckmann-gmbh.de; spf=pass smtp.mailfrom=brueckmann-gmbh.de; arc=none smtp.client-ip=194.37.255.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=brueckmann-gmbh.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=brueckmann-gmbh.de Received: from [127.0.0.1] (helo=localhost) by relay.expurgate.net with smtp (Exim 4.92) (envelope-from ) id 1s6rH4-001y82-GG; Tue, 14 May 2024 14:28:54 +0200 Received: from [217.239.223.202] (helo=zimbra.brueckmann-gmbh.de) by relay.expurgate.net with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1s6rH3-00H5xj-7Z; Tue, 14 May 2024 14:28:53 +0200 Received: from zimbra.brueckmann-gmbh.de (localhost [127.0.0.1]) by zimbra.brueckmann-gmbh.de (Postfix) with ESMTPS id 8A044CA5AA8; Tue, 14 May 2024 14:28:52 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by zimbra.brueckmann-gmbh.de (Postfix) with ESMTP id 7B112CA5AF6; Tue, 14 May 2024 14:28:52 +0200 (CEST) Received: from zimbra.brueckmann-gmbh.de ([127.0.0.1]) by localhost (zimbra.brueckmann-gmbh.de [127.0.0.1]) (amavis, port 10026) with ESMTP id we7DMOYIUk4Z; Tue, 14 May 2024 14:28:52 +0200 (CEST) Received: from ew-linux.ew (unknown [10.0.11.14]) by zimbra.brueckmann-gmbh.de (Postfix) with ESMTPSA id 62877CA5AA8; Tue, 14 May 2024 14:28:52 +0200 (CEST) From: Thomas Gessler To: Andrew Lunn , Heiner Kallweit , Russell King , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Thomas Gessler , MD Danish Anwar , Ravi Gunasekaran Subject: [PATCH 2/2] net: phy: dp83869: Fix RGMII-SGMII and 1000BASE-X Date: Tue, 14 May 2024 14:27:28 +0200 Message-Id: <20240514122728.1490156-2-thomas.gessler@brueckmann-gmbh.de> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514122728.1490156-1-thomas.gessler@brueckmann-gmbh.de> References: <20240514122728.1490156-1-thomas.gessler@brueckmann-gmbh.de> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-purgate: clean X-purgate-type: clean X-purgate-ID: 151534::1715689734-04F54B7C-E8CD99F5/0/0 X-Patchwork-Delegate: kuba@kernel.org The PHY supports multiple modes of which not all are properly implemented by the driver. In the case of the RGMII-to-SGMII and 1000BASE-X modes, this was primarily due to the use of non-standard registers for auto-negotiation settings and link status. This patch adds device-specific get_features(), config_aneg(), aneg_done(), and read_status() functions for these modes. They are based on the genphy_* versions with the correct registers and fall back to the genphy_* versions for other modes. The RGMII-to-SGMII mode is special, because the chip does not really act as a PHY in this mode but rather as a bridge. It requires a connected SGMII PHY and gets the negotiated speed and duplex from it through SGMII auto-negotiation. To use the DP83869 as a virtual PHY, we assume that the connected SGMII PHY supports 10/100/1000M half/full duplex and therefore support and always advertise those settings. Signed-off-by: Thomas Gessler --- drivers/net/phy/dp83869.c | 391 +++++++++++++++++++++++++++++++++++++- 1 file changed, 387 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index d248a13c1749..cc7a9889829e 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -42,6 +42,9 @@ #define DP83869_IO_MUX_CFG 0x0170 #define DP83869_OP_MODE 0x01df #define DP83869_FX_CTRL 0x0c00 +#define DP83869_FX_STS 0x0c01 +#define DP83869_FX_ANADV 0x0c04 +#define DP83869_FX_LPABL 0x0c05 #define DP83869_SW_RESET BIT(15) #define DP83869_SW_RESTART BIT(14) @@ -116,6 +119,39 @@ #define DP83869_OP_MODE_MII BIT(5) #define DP83869_SGMII_RGMII_BRIDGE BIT(6) +/* FX_CTRL bits */ +#define DP83869_CTRL0_SPEED_SEL_MSB BIT(6) +#define DP83869_CTRL0_DUPLEX_MODE BIT(8) +#define DP83869_CTRL0_RESTART_AN BIT(9) +#define DP83869_CTRL0_ISOLATE BIT(10) +#define DP83869_CTRL0_PWRDN BIT(11) +#define DP83869_CTRL0_ANEG_EN BIT(12) +#define DP83869_CTRL0_SPEED_SEL_LSB BIT(13) +#define DP83869_CTRL0_LOOPBACK BIT(14) + +/* FX_STS bits */ +#define DP83869_STTS_LINK_STATUS BIT(2) +#define DP83869_STTS_ANEG_COMPLETE BIT(5) + +/* FX_ANADV bits */ +#define DP83869_BP_FULL_DUPLEX BIT(5) +#define DP83869_BP_HALF_DUPLEX BIT(6) +#define DP83869_BP_PAUSE BIT(7) +#define DP83869_BP_ASYMMETRIC_PAUSE BIT(8) + +/* FX_LPABL bits + * Bits 12:10 for RGMII-to-SGMII mode are undocumented and were determined + * through tests. It appears that, in this mode, the tx_config_Reg defined in + * the SGMII spec is copied to FX_LPABL after SGMII auto-negotiation. + */ +#define DP83869_LP_ABILITY_FULL_DUPLEX BIT(5) +#define DP83869_LP_ABILITY_PAUSE BIT(7) +#define DP83869_LP_ABILITY_ASYMMETRIC_PAUSE BIT(8) +#define DP83869_LP_ABILITY_SGMII_100 BIT(10) +#define DP83869_LP_ABILITY_SGMII_1000 BIT(11) +#define DP83869_LP_ABILITY_SGMII_DUPLEX BIT(12) +#define DP83869_LP_ABILITY_ACK BIT(14) + /* RXFCFG bits*/ #define DP83869_WOL_MAGIC_EN BIT(0) #define DP83869_WOL_PATTERN_EN BIT(1) @@ -154,19 +190,319 @@ struct dp83869_private { int mode; }; +static int dp83869_fx_setup_forced(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + u16 ctl = 0; + + phydev->pause = 0; + phydev->asym_pause = 0; + + if (dp83869->mode == DP83869_RGMII_1000_BASE) + ctl |= DP83869_CTRL0_SPEED_SEL_MSB; + + if (dp83869->mode == DP83869_RGMII_SGMII_BRIDGE) { + if (phydev->speed == SPEED_1000) + ctl |= DP83869_CTRL0_SPEED_SEL_MSB; + else if (phydev->speed == SPEED_100) + ctl |= DP83869_CTRL0_SPEED_SEL_LSB; + + /* Contrary to the data sheet, there is NO need to clear + * 10M_SGMII_RATE_ADAPT_DISABLE in 10M_SGMII_CFG for 10M SGMII. + */ + } + + if (phydev->duplex == DUPLEX_FULL) + ctl |= DP83869_CTRL0_DUPLEX_MODE; + + return phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_FX_CTRL, + ~(u16)(DP83869_CTRL0_LOOPBACK | + DP83869_CTRL0_ISOLATE | + DP83869_CTRL0_PWRDN), ctl); +} + +static int dp83869_fx_config_advert(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + int err, changed = 0; + u32 adv = 0; + + if (dp83869->mode == DP83869_RGMII_1000_BASE) { + linkmode_and(phydev->advertising, phydev->advertising, + phydev->supported); + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->advertising)) + adv |= DP83869_BP_FULL_DUPLEX; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, + phydev->advertising)) + adv |= DP83869_BP_PAUSE; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + phydev->advertising)) + adv |= DP83869_BP_ASYMMETRIC_PAUSE; + } + + if (dp83869->mode == DP83869_RGMII_SGMII_BRIDGE) { + /* As we cannot control the connected SGMII PHY, we force + * advertising all speeds. + */ + linkmode_copy(phydev->advertising, phydev->supported); + adv |= DP83869_BP_HALF_DUPLEX; + adv |= DP83869_BP_FULL_DUPLEX; + } + + err = phy_modify_mmd_changed(phydev, DP83869_DEVADDR, DP83869_FX_ANADV, + DP83869_BP_HALF_DUPLEX | + DP83869_BP_FULL_DUPLEX | DP83869_BP_PAUSE | + DP83869_BP_ASYMMETRIC_PAUSE, adv); + if (err < 0) + return err; + if (err > 0) + changed = 1; + + return changed; +} + +static int dp83869_fx_restart_aneg(struct phy_device *phydev) +{ + return phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_FX_CTRL, + DP83869_CTRL0_ISOLATE, DP83869_CTRL0_ANEG_EN | + DP83869_CTRL0_RESTART_AN); +} + +static int dp83869_fx_check_and_restart_aneg(struct phy_device *phydev, + bool restart) +{ + int ret; + + if (!restart) { + ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_FX_CTRL); + if (ret < 0) + return ret; + + if (!(ret & DP83869_CTRL0_ANEG_EN) || + (ret & DP83869_CTRL0_ISOLATE)) + restart = true; + } + + if (restart) + return dp83869_fx_restart_aneg(phydev); + + return 0; +} + +static int dp83869_config_aneg(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + bool changed = false; + int err; + + if (dp83869->mode != DP83869_RGMII_1000_BASE && + dp83869->mode != DP83869_RGMII_SGMII_BRIDGE) + return genphy_config_aneg(phydev); + + if (phydev->autoneg != AUTONEG_ENABLE) + return dp83869_fx_setup_forced(phydev); + + err = dp83869_fx_config_advert(phydev); + if (err < 0) + return err; + else if (err) + changed = true; + + return dp83869_fx_check_and_restart_aneg(phydev, changed); +} + +static int dp83869_aneg_done(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + + if (dp83869->mode == DP83869_RGMII_1000_BASE || + dp83869->mode == DP83869_RGMII_SGMII_BRIDGE) { + int retval = phy_read_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_STS); + + return (retval < 0) ? retval : + (retval & DP83869_STTS_ANEG_COMPLETE); + } else { + return genphy_aneg_done(phydev); + } +} + +static int dp83869_fx_update_link(struct phy_device *phydev) +{ + int status = 0, fx_ctrl; + + fx_ctrl = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_FX_CTRL); + if (fx_ctrl < 0) + return fx_ctrl; + + if (fx_ctrl & DP83869_CTRL0_RESTART_AN) + goto done; + + if (!phy_polling_mode(phydev) || !phydev->link) { + status = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_FX_STS); + if (status < 0) + return status; + else if (status & DP83869_STTS_LINK_STATUS) + goto done; + } + + status = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_FX_STS); + if (status < 0) + return status; + done: + phydev->link = status & DP83869_STTS_LINK_STATUS ? 1 : 0; + phydev->autoneg_complete = status & DP83869_STTS_ANEG_COMPLETE ? 1 : 0; + + if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) + phydev->link = 0; + + return 0; +} + +static int dp83869_1000basex_read_lpa(struct phy_device *phydev) +{ + int fx_lpabl; + + if (phydev->autoneg == AUTONEG_ENABLE) { + if (!phydev->autoneg_complete) { + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, + 0); + mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->lp_advertising, 0); + return 0; + } + + fx_lpabl = phy_read_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_LPABL); + if (fx_lpabl < 0) + return fx_lpabl; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->lp_advertising, + fx_lpabl & DP83869_LP_ABILITY_FULL_DUPLEX); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, + phydev->lp_advertising, + fx_lpabl & DP83869_LP_ABILITY_PAUSE); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + phydev->lp_advertising, + fx_lpabl & + DP83869_LP_ABILITY_ASYMMETRIC_PAUSE); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + phydev->lp_advertising, + fx_lpabl & DP83869_LP_ABILITY_ACK); + + } else { + linkmode_zero(phydev->lp_advertising); + } + + return 0; +} + +static int dp83869_fx_read_status_fixed(struct phy_device *phydev) +{ + int fx_ctrl = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_FX_CTRL); + + if (fx_ctrl < 0) + return fx_ctrl; + + if (fx_ctrl & DP83869_CTRL0_DUPLEX_MODE) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (fx_ctrl & DP83869_CTRL0_SPEED_SEL_MSB) + phydev->speed = SPEED_1000; + else if (fx_ctrl & DP83869_CTRL0_SPEED_SEL_LSB) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + return 0; +} + +static int dp83869_fx_read_status(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + int err, old_link = phydev->link; + + err = dp83869_fx_update_link(phydev); + if (err) + return err; + + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + return 0; + + phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED; + phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + + if (dp83869->mode == DP83869_RGMII_1000_BASE) { + err = dp83869_1000basex_read_lpa(phydev); + if (err < 0) + return err; + + if (phydev->autoneg == AUTONEG_ENABLE && + phydev->autoneg_complete) { + phy_resolve_aneg_linkmode(phydev); + } else if (phydev->autoneg == AUTONEG_DISABLE) { + err = dp83869_fx_read_status_fixed(phydev); + if (err < 0) + return err; + } + } else if (dp83869->mode == DP83869_RGMII_SGMII_BRIDGE) { + if (phydev->autoneg == AUTONEG_ENABLE && + phydev->autoneg_complete) { + int fx_lpabl; + + fx_lpabl = phy_read_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_LPABL); + if (fx_lpabl < 0) + return fx_lpabl; + + if (fx_lpabl & DP83869_LP_ABILITY_SGMII_1000) + phydev->speed = SPEED_1000; + else if (fx_lpabl & DP83869_LP_ABILITY_SGMII_100) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + if (fx_lpabl & DP83869_LP_ABILITY_SGMII_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + } else if (phydev->autoneg == AUTONEG_DISABLE) { + err = dp83869_fx_read_status_fixed(phydev); + if (err < 0) + return err; + } + } + + return 0; +} + static int dp83869_read_status(struct phy_device *phydev) { struct dp83869_private *dp83869 = phydev->priv; int ret; - ret = genphy_read_status(phydev); + if (dp83869->mode == DP83869_RGMII_1000_BASE || + dp83869->mode == DP83869_RGMII_SGMII_BRIDGE) + ret = dp83869_fx_read_status(phydev); + else + ret = genphy_read_status(phydev); + if (ret) return ret; - if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) { + if (dp83869->mode == DP83869_RGMII_100_BASE) { if (phydev->link) { - if (dp83869->mode == DP83869_RGMII_100_BASE) - phydev->speed = SPEED_100; + phydev->speed = SPEED_100; } else { phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; @@ -780,6 +1116,7 @@ static int dp83869_configure_mode(struct phy_device *phydev, break; case DP83869_RGMII_1000_BASE: + break; case DP83869_RGMII_100_BASE: ret = dp83869_configure_fiber(phydev, dp83869); break; @@ -874,6 +1211,49 @@ static int dp83869_probe(struct phy_device *phydev) return dp83869_config_init(phydev); } +static int dp83869_get_features(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + int err; + + err = genphy_read_abilities(phydev); + if (err) + return err; + + /* The PHY reports all speeds (10/100/1000BASE-T full/half-duplex and + * 1000BASE-X) as supported independent of the selected mode. We clear + * the settings that are nonsensical for each mode. + */ + + if (dp83869->mode == DP83869_RGMII_SGMII_BRIDGE) { + linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_TP_BIT, phydev->supported); + } + + if (dp83869->mode == DP83869_RGMII_1000_BASE) { + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_TP_BIT, phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_MII_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported); + } + + return 0; +} + static int dp83869_phy_reset(struct phy_device *phydev) { int ret; @@ -896,10 +1276,13 @@ static int dp83869_phy_reset(struct phy_device *phydev) PHY_ID_MATCH_MODEL(_id), \ .name = (_name), \ .probe = dp83869_probe, \ + .get_features = dp83869_get_features, \ .config_init = dp83869_config_init, \ .soft_reset = dp83869_phy_reset, \ .config_intr = dp83869_config_intr, \ .handle_interrupt = dp83869_handle_interrupt, \ + .config_aneg = dp83869_config_aneg, \ + .aneg_done = dp83869_aneg_done, \ .read_status = dp83869_read_status, \ .get_tunable = dp83869_get_tunable, \ .set_tunable = dp83869_set_tunable, \