From nobody Mon Jun 8 16:33:22 2026 Received: from rtits2.realtek.com.tw (rtits2.realtek.com [211.75.126.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E6AAF3546F1; Thu, 28 May 2026 07:54:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.75.126.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779954848; cv=none; b=B9A8mfFkZNMPnMEn4nEoAZorXiuZik32HFQ0vHTSfWB+BtUHCX48QC7cGXu+Kt+lKeILsdKSEtAzD2PEL4ZErrPj/tcJrnuzWm2kUvgDfGLeXWPfS74BBO9zYfqzmv3uA573TAKk5HQxUJyrdn0Laad1xgoaVayvKs+mk5j6Rvo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779954848; c=relaxed/simple; bh=P7qMu2Jn9cKZHjG86fuRMluqCeP24phFHg32tRypnbk=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=AR3x0LecrtAXuXm3OqBB3H1LR1cvuxNy9oX/mHtdu63FOwY1Qh8oyl9HD59z9BoLx5u9JTGrhk0yoxdOR7Gc42+TUOLEvm5b5P7EyxFn5MWGjRJnKkJkceqFrxbEWiPVdAbJLLwUzgU0WeBpxqxKTOOsiqSogf0s5vK80HN6dFE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=realsil.com.cn; spf=pass smtp.mailfrom=realsil.com.cn; dkim=pass (2048-bit key) header.d=realsil.com.cn header.i=@realsil.com.cn header.b=pDArlws8; arc=none smtp.client-ip=211.75.126.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=realsil.com.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=realsil.com.cn Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=realsil.com.cn header.i=@realsil.com.cn header.b="pDArlws8" X-SpamFilter-By: ArmorX SpamTrap 5.80 with qID 64S7rPTq93171895, This message is accepted by code: ctloc85258 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=realsil.com.cn; s=dkim; t=1779954806; bh=i0v7UdCz5ih9TJtCWsSynLCRG2gLpxggSD47L8joPao=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding:Content-Type; b=pDArlws8jqhJbfD0ElKjSKQCkBJo9RQY93w9ZXe6RSkkdHZf/++vtpGM73KxnBqcZ jPOlMoozhTronPMLgbA94q7Jl0dCtoy4Zp4ZnVgzMvZl3VHbSrsXLb+V2jTNvggrJQ 06JFPXmALgOHUeXIqI/OYEwKMPBcbOxcCSRPEbL+FdIWQV1vGuW+tLgLVPxreJyz6J M8xKCwROruAwSX2Ei5mRagasljIG3K9wGmgOPUHC7JDjUGSS/6a1lcc20hRLiOD+OI r37gi5onOSuWpUnqHQlDeiIJcedNvTTgXlFvRdrtuIQQGVBKtjmBYYmPql3uft94ta tCkqkimcZzboA== Received: from RS-EX-MBS2.realsil.com.cn ([172.29.17.102]) by rtits2.realtek.com.tw (8.15.2/3.28/5.94) with ESMTPS id 64S7rPTq93171895 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Thu, 28 May 2026 15:53:26 +0800 Received: from RS-EX-MBS2.realsil.com.cn (172.29.17.102) by RS-EX-MBS2.realsil.com.cn (172.29.17.102) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.17; Thu, 28 May 2026 15:53:25 +0800 Received: from 172.29.37.154 (172.29.37.152) by RS-EX-MBS2.realsil.com.cn (172.29.17.102) with Microsoft SMTP Server id 15.2.2562.17 via Frontend Transport; Thu, 28 May 2026 15:53:25 +0800 From: javen To: , , , , , , , CC: , , , , Javen Xu Subject: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 Date: Thu, 28 May 2026 15:52:25 +0800 Message-ID: <20260528075226.1054-2-javen_xu@realsil.com.cn> X-Mailer: git-send-email 2.50.1.windows.1 In-Reply-To: <20260528075226.1054-1-javen_xu@realsil.com.cn> References: <20260528075226.1054-1-javen_xu@realsil.com.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Javen Xu This patch adds support for Realtek phy chip RTL8261. Its PHY id is 0x001cc898 and 0x001cc899. Signed-off-by: Javen Xu --- drivers/net/phy/realtek/realtek_main.c | 315 +++++++++++++++++++++++++ 1 file changed, 315 insertions(+) diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realt= ek/realtek_main.c index 27268811f564..fe743fd0421b 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -22,6 +22,9 @@ #include "../phylib.h" #include "realtek.h" =20 +#define RTL_8261C_CG 0x001cc898 +#define RTL_8261CE_CG 0x001cc899 + #define RTL8201F_IER_PAGE 0x07 #define RTL8201F_IER 0x13 #define RTL8201F_IER_LINK BIT(13) @@ -141,6 +144,10 @@ #define RTL8211F_PHYSICAL_ADDR_WORD1 17 #define RTL8211F_PHYSICAL_ADDR_WORD2 18 =20 +#define RTL8261X_EXT_ADDR_REG 0xa436 +#define RTL8261X_EXT_DATA_REG 0xa438 +#define RTL_8261X_SUB_PHY_ID_ADDR 0x801d + #define RTL822X_VND1_SERDES_OPTION 0x697a #define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0) #define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0 @@ -252,6 +259,57 @@ #define RTL_8251B 0x001cc862 #define RTL_8261C 0x001cc890 =20 +#define RTL8261C_CE_MODEL 0x00 +#define RTL8261D_MODEL 0x81 +#define RTL8261X_PHYSR_REG 0xa434 +#define RTL8261X_GBCR_REG 0xa412 +#define RTL8261X_IMR 0xa4d2 +#define RTL8261X_ISR 0xa4d4 +#define RTL8261X_INT_AUTONEG_ERROR BIT(0) +#define RTL8261X_INT_PAGE_RECV BIT(2) +#define RTL8261X_INT_AUTONEG_DONE BIT(3) +#define RTL8261X_INT_LINK_CHG BIT(4) +#define RTL8261X_INT_PHY_REG_ACCESS BIT(5) +#define RTL8261X_INT_PME BIT(7) +#define RTL8261X_INT_ALDPS_CHG BIT(9) +#define RTL8261X_INT_JABBER BIT(10) +#define RTL8261X_PHYSR_LINK BIT(2) +#define RTL8261X_PHYSR_DUPLEX BIT(3) +#define RTL8261X_PHYSR_SPEED_L GENMASK(5, 4) +#define RTL8261X_PHYSR_SPEED_H GENMASK(10, 9) + +/* Concatenated 4-bit speed code values (SPD_H << 2 | SPD_L) */ +#define RTL8261X_SPEED_CODE_500M 0x3 /* H=3D0, L=3D3 */ +#define RTL8261X_SPEED_CODE_1000M 0x7 /* H=3D1, L=3D3 */ +#define RTL8261X_SPEED_CODE_2500M 0x8 /* H=3D2, L=3D0 */ +#define RTL8261X_SPEED_CODE_5000M 0x9 /* H=3D2, L=3D1 */ +#define RTL8261X_SPEED_500 500 + +#define RTL8261X_INT_MASK_DEFAULT (RTL8261X_INT_AUTONEG_DONE | \ + RTL8261X_INT_LINK_CHG) + +#define RTL8261X_INT_MASK_ALL (RTL8261X_INT_AUTONEG_ERROR | \ + RTL8261X_INT_PAGE_RECV | \ + RTL8261X_INT_AUTONEG_DONE | \ + RTL8261X_INT_LINK_CHG | \ + RTL8261X_INT_PHY_REG_ACCESS | \ + RTL8261X_INT_PME | \ + RTL8261X_INT_ALDPS_CHG | \ + RTL8261X_INT_JABBER) + +#define RTL8261X_MULTIG_CTRL 0x0020 +#define RTL8261X_MASTER_SLAVE_MASK GENMASK(15, 14) + +#define RTL8261X_MS_AUTO 0x0000 +#define RTL8261X_MS_SLAVE 0x8000 +#define RTL8261X_MS_MASTER 0xC000 + +enum rtl8261x_chip_model { + RTL8261_MODEL_C_CE =3D 0, + RTL8261_MODEL_D, + RTL8261_MODEL_GENERIC, +}; + /* RTL8211E and RTL8211F support up to three LEDs */ #define RTL8211x_LED_COUNT 3 =20 @@ -270,6 +328,12 @@ struct rtl821x_priv { u16 iner; }; =20 +struct rtl8261x_priv { + enum rtl8261x_chip_model model; + u8 sub_phy_id; + bool is_generic; +}; + static int rtl821x_read_page(struct phy_device *phydev) { return __phy_read(phydev, RTL821x_PAGE_SELECT); @@ -310,6 +374,233 @@ static int rtl821x_modify_ext_page(struct phy_device = *phydev, u16 ext_page, return phy_restore_page(phydev, oldpage, ret); } =20 +static int rtl8261x_soft_reset(struct phy_device *phydev) +{ + int ret, val; + + ret =3D phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, MDIO_CTRL1_= RESET); + if (ret < 0) + return ret; + + return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PMAPMD, + MDIO_CTRL1, val, + !(val & MDIO_CTRL1_RESET), + 5000, 100000, true); +} + +static int rtl8261x_probe(struct phy_device *phydev) +{ + struct device *dev =3D &phydev->mdio.dev; + struct rtl8261x_priv *priv; + int sub_phy_id, ret; + + priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG, + RTL_8261X_SUB_PHY_ID_ADDR); + if (ret < 0) + return ret; + ret =3D phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_DATA_REG); + if (ret < 0) + return ret; + + sub_phy_id =3D (ret >> 8) & 0xff; + priv->sub_phy_id =3D sub_phy_id; + priv->is_generic =3D false; + + switch (sub_phy_id) { + case RTL8261C_CE_MODEL: + priv->model =3D RTL8261_MODEL_C_CE; + phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", sub_phy_id= ); + break; + + case RTL8261D_MODEL: + priv->model =3D RTL8261_MODEL_D; + phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id); + break; + + default: + priv->model =3D RTL8261_MODEL_GENERIC; + priv->is_generic =3D true; + phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. Update d= river for full support.\n", + sub_phy_id); + break; + } + phydev->priv =3D priv; + + return 0; +} + +static int rtl8261x_get_features(struct phy_device *phydev) +{ + int ret; + + ret =3D genphy_c45_pma_read_abilities(phydev); + if (ret) + return ret; + /* + * Supplement Multi-Gig speeds that may not be automatically detected + * RTL8261X supports 2.5G/5G in addition to standard 10G + */ + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, + phydev->supported); + + return 0; +} + +static int rtl8261x_config_master_slave(struct phy_device *phydev) +{ + u16 val; + /* + * Configure bits 15:14 of MMD 7.0x0020 + * + * Bit 15 (Enable) | Bit 14 (Value) | Mode + * ----------------|----------------|------------- + * 0 | 0 | Auto (disabled) + * 1 | 0 | Force Slave + * 1 | 1 | Force Master + */ + switch (phydev->master_slave_set) { + case MASTER_SLAVE_CFG_MASTER_FORCE: + val =3D RTL8261X_MS_MASTER; + break; + case MASTER_SLAVE_CFG_SLAVE_FORCE: + val =3D RTL8261X_MS_SLAVE; + break; + case MASTER_SLAVE_CFG_UNKNOWN: + case MASTER_SLAVE_CFG_MASTER_PREFERRED: + case MASTER_SLAVE_CFG_SLAVE_PREFERRED: + default: + val =3D RTL8261X_MS_AUTO; + break; + } + + return phy_modify_mmd(phydev, MDIO_MMD_AN, RTL8261X_MULTIG_CTRL, + RTL8261X_MASTER_SLAVE_MASK, val); +} + +static int rtl8261x_config_intr(struct phy_device *phydev) +{ + int ret; + + if (phydev->interrupts =3D=3D PHY_INTERRUPT_ENABLED) { + ret =3D phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_ISR); + if (ret < 0) + return ret; + + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_IMR, + RTL8261X_INT_MASK_DEFAULT); + if (ret < 0) + return ret; + } else { + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_IMR, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static irqreturn_t rtl8261x_handle_interrupt(struct phy_device *phydev) +{ + int irq_status; + + irq_status =3D phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_ISR); + if (irq_status < 0) { + phy_error(phydev); + return IRQ_NONE; + } + + if (!(irq_status & RTL8261X_INT_MASK_ALL)) + return IRQ_NONE; + + if (irq_status & (RTL8261X_INT_LINK_CHG | RTL8261X_INT_AUTONEG_DONE | + RTL8261X_INT_AUTONEG_ERROR | RTL8261X_INT_JABBER)) + phy_trigger_machine(phydev); + + return IRQ_HANDLED; +} + +static int rtl8261x_read_status(struct phy_device *phydev) +{ + int ret, val, speed_code; + + ret =3D genphy_c45_read_status(phydev); + if (ret < 0) + return ret; + + val =3D phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_PHYSR_REG); + if (val < 0) + return val; + + phydev->link =3D !!(val & RTL8261X_PHYSR_LINK); + if (!phydev->link) { + phydev->speed =3D SPEED_UNKNOWN; + phydev->duplex =3D DUPLEX_UNKNOWN; + return 0; + } + + phydev->duplex =3D (val & RTL8261X_PHYSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_H= ALF; + speed_code =3D (FIELD_GET(RTL8261X_PHYSR_SPEED_H, val) << 2) | + FIELD_GET(RTL8261X_PHYSR_SPEED_L, val); + switch (speed_code) { + case RTL8261X_SPEED_CODE_500M: + phydev->speed =3D RTL8261X_SPEED_500; + break; + case RTL8261X_SPEED_CODE_1000M: + phydev->speed =3D SPEED_1000; + break; + case RTL8261X_SPEED_CODE_2500M: + phydev->speed =3D SPEED_2500; + break; + case RTL8261X_SPEED_CODE_5000M: + phydev->speed =3D SPEED_5000; + break; + default: + phydev_warn(phydev, "unknown speed code 0x%x (PHYSR=3D0x%04x)\n", speed_= code, val); + phydev->speed =3D SPEED_UNKNOWN; + break; + } + + return 0; +} + +static int rtl8261x_config_aneg(struct phy_device *phydev) +{ + u16 adv_1g =3D 0; + int ret; + + if (phydev->autoneg =3D=3D AUTONEG_DISABLE) + return genphy_c45_pma_setup_forced(phydev); + + ret =3D rtl8261x_config_master_slave(phydev); + if (ret < 0) + return ret; + + ret =3D genphy_c45_config_aneg(phydev); + if (ret < 0) + return ret; + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->advertising)) + adv_1g =3D BIT(9); + + ret =3D phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, + RTL8261X_GBCR_REG, + BIT(9), adv_1g); + if (ret < 0) + return ret; + + if (ret > 0) + return genphy_c45_restart_aneg(phydev); + + return 0; +} + static int rtl821x_probe(struct phy_device *phydev) { struct device *dev =3D &phydev->mdio.dev; @@ -3001,6 +3292,30 @@ static struct phy_driver realtek_drvs[] =3D { .resume =3D genphy_resume, .read_mmd =3D genphy_read_mmd_unsupported, .write_mmd =3D genphy_write_mmd_unsupported, + }, { + PHY_ID_MATCH_EXACT(RTL_8261C_CG), + .name =3D "Realtek RTL8261C_RTL8261D 10Gbps PHY", + .probe =3D rtl8261x_probe, + .get_features =3D rtl8261x_get_features, + .config_aneg =3D rtl8261x_config_aneg, + .read_status =3D rtl8261x_read_status, + .config_intr =3D rtl8261x_config_intr, + .handle_interrupt =3D rtl8261x_handle_interrupt, + .soft_reset =3D rtl8261x_soft_reset, + .suspend =3D genphy_c45_pma_suspend, + .resume =3D genphy_c45_pma_resume, + }, { + PHY_ID_MATCH_EXACT(RTL_8261CE_CG), + .name =3D "Realtek RTL8261CE 10Gbps PHY", + .probe =3D rtl8261x_probe, + .get_features =3D rtl8261x_get_features, + .config_aneg =3D rtl8261x_config_aneg, + .read_status =3D rtl8261x_read_status, + .config_intr =3D rtl8261x_config_intr, + .handle_interrupt =3D rtl8261x_handle_interrupt, + .soft_reset =3D rtl8261x_soft_reset, + .suspend =3D genphy_c45_pma_suspend, + .resume =3D genphy_c45_pma_resume, }, }; =20 --=20 2.43.0 From nobody Mon Jun 8 16:33:22 2026 Received: from rtits2.realtek.com.tw (rtits2.realtek.com [211.75.126.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0B9103546FB; Thu, 28 May 2026 07:54:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.75.126.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779954848; cv=none; b=diCVMJzWbo7svboSZ2eWu9JeBzh8KLJCwGA9A53r/mH2RT1VUoT4hAFrCHSzAJED6pw9FcHU+GAAoBBPq3NpJ+QCXvWVmY7LK6YKaVjV8fTI9b2DiUYmwU37S/cdmLmGAvRHspfc9pEwekcBONy5au4tBo+TpPiJLucrmA2dSbU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779954848; c=relaxed/simple; bh=uEo1Y/l/5mgCgbITW7t40b5ZWdmKIqZ5sCdvEHqwmmQ=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=u9VV+iju159nlMpWay6fdzg+1mHOWkHVTxJ/Exh5uG+hHDd69bKm+ciikznd6+rX5QlceVke54cmTFRb0bZd/cDVME4hAqqWwpAoazdsiABRfYEw7KIrvQDyS0XvUz2PDtq/DG6PlRfZxKgDLOr1bDqMxyQrAX9dv0BWbDPRpwM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=realsil.com.cn; spf=pass smtp.mailfrom=realsil.com.cn; dkim=pass (2048-bit key) header.d=realsil.com.cn header.i=@realsil.com.cn header.b=XepxTvSO; arc=none smtp.client-ip=211.75.126.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=realsil.com.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=realsil.com.cn Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=realsil.com.cn header.i=@realsil.com.cn header.b="XepxTvSO" X-SpamFilter-By: ArmorX SpamTrap 5.80 with qID 64S7rTB113171905, This message is accepted by code: ctloc85258 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=realsil.com.cn; s=dkim; t=1779954809; bh=HB/h7ITvi5hb4qCFCsAgNXRU83Usr6AZH9kColeDYdw=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding:Content-Type; b=XepxTvSOKsfTw33v0BKvg9Z2ocsuy4RZe1QqhY+GAX3Ka7iNTPSKvgM5huC7IMArW +exF5XWJmixxziETJhEJOGhRtiHQ1eSzMN27qHLxpUYBLZQQBHyop3/c1lzcJRcB6j TH38XxFX2wo2G3eSAxua27bodgrWcCJVIlgzAKpxlWC2yjlSWlz8dmycPiAadiIZtc ETD924FGJFr6BvjqoU6OFdPVME463ZHehZ2rF5sBAOrQf+4gOVwLe+jfB4P5BrFFmB LPsNp+mOEVKncjmqD+ELdiSh4WhCpjNo/7KJJOBVDfoyUe6owSuLYqQBDEkgu475kQ 0TKUm9zoapKsQ== Received: from RS-EX-MBS2.realsil.com.cn ([172.29.17.102]) by rtits2.realtek.com.tw (8.15.2/3.28/5.94) with ESMTPS id 64S7rTB113171905 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Thu, 28 May 2026 15:53:29 +0800 Received: from RS-EX-MBS2.realsil.com.cn (172.29.17.102) by RS-EX-MBS2.realsil.com.cn (172.29.17.102) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.17; Thu, 28 May 2026 15:53:28 +0800 Received: from 172.29.37.154 (172.29.37.152) by RS-EX-MBS2.realsil.com.cn (172.29.17.102) with Microsoft SMTP Server id 15.2.2562.17 via Frontend Transport; Thu, 28 May 2026 15:53:28 +0800 From: javen To: , , , , , , , CC: , , , , Javen Xu Subject: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C Date: Thu, 28 May 2026 15:52:26 +0800 Message-ID: <20260528075226.1054-3-javen_xu@realsil.com.cn> X-Mailer: git-send-email 2.50.1.windows.1 In-Reply-To: <20260528075226.1054-1-javen_xu@realsil.com.cn> References: <20260528075226.1054-1-javen_xu@realsil.com.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Javen Xu This patch adds support for loading firmware. Download some parameters for RTL8261C. Signed-off-by: Javen Xu --- drivers/net/phy/realtek/realtek_main.c | 221 +++++++++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realt= ek/realtek_main.c index fe743fd0421b..d20cdc68cc62 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -18,12 +18,51 @@ #include #include #include +#include +#include =20 #include "../phylib.h" #include "realtek.h" =20 #define RTL_8261C_CG 0x001cc898 #define RTL_8261CE_CG 0x001cc899 +#define FW_MAIN_MAGIC 0x52544C38 +#define FW_SUB_MAGIC_8261C 0x32363143 +#define FW_SUB_MAGIC_8261D 0x32363144 +#define RTL8261X_POLL_TIMEOUT_MS 100 + +#define RTL8261C_CE_FW_NAME "rtl_nic/rtl8261c.bin" +MODULE_FIRMWARE(RTL8261C_CE_FW_NAME); + +enum rtl8261x_fw_op { + OP_WRITE =3D 0x00, /* Write */ + OP_POLL =3D 0x02, /* Polling */ +}; + +struct rtl8261x_fw_header { + __le32 main_magic; /* Main magic number 0x52544C38 ("RTL8") */ + __le32 sub_magic; /* Sub magic number */ + __le16 version_major; /* Major version */ + __le16 version_minor; /* Minor version */ + __le16 num_entries; /* Number of entries */ + __le16 reserved; /* Reserved */ + __le32 crc32; /* CRC32 checksum */ +} __packed; + +struct rtl8261x_fw_entry { + __u8 type; /* Operation type (OP_*) */ + __u8 dev; /* MMD device */ + __le16 addr; /* Register address */ + __u8 msb; /* MSB bit position */ + __u8 lsb; /* LSB bit position */ + __le16 value; /* Value to write/compare */ + __le16 timeout_ms; /* Poll timeout in milliseconds */ + __u8 poll_set; /* Poll for set (1) or clear (0) */ + __u8 reserved; /* Reserved */ +} __packed; + +#define FW_HEADER_SIZE sizeof(struct rtl8261x_fw_header) +#define FW_ENTRY_SIZE sizeof(struct rtl8261x_fw_entry) =20 #define RTL8201F_IER_PAGE 0x07 #define RTL8201F_IER 0x13 @@ -332,6 +371,8 @@ struct rtl8261x_priv { enum rtl8261x_chip_model model; u8 sub_phy_id; bool is_generic; + const char *fw_name; + bool fw_loaded; }; =20 static int rtl821x_read_page(struct phy_device *phydev) @@ -413,16 +454,19 @@ static int rtl8261x_probe(struct phy_device *phydev) switch (sub_phy_id) { case RTL8261C_CE_MODEL: priv->model =3D RTL8261_MODEL_C_CE; + priv->fw_name =3D RTL8261C_CE_FW_NAME; phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", sub_phy_id= ); break; =20 case RTL8261D_MODEL: priv->model =3D RTL8261_MODEL_D; + priv->fw_name =3D NULL; phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id); break; =20 default: priv->model =3D RTL8261_MODEL_GENERIC; + priv->fw_name =3D NULL; priv->is_generic =3D true; phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. Update d= river for full support.\n", sub_phy_id); @@ -452,6 +496,165 @@ static int rtl8261x_get_features(struct phy_device *p= hydev) return 0; } =20 +static int rtl8261x_verify_firmware(struct phy_device *phydev, const struc= t firmware *fw) +{ + const struct rtl8261x_fw_header *hdr; + u32 calc_crc, file_crc; + size_t data_len; + u16 num_entries; + u32 main_magic, sub_magic; + + if (fw->size < FW_HEADER_SIZE) { + phydev_err(phydev, "Firmware too small: %zu bytes\n", fw->size); + return -EINVAL; + } + + hdr =3D (const struct rtl8261x_fw_header *)fw->data; + + main_magic =3D le32_to_cpu(hdr->main_magic); + if (main_magic !=3D FW_MAIN_MAGIC) { + phydev_err(phydev, "Invalid firmware magic: 0x%08x\n", main_magic); + return -EINVAL; + } + + sub_magic =3D le32_to_cpu(hdr->sub_magic); + if (sub_magic !=3D FW_SUB_MAGIC_8261C && sub_magic !=3D FW_SUB_MAGIC_8261= D) { + phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic); + return -EINVAL; + } + + num_entries =3D le16_to_cpu(hdr->num_entries); + data_len =3D num_entries * FW_ENTRY_SIZE; + + if (fw->size !=3D sizeof(*hdr) + data_len) { + phydev_err(phydev, "Firmware size mismatch\n"); + return -EINVAL; + } + + calc_crc =3D crc32(~0, fw->data + FW_HEADER_SIZE, data_len) ^ ~0; + file_crc =3D le32_to_cpu(hdr->crc32); + + if (calc_crc !=3D file_crc) { + phydev_err(phydev, "CRC32 mismatch: calculated=3D0x%08x file=3D0x%08x\n", + calc_crc, file_crc); + return -EINVAL; + } + + return 0; +} + +static int rtl_phy_write_mmd_bits(struct phy_device *phydev, int devnum, + u16 reg, u8 msb, u8 lsb, u16 val) +{ + int ret; + u32 reg_val; + + if (msb > 15 || lsb > msb) + return -EINVAL; + + ret =3D phy_read_mmd(phydev, devnum, reg); + if (ret < 0) + return ret; + reg_val =3D ret; + + reg_val &=3D ~GENMASK(msb, lsb); + reg_val |=3D (val << lsb) & GENMASK(msb, lsb); + + return phy_write_mmd(phydev, devnum, reg, reg_val); +} + +static int rtl8261x_fw_execute_entry(struct phy_device *phydev, + const struct rtl8261x_fw_entry *entry) +{ + u16 addr, value, timeout_ms; + u8 dev, msb, lsb, poll_set; + u32 bits, expect_val; + int ret =3D 0; + int val; + + dev =3D entry->dev; + addr =3D le16_to_cpu(entry->addr); + msb =3D entry->msb; + lsb =3D entry->lsb; + value =3D le16_to_cpu(entry->value); + timeout_ms =3D le16_to_cpu(entry->timeout_ms); + poll_set =3D entry->poll_set; + + if (timeout_ms =3D=3D 0) + timeout_ms =3D RTL8261X_POLL_TIMEOUT_MS; + + switch (entry->type) { + case OP_WRITE: + ret =3D rtl_phy_write_mmd_bits(phydev, dev, addr, msb, lsb, value); + if (ret) { + phydev_err(phydev, "WRITE failed: dev=3D%d addr=3D0x%04x\n", dev, addr); + return ret; + } + break; + + case OP_POLL: { + bits =3D GENMASK(msb, lsb); + expect_val =3D (value << lsb) & bits; + + if (poll_set) + ret =3D phy_read_mmd_poll_timeout(phydev, dev, addr, val, + (val & bits) =3D=3D expect_val, + 1000, timeout_ms * 1000, false); + else + ret =3D phy_read_mmd_poll_timeout(phydev, dev, addr, val, + (val & bits) !=3D expect_val, + 1000, timeout_ms * 1000, false); + if (ret) + phydev_err(phydev, "POLL timeout: dev=3D%d addr=3D0x%04x\n", + dev, addr); + break; + } + default: + phydev_err(phydev, "Unknown firmware operation: %d\n", entry->type); + ret =3D -EINVAL; + break; + } + + return ret; +} + +static int rtl8261x_fw_load(struct phy_device *phydev) +{ + struct rtl8261x_priv *priv =3D phydev->priv; + const struct rtl8261x_fw_entry *entry; + const struct rtl8261x_fw_header *hdr; + const struct firmware *fw; + int ret, i; + + if (!priv->fw_name) + return 0; + + ret =3D request_firmware(&fw, priv->fw_name, &phydev->mdio.dev); + if (ret) + return ret; + + ret =3D rtl8261x_verify_firmware(phydev, fw); + if (ret) + goto release_fw; + + hdr =3D (const struct rtl8261x_fw_header *)fw->data; + + entry =3D (const struct rtl8261x_fw_entry *)(fw->data + FW_HEADER_SIZE); + for (i =3D 0; i < le16_to_cpu(hdr->num_entries); i++, entry++) { + ret =3D rtl8261x_fw_execute_entry(phydev, entry); + if (ret) { + phydev_err(phydev, "Entry %d failed: %d\n", i, ret); + goto release_fw; + } + } + + priv->fw_loaded =3D true; + +release_fw: + release_firmware(fw); + return ret; +} + static int rtl8261x_config_master_slave(struct phy_device *phydev) { u16 val; @@ -601,6 +804,22 @@ static int rtl8261x_config_aneg(struct phy_device *phy= dev) return 0; } =20 +static int rtl8261x_config_init(struct phy_device *phydev) +{ + struct rtl8261x_priv *priv =3D phydev->priv; + int ret =3D 0; + + if (!priv->is_generic && !priv->fw_loaded) { + ret =3D rtl8261x_fw_load(phydev); + if (ret) { + phydev_err(phydev, "Firmware loading failed: %d\n", ret); + return ret; + } + } + + return ret; +} + static int rtl821x_probe(struct phy_device *phydev) { struct device *dev =3D &phydev->mdio.dev; @@ -3296,6 +3515,7 @@ static struct phy_driver realtek_drvs[] =3D { PHY_ID_MATCH_EXACT(RTL_8261C_CG), .name =3D "Realtek RTL8261C_RTL8261D 10Gbps PHY", .probe =3D rtl8261x_probe, + .config_init =3D rtl8261x_config_init, .get_features =3D rtl8261x_get_features, .config_aneg =3D rtl8261x_config_aneg, .read_status =3D rtl8261x_read_status, @@ -3308,6 +3528,7 @@ static struct phy_driver realtek_drvs[] =3D { PHY_ID_MATCH_EXACT(RTL_8261CE_CG), .name =3D "Realtek RTL8261CE 10Gbps PHY", .probe =3D rtl8261x_probe, + .config_init =3D rtl8261x_config_init, .get_features =3D rtl8261x_get_features, .config_aneg =3D rtl8261x_config_aneg, .read_status =3D rtl8261x_read_status, --=20 2.43.0