* [PATCH net-next v1 0/2] Add support for RTL8261c
@ 2026-05-28 7:52 javen
2026-05-28 7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
2026-05-28 7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
0 siblings, 2 replies; 13+ messages in thread
From: javen @ 2026-05-28 7:52 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu
Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
Add support for RTL8261c and add support for loading firmware.
Javen Xu (2):
net: phy: realtek: add support for RTL8261
net: phy: realtek: load firmware for RTL8261C
drivers/net/phy/realtek/realtek_main.c | 536 +++++++++++++++++++++++++
1 file changed, 536 insertions(+)
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
2026-05-28 7:52 [PATCH net-next v1 0/2] Add support for RTL8261c javen
@ 2026-05-28 7:52 ` javen
2026-05-28 9:37 ` Nicolai Buchwitz
` (3 more replies)
2026-05-28 7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
1 sibling, 4 replies; 13+ messages in thread
From: javen @ 2026-05-28 7:52 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu
Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds support for Realtek phy chip RTL8261. Its PHY id is
0x001cc898 and 0x001cc899.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
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/realtek/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"
+#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
+#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
+#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=0, L=3 */
+#define RTL8261X_SPEED_CODE_1000M 0x7 /* H=1, L=3 */
+#define RTL8261X_SPEED_CODE_2500M 0x8 /* H=2, L=0 */
+#define RTL8261X_SPEED_CODE_5000M 0x9 /* H=2, L=1 */
+#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 = 0,
+ RTL8261_MODEL_D,
+ RTL8261_MODEL_GENERIC,
+};
+
/* RTL8211E and RTL8211F support up to three LEDs */
#define RTL8211x_LED_COUNT 3
@@ -270,6 +328,12 @@ struct rtl821x_priv {
u16 iner;
};
+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);
}
+static int rtl8261x_soft_reset(struct phy_device *phydev)
+{
+ int ret, val;
+
+ ret = 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 = &phydev->mdio.dev;
+ struct rtl8261x_priv *priv;
+ int sub_phy_id, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
+ RTL_8261X_SUB_PHY_ID_ADDR);
+ if (ret < 0)
+ return ret;
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_DATA_REG);
+ if (ret < 0)
+ return ret;
+
+ sub_phy_id = (ret >> 8) & 0xff;
+ priv->sub_phy_id = sub_phy_id;
+ priv->is_generic = false;
+
+ switch (sub_phy_id) {
+ case RTL8261C_CE_MODEL:
+ priv->model = RTL8261_MODEL_C_CE;
+ phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", sub_phy_id);
+ break;
+
+ case RTL8261D_MODEL:
+ priv->model = RTL8261_MODEL_D;
+ phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id);
+ break;
+
+ default:
+ priv->model = RTL8261_MODEL_GENERIC;
+ priv->is_generic = true;
+ phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. Update driver for full support.\n",
+ sub_phy_id);
+ break;
+ }
+ phydev->priv = priv;
+
+ return 0;
+}
+
+static int rtl8261x_get_features(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = 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 = RTL8261X_MS_MASTER;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
+ val = RTL8261X_MS_SLAVE;
+ break;
+ case MASTER_SLAVE_CFG_UNKNOWN:
+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+ default:
+ val = 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 == PHY_INTERRUPT_ENABLED) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_ISR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_IMR,
+ RTL8261X_INT_MASK_DEFAULT);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = 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 = 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 = genphy_c45_read_status(phydev);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_PHYSR_REG);
+ if (val < 0)
+ return val;
+
+ phydev->link = !!(val & RTL8261X_PHYSR_LINK);
+ if (!phydev->link) {
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+ return 0;
+ }
+
+ phydev->duplex = (val & RTL8261X_PHYSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
+ speed_code = (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 = RTL8261X_SPEED_500;
+ break;
+ case RTL8261X_SPEED_CODE_1000M:
+ phydev->speed = SPEED_1000;
+ break;
+ case RTL8261X_SPEED_CODE_2500M:
+ phydev->speed = SPEED_2500;
+ break;
+ case RTL8261X_SPEED_CODE_5000M:
+ phydev->speed = SPEED_5000;
+ break;
+ default:
+ phydev_warn(phydev, "unknown speed code 0x%x (PHYSR=0x%04x)\n", speed_code, val);
+ phydev->speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int rtl8261x_config_aneg(struct phy_device *phydev)
+{
+ u16 adv_1g = 0;
+ int ret;
+
+ if (phydev->autoneg == AUTONEG_DISABLE)
+ return genphy_c45_pma_setup_forced(phydev);
+
+ ret = rtl8261x_config_master_slave(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = genphy_c45_config_aneg(phydev);
+ if (ret < 0)
+ return ret;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->advertising))
+ adv_1g = BIT(9);
+
+ ret = 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 = &phydev->mdio.dev;
@@ -3001,6 +3292,30 @@ static struct phy_driver realtek_drvs[] = {
.resume = genphy_resume,
.read_mmd = genphy_read_mmd_unsupported,
.write_mmd = genphy_write_mmd_unsupported,
+ }, {
+ PHY_ID_MATCH_EXACT(RTL_8261C_CG),
+ .name = "Realtek RTL8261C_RTL8261D 10Gbps PHY",
+ .probe = rtl8261x_probe,
+ .get_features = rtl8261x_get_features,
+ .config_aneg = rtl8261x_config_aneg,
+ .read_status = rtl8261x_read_status,
+ .config_intr = rtl8261x_config_intr,
+ .handle_interrupt = rtl8261x_handle_interrupt,
+ .soft_reset = rtl8261x_soft_reset,
+ .suspend = genphy_c45_pma_suspend,
+ .resume = genphy_c45_pma_resume,
+ }, {
+ PHY_ID_MATCH_EXACT(RTL_8261CE_CG),
+ .name = "Realtek RTL8261CE 10Gbps PHY",
+ .probe = rtl8261x_probe,
+ .get_features = rtl8261x_get_features,
+ .config_aneg = rtl8261x_config_aneg,
+ .read_status = rtl8261x_read_status,
+ .config_intr = rtl8261x_config_intr,
+ .handle_interrupt = rtl8261x_handle_interrupt,
+ .soft_reset = rtl8261x_soft_reset,
+ .suspend = genphy_c45_pma_suspend,
+ .resume = genphy_c45_pma_resume,
},
};
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
2026-05-28 7:52 [PATCH net-next v1 0/2] Add support for RTL8261c javen
2026-05-28 7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
@ 2026-05-28 7:52 ` javen
2026-05-28 9:08 ` Nicolai Buchwitz
` (2 more replies)
1 sibling, 3 replies; 13+ messages in thread
From: javen @ 2026-05-28 7:52 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu
Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds support for loading firmware. Download some parameters
for RTL8261C.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
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/realtek/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 <linux/clk.h>
#include <linux/string_choices.h>
#include <net/phy/realtek_phy.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
#include "../phylib.h"
#include "realtek.h"
#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 = 0x00, /* Write */
+ OP_POLL = 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)
#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;
};
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 = RTL8261_MODEL_C_CE;
+ priv->fw_name = RTL8261C_CE_FW_NAME;
phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", sub_phy_id);
break;
case RTL8261D_MODEL:
priv->model = RTL8261_MODEL_D;
+ priv->fw_name = NULL;
phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id);
break;
default:
priv->model = RTL8261_MODEL_GENERIC;
+ priv->fw_name = NULL;
priv->is_generic = true;
phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. Update driver for full support.\n",
sub_phy_id);
@@ -452,6 +496,165 @@ static int rtl8261x_get_features(struct phy_device *phydev)
return 0;
}
+static int rtl8261x_verify_firmware(struct phy_device *phydev, const struct 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 = (const struct rtl8261x_fw_header *)fw->data;
+
+ main_magic = le32_to_cpu(hdr->main_magic);
+ if (main_magic != FW_MAIN_MAGIC) {
+ phydev_err(phydev, "Invalid firmware magic: 0x%08x\n", main_magic);
+ return -EINVAL;
+ }
+
+ sub_magic = le32_to_cpu(hdr->sub_magic);
+ if (sub_magic != FW_SUB_MAGIC_8261C && sub_magic != FW_SUB_MAGIC_8261D) {
+ phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
+ return -EINVAL;
+ }
+
+ num_entries = le16_to_cpu(hdr->num_entries);
+ data_len = num_entries * FW_ENTRY_SIZE;
+
+ if (fw->size != sizeof(*hdr) + data_len) {
+ phydev_err(phydev, "Firmware size mismatch\n");
+ return -EINVAL;
+ }
+
+ calc_crc = crc32(~0, fw->data + FW_HEADER_SIZE, data_len) ^ ~0;
+ file_crc = le32_to_cpu(hdr->crc32);
+
+ if (calc_crc != file_crc) {
+ phydev_err(phydev, "CRC32 mismatch: calculated=0x%08x file=0x%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 = phy_read_mmd(phydev, devnum, reg);
+ if (ret < 0)
+ return ret;
+ reg_val = ret;
+
+ reg_val &= ~GENMASK(msb, lsb);
+ reg_val |= (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 = 0;
+ int val;
+
+ dev = entry->dev;
+ addr = le16_to_cpu(entry->addr);
+ msb = entry->msb;
+ lsb = entry->lsb;
+ value = le16_to_cpu(entry->value);
+ timeout_ms = le16_to_cpu(entry->timeout_ms);
+ poll_set = entry->poll_set;
+
+ if (timeout_ms == 0)
+ timeout_ms = RTL8261X_POLL_TIMEOUT_MS;
+
+ switch (entry->type) {
+ case OP_WRITE:
+ ret = rtl_phy_write_mmd_bits(phydev, dev, addr, msb, lsb, value);
+ if (ret) {
+ phydev_err(phydev, "WRITE failed: dev=%d addr=0x%04x\n", dev, addr);
+ return ret;
+ }
+ break;
+
+ case OP_POLL: {
+ bits = GENMASK(msb, lsb);
+ expect_val = (value << lsb) & bits;
+
+ if (poll_set)
+ ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
+ (val & bits) == expect_val,
+ 1000, timeout_ms * 1000, false);
+ else
+ ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
+ (val & bits) != expect_val,
+ 1000, timeout_ms * 1000, false);
+ if (ret)
+ phydev_err(phydev, "POLL timeout: dev=%d addr=0x%04x\n",
+ dev, addr);
+ break;
+ }
+ default:
+ phydev_err(phydev, "Unknown firmware operation: %d\n", entry->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rtl8261x_fw_load(struct phy_device *phydev)
+{
+ struct rtl8261x_priv *priv = 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 = request_firmware(&fw, priv->fw_name, &phydev->mdio.dev);
+ if (ret)
+ return ret;
+
+ ret = rtl8261x_verify_firmware(phydev, fw);
+ if (ret)
+ goto release_fw;
+
+ hdr = (const struct rtl8261x_fw_header *)fw->data;
+
+ entry = (const struct rtl8261x_fw_entry *)(fw->data + FW_HEADER_SIZE);
+ for (i = 0; i < le16_to_cpu(hdr->num_entries); i++, entry++) {
+ ret = rtl8261x_fw_execute_entry(phydev, entry);
+ if (ret) {
+ phydev_err(phydev, "Entry %d failed: %d\n", i, ret);
+ goto release_fw;
+ }
+ }
+
+ priv->fw_loaded = 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 *phydev)
return 0;
}
+static int rtl8261x_config_init(struct phy_device *phydev)
+{
+ struct rtl8261x_priv *priv = phydev->priv;
+ int ret = 0;
+
+ if (!priv->is_generic && !priv->fw_loaded) {
+ ret = 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 = &phydev->mdio.dev;
@@ -3296,6 +3515,7 @@ static struct phy_driver realtek_drvs[] = {
PHY_ID_MATCH_EXACT(RTL_8261C_CG),
.name = "Realtek RTL8261C_RTL8261D 10Gbps PHY",
.probe = rtl8261x_probe,
+ .config_init = rtl8261x_config_init,
.get_features = rtl8261x_get_features,
.config_aneg = rtl8261x_config_aneg,
.read_status = rtl8261x_read_status,
@@ -3308,6 +3528,7 @@ static struct phy_driver realtek_drvs[] = {
PHY_ID_MATCH_EXACT(RTL_8261CE_CG),
.name = "Realtek RTL8261CE 10Gbps PHY",
.probe = rtl8261x_probe,
+ .config_init = rtl8261x_config_init,
.get_features = rtl8261x_get_features,
.config_aneg = rtl8261x_config_aneg,
.read_status = rtl8261x_read_status,
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
2026-05-28 7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
@ 2026-05-28 9:08 ` Nicolai Buchwitz
2026-05-28 12:20 ` Daniel Golle
2026-05-29 1:01 ` Andrew Lunn
2 siblings, 0 replies; 13+ messages in thread
From: Nicolai Buchwitz @ 2026-05-28 9:08 UTC (permalink / raw)
To: javen
Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean
Hi Javen
On 28.5.2026 09:52, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This patch adds support for loading firmware. Download some parameters
> for RTL8261C.
Maybe you can add a bit more context to the commit message? From what I
can read in the code this also add other variant etc.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> 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/realtek/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 <linux/clk.h>
> #include <linux/string_choices.h>
> #include <net/phy/realtek_phy.h>
> +#include <linux/firmware.h>
> +#include <linux/crc32.h>
Move before net/ include
>
> #include "../phylib.h"
> #include "realtek.h"
>
> #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 = 0x00, /* Write */
> + OP_POLL = 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)
>
> #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;
> };
>
> 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 = RTL8261_MODEL_C_CE;
> + priv->fw_name = RTL8261C_CE_FW_NAME;
> phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n",
> sub_phy_id);
> break;
>
> case RTL8261D_MODEL:
> priv->model = RTL8261_MODEL_D;
> + priv->fw_name = NULL;
priv->fw_name is already initialized with NULL by devm_kzalloc
> phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n",
> sub_phy_id);
> break;
>
> default:
> priv->model = RTL8261_MODEL_GENERIC;
> + priv->fw_name = NULL;
priv->fw_name is already initialized with NULL by devm_kzalloc
> priv->is_generic = true;
> phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode.
> Update driver for full support.\n",
> sub_phy_id);
> @@ -452,6 +496,165 @@ static int rtl8261x_get_features(struct
> phy_device *phydev)
> return 0;
> }
>
> +static int rtl8261x_verify_firmware(struct phy_device *phydev, const
> struct 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;
Please re-order to match reverse x-mas
> +
> + if (fw->size < FW_HEADER_SIZE) {
> + phydev_err(phydev, "Firmware too small: %zu bytes\n", fw->size);
> + return -EINVAL;
> + }
> +
> + hdr = (const struct rtl8261x_fw_header *)fw->data;
> +
> + main_magic = le32_to_cpu(hdr->main_magic);
> + if (main_magic != FW_MAIN_MAGIC) {
> + phydev_err(phydev, "Invalid firmware magic: 0x%08x\n", main_magic);
> + return -EINVAL;
> + }
> +
> + sub_magic = le32_to_cpu(hdr->sub_magic);
> + if (sub_magic != FW_SUB_MAGIC_8261C && sub_magic !=
> FW_SUB_MAGIC_8261D) {
> + phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
> + return -EINVAL;
> + }
It checks for FW_SUB_MAGIC_8261D, but priv->fw_name is always NULL and
no firmwaer?
Is this a prepartion for a follow-up or am I missing something?
> +
> + num_entries = le16_to_cpu(hdr->num_entries);
> + data_len = num_entries * FW_ENTRY_SIZE;
> +
> + if (fw->size != sizeof(*hdr) + data_len) {
> + phydev_err(phydev, "Firmware size mismatch\n");
> + return -EINVAL;
> + }
> +
> + calc_crc = crc32(~0, fw->data + FW_HEADER_SIZE, data_len) ^ ~0;
> + file_crc = le32_to_cpu(hdr->crc32);
> +
> + if (calc_crc != file_crc) {
> + phydev_err(phydev, "CRC32 mismatch: calculated=0x%08x
> file=0x%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;
Please re-order to match reverse x-mas
> +
> + if (msb > 15 || lsb > msb)
> + return -EINVAL;
> +
> + ret = phy_read_mmd(phydev, devnum, reg);
> + if (ret < 0)
> + return ret;
> + reg_val = ret;
> +
> + reg_val &= ~GENMASK(msb, lsb);
> + reg_val |= (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 = 0;
> + int val;
> +
> + dev = entry->dev;
> + addr = le16_to_cpu(entry->addr);
> + msb = entry->msb;
> + lsb = entry->lsb;
> + value = le16_to_cpu(entry->value);
> + timeout_ms = le16_to_cpu(entry->timeout_ms);
> + poll_set = entry->poll_set;
> +
> + if (timeout_ms == 0)
> + timeout_ms = RTL8261X_POLL_TIMEOUT_MS;
> +
> + switch (entry->type) {
> + case OP_WRITE:
> + ret = rtl_phy_write_mmd_bits(phydev, dev, addr, msb, lsb, value);
> + if (ret) {
> + phydev_err(phydev, "WRITE failed: dev=%d addr=0x%04x\n", dev,
> addr);
> + return ret;
> + }
> + break;
> +
> + case OP_POLL: {
> + bits = GENMASK(msb, lsb);
> + expect_val = (value << lsb) & bits;
> +
> + if (poll_set)
> + ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
> + (val & bits) == expect_val,
> + 1000, timeout_ms * 1000, false);
> + else
> + ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
> + (val & bits) != expect_val,
> + 1000, timeout_ms * 1000, false);
> + if (ret)
> + phydev_err(phydev, "POLL timeout: dev=%d addr=0x%04x\n",
> + dev, addr);
> + break;
> + }
> + default:
> + phydev_err(phydev, "Unknown firmware operation: %d\n", entry->type);
> + ret = -EINVAL;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int rtl8261x_fw_load(struct phy_device *phydev)
> +{
> + struct rtl8261x_priv *priv = 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 = request_firmware(&fw, priv->fw_name, &phydev->mdio.dev);
> + if (ret)
> + return ret;
> +
> + ret = rtl8261x_verify_firmware(phydev, fw);
> + if (ret)
> + goto release_fw;
> +
> + hdr = (const struct rtl8261x_fw_header *)fw->data;
> +
> + entry = (const struct rtl8261x_fw_entry *)(fw->data +
> FW_HEADER_SIZE);
> + for (i = 0; i < le16_to_cpu(hdr->num_entries); i++, entry++) {
> + ret = rtl8261x_fw_execute_entry(phydev, entry);
> + if (ret) {
> + phydev_err(phydev, "Entry %d failed: %d\n", i, ret);
> + goto release_fw;
> + }
> + }
> +
> + priv->fw_loaded = 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
> *phydev)
> return 0;
> }
>
> +static int rtl8261x_config_init(struct phy_device *phydev)
> +{
> + struct rtl8261x_priv *priv = phydev->priv;
> + int ret = 0;
> +
> + if (!priv->is_generic && !priv->fw_loaded) {
> + ret = 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 = &phydev->mdio.dev;
> @@ -3296,6 +3515,7 @@ static struct phy_driver realtek_drvs[] = {
> PHY_ID_MATCH_EXACT(RTL_8261C_CG),
> .name = "Realtek RTL8261C_RTL8261D 10Gbps PHY",
> .probe = rtl8261x_probe,
> + .config_init = rtl8261x_config_init,
Use spaces before = to keep alignment of the rest.
> .get_features = rtl8261x_get_features,
> .config_aneg = rtl8261x_config_aneg,
> .read_status = rtl8261x_read_status,
> @@ -3308,6 +3528,7 @@ static struct phy_driver realtek_drvs[] = {
> PHY_ID_MATCH_EXACT(RTL_8261CE_CG),
> .name = "Realtek RTL8261CE 10Gbps PHY",
> .probe = rtl8261x_probe,
> + .config_init = rtl8261x_config_init,
Use spaces before = to keep alignment of the rest.
> .get_features = rtl8261x_get_features,
> .config_aneg = rtl8261x_config_aneg,
> .read_status = rtl8261x_read_status,
Nicolai
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
2026-05-28 7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
@ 2026-05-28 9:37 ` Nicolai Buchwitz
2026-05-29 0:51 ` Andrew Lunn
2026-05-28 12:39 ` Andrew Lunn
` (2 subsequent siblings)
3 siblings, 1 reply; 13+ messages in thread
From: Nicolai Buchwitz @ 2026-05-28 9:37 UTC (permalink / raw)
To: javen
Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean
Hi Javen
On 28.5.2026 09:52, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This patch adds support for Realtek phy chip RTL8261. Its PHY id is
> 0x001cc898 and 0x001cc899.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> 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/realtek/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"
>
> +#define RTL_8261C_CG 0x001cc898
> +#define RTL_8261CE_CG 0x001cc899
Imho these should be added below RTL_8261C and not at the top.
> +
> #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
>
> +#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
>
> +#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=0, L=3 */
> +#define RTL8261X_SPEED_CODE_1000M 0x7 /* H=1, L=3 */
> +#define RTL8261X_SPEED_CODE_2500M 0x8 /* H=2, L=0 */
> +#define RTL8261X_SPEED_CODE_5000M 0x9 /* H=2, L=1 */
> +#define RTL8261X_SPEED_500 500
500 MBit/s isnt a standard rate which also isnt represented in ethtool.
Is this just for documentation or intended to be used? If yes, how?
> [...]
> +
> +static int rtl8261x_get_features(struct phy_device *phydev)
> +{
> + int ret;
> +
> + ret = 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);
Feature bits for 2.5 and 5 GBit/s are set unconditionally - even for the
generic variant.
Do all variants have these capabilities? If not better add check for at
least the generic one.
> +
> + 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 = RTL8261X_MS_MASTER;
> + break;
> + case MASTER_SLAVE_CFG_SLAVE_FORCE:
> + val = RTL8261X_MS_SLAVE;
> + break;
> + case MASTER_SLAVE_CFG_UNKNOWN:
> + case MASTER_SLAVE_CFG_MASTER_PREFERRED:
> + case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
> + default:
> + val = RTL8261X_MS_AUTO;
> + break;
> + }
> +
> + return phy_modify_mmd(phydev, MDIO_MMD_AN, RTL8261X_MULTIG_CTRL,
> + RTL8261X_MASTER_SLAVE_MASK, val);
> +}
RTL8261X_MULTIG_CTRL is a IEEE-defined register, so it can use
MDIO_AN_10GBT_CTRL instead.
Also the define for RTL8261X_MULTIG_CTRL can be dropped.
While at it I would suggest to add MDIO_AN_10GBT_CTRL_MS_ENABLE (bit 15)
and
MDIO_AN_10GBT_CTRL_MS_VALUE (bit 14) to include/uapi/linux/mdio.h and
also make use of it.
RTL8261X_MS_AUTO would become
val = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;
> [...]
> +
> +static int rtl8261x_read_status(struct phy_device *phydev)
> +{
> + int ret, val, speed_code;
> +
> + ret = genphy_c45_read_status(phydev);
> + if (ret < 0)
> + return ret;
> +
> + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_PHYSR_REG);
> + if (val < 0)
> + return val;
> +
> + phydev->link = !!(val & RTL8261X_PHYSR_LINK);
> + if (!phydev->link) {
> + phydev->speed = SPEED_UNKNOWN;
> + phydev->duplex = DUPLEX_UNKNOWN;
> + return 0;
> + }
Why does it need to overreide the values from genphy_c45_read_status() ?
> +
> + phydev->duplex = (val & RTL8261X_PHYSR_DUPLEX) ? DUPLEX_FULL :
> DUPLEX_HALF;
> + speed_code = (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 = RTL8261X_SPEED_500;
See question above (defines).
> + break;
> + case RTL8261X_SPEED_CODE_1000M:
> + phydev->speed = SPEED_1000;
> + break;
> + case RTL8261X_SPEED_CODE_2500M:
> + phydev->speed = SPEED_2500;
> + break;
> + case RTL8261X_SPEED_CODE_5000M:
> + phydev->speed = SPEED_5000;
> + break;
> + default:
> + phydev_warn(phydev, "unknown speed code 0x%x (PHYSR=0x%04x)\n",
> speed_code, val);
> + phydev->speed = SPEED_UNKNOWN;
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int rtl8261x_config_aneg(struct phy_device *phydev)
> +{
> + u16 adv_1g = 0;
> + int ret;
> +
> + if (phydev->autoneg == AUTONEG_DISABLE)
> + return genphy_c45_pma_setup_forced(phydev);
> +
> + ret = rtl8261x_config_master_slave(phydev);
> + if (ret < 0)
> + return ret;
> +
> + ret = genphy_c45_config_aneg(phydev);
> + if (ret < 0)
> + return ret;
> +
> + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
> + phydev->advertising))
> + adv_1g = BIT(9);
> +
> + ret = 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);
This does not take rtl8261x_config_master_slave() into account. So maybe
change rtl8261x_config_master_slave
return phy_modify_mmd_changed ...
and keep the result in a bool and also trigger auto nego restart if
needed.
> +
> + return 0;
> +}
> +
> static int rtl821x_probe(struct phy_device *phydev)
> {
> struct device *dev = &phydev->mdio.dev;
> @@ -3001,6 +3292,30 @@ static struct phy_driver realtek_drvs[] = {
> .resume = genphy_resume,
> .read_mmd = genphy_read_mmd_unsupported,
> .write_mmd = genphy_write_mmd_unsupported,
> + }, {
> + PHY_ID_MATCH_EXACT(RTL_8261C_CG),
> + .name = "Realtek RTL8261C_RTL8261D 10Gbps PHY",
> + .probe = rtl8261x_probe,
RTL8261C/RTL8261D? Or just the c variant as the phy id suggests?
> + .get_features = rtl8261x_get_features,
> + .config_aneg = rtl8261x_config_aneg,
> + .read_status = rtl8261x_read_status,
> + .config_intr = rtl8261x_config_intr,
> + .handle_interrupt = rtl8261x_handle_interrupt,
> + .soft_reset = rtl8261x_soft_reset,
> + .suspend = genphy_c45_pma_suspend,
> + .resume = genphy_c45_pma_resume,
> + }, {
> + PHY_ID_MATCH_EXACT(RTL_8261CE_CG),
> + .name = "Realtek RTL8261CE 10Gbps PHY",
> + .probe = rtl8261x_probe,
> + .get_features = rtl8261x_get_features,
> + .config_aneg = rtl8261x_config_aneg,
> + .read_status = rtl8261x_read_status,
> + .config_intr = rtl8261x_config_intr,
> + .handle_interrupt = rtl8261x_handle_interrupt,
> + .soft_reset = rtl8261x_soft_reset,
> + .suspend = genphy_c45_pma_suspend,
> + .resume = genphy_c45_pma_resume,
> },
> };
Please also make sure for both patches that checkpatch has no further
complaints. Thanks.
Nicolai
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
2026-05-28 7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
2026-05-28 9:08 ` Nicolai Buchwitz
@ 2026-05-28 12:20 ` Daniel Golle
2026-06-01 5:38 ` Javen
2026-05-29 1:01 ` Andrew Lunn
2 siblings, 1 reply; 13+ messages in thread
From: Daniel Golle @ 2026-05-28 12:20 UTC (permalink / raw)
To: javen
Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, netdev, linux-kernel, vladimir.oltean,
Balázs Triszka
On Thu, May 28, 2026 at 03:52:26PM +0800, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This patch adds support for loading firmware. Download some parameters
> for RTL8261C.
I'd like to bring to your attention that the OpenWrt community
has impemented a more complete support for initial register patching
using a extremely similar firmware format.
https://github.com/openwrt/openwrt/blob/main/target/linux/generic/pending-6.18/742-net-phy-realtek-add-5G-and-10G-PHY-support.patch
We used 'RTK_PATCH_OP_*' while this series uses only 'OP_*' as
macro names defining the initval patching operations.
As the vendor downstream driver had the operations hard-coded in tables
our community came up with a tool to generate the patching blobs from the
GPL-licensed vendor driver:
https://github.com/balika011/realtek_phy_firmware
Given the similarity I wonder if this code has been taken from Balázs
Triszka work (which clearly predates it) without giving him the due
credit.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
2026-05-28 7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
2026-05-28 9:37 ` Nicolai Buchwitz
@ 2026-05-28 12:39 ` Andrew Lunn
2026-05-28 12:42 ` Andrew Lunn
2026-05-28 16:56 ` Aleksander Jan Bajkowski
3 siblings, 0 replies; 13+ messages in thread
From: Andrew Lunn @ 2026-05-28 12:39 UTC (permalink / raw)
To: javen
Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu,
netdev, linux-kernel, daniel, vladimir.oltean
> +static int rtl8261x_soft_reset(struct phy_device *phydev)
> +{
> + int ret, val;
> +
> + ret = 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);
We have a few copies of this function in various PHY drivers. Please
add a genphy_c45_soft_reset() helper.
> +}
> +
> +static int rtl8261x_probe(struct phy_device *phydev)
> +{
> + struct device *dev = &phydev->mdio.dev;
> + struct rtl8261x_priv *priv;
> + int sub_phy_id, ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
> + RTL_8261X_SUB_PHY_ID_ADDR);
> + if (ret < 0)
> + return ret;
blank line
> + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_DATA_REG);
> + if (ret < 0)
> + return ret;
> +
> + sub_phy_id = (ret >> 8) & 0xff;
> + priv->sub_phy_id = sub_phy_id;
> + priv->is_generic = false;
> +
> + switch (sub_phy_id) {
> + case RTL8261C_CE_MODEL:
> + priv->model = RTL8261_MODEL_C_CE;
> + phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", sub_phy_id);
> + break;
> +
> + case RTL8261D_MODEL:
> + priv->model = RTL8261_MODEL_D;
> + phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id);
> + break;
> +
> + default:
> + priv->model = RTL8261_MODEL_GENERIC;
> + priv->is_generic = true;
> + phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. Update driver for full support.\n",
> + sub_phy_id);
> + break;
This will get ignored. It is better to return -ENODEV so the probe
fails.
Andrew
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
2026-05-28 7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
2026-05-28 9:37 ` Nicolai Buchwitz
2026-05-28 12:39 ` Andrew Lunn
@ 2026-05-28 12:42 ` Andrew Lunn
2026-05-28 16:56 ` Aleksander Jan Bajkowski
3 siblings, 0 replies; 13+ messages in thread
From: Andrew Lunn @ 2026-05-28 12:42 UTC (permalink / raw)
To: javen
Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu,
netdev, linux-kernel, daniel, vladimir.oltean
> +struct rtl8261x_priv {
> + enum rtl8261x_chip_model model;
> + u8 sub_phy_id;
> + bool is_generic;
These don't appear to be used anywhere, just set. Are they really
needed?
Andrew
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
2026-05-28 7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
` (2 preceding siblings ...)
2026-05-28 12:42 ` Andrew Lunn
@ 2026-05-28 16:56 ` Aleksander Jan Bajkowski
3 siblings, 0 replies; 13+ messages in thread
From: Aleksander Jan Bajkowski @ 2026-05-28 16:56 UTC (permalink / raw)
To: javen, andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu
Cc: netdev, linux-kernel, daniel, vladimir.oltean
Hi Javen,
On 28/05/2026 09:52, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This patch adds support for Realtek phy chip RTL8261. Its PHY id is
> 0x001cc898 and 0x001cc899.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> 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/realtek/realtek_main.c
> index 27268811f564..fe743fd0421b 100644
> --- a/drivers/net/phy/realtek/realtek_main.c
> +++ b/drivers/net/phy/realtek/realtek_main.c
> +static int rtl8261x_config_aneg(struct phy_device *phydev)
> +{
> + u16 adv_1g = 0;
> + int ret;
> +
> + if (phydev->autoneg == AUTONEG_DISABLE)
> + return genphy_c45_pma_setup_forced(phydev);
> +
> + ret = rtl8261x_config_master_slave(phydev);
> + if (ret < 0)
> + return ret;
> +
> + ret = genphy_c45_config_aneg(phydev);
> + if (ret < 0)
> + return ret;
> +
> + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
> + phydev->advertising))
> + adv_1g = BIT(9);
adv_1g = ADVERTISE_1000FULL;
> +
> + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
> + RTL8261X_GBCR_REG,
> + BIT(9), adv_1g);
This is the C22 register mapped in VEND2 page. You can replace it with:
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
RTL822X_VND2_C22_REG(MII_CTRL1000 ),
ADVERTISE_1000FULL, adv_1g);
Best regards,
Aleksander
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
2026-05-28 9:37 ` Nicolai Buchwitz
@ 2026-05-29 0:51 ` Andrew Lunn
2026-06-03 2:35 ` Javen
0 siblings, 1 reply; 13+ messages in thread
From: Andrew Lunn @ 2026-05-29 0:51 UTC (permalink / raw)
To: Nicolai Buchwitz
Cc: javen, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean
> > +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 = RTL8261X_MS_MASTER;
> > + break;
> > + case MASTER_SLAVE_CFG_SLAVE_FORCE:
> > + val = RTL8261X_MS_SLAVE;
> > + break;
> > + case MASTER_SLAVE_CFG_UNKNOWN:
> > + case MASTER_SLAVE_CFG_MASTER_PREFERRED:
> > + case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
> > + default:
> > + val = RTL8261X_MS_AUTO;
> > + break;
> > + }
> > +
> > + return phy_modify_mmd(phydev, MDIO_MMD_AN, RTL8261X_MULTIG_CTRL,
> > + RTL8261X_MASTER_SLAVE_MASK, val);
> > +}
>
> RTL8261X_MULTIG_CTRL is a IEEE-defined register, so it can use
> MDIO_AN_10GBT_CTRL instead.
> Also the define for RTL8261X_MULTIG_CTRL can be dropped.
>
> While at it I would suggest to add MDIO_AN_10GBT_CTRL_MS_ENABLE (bit 15) and
> MDIO_AN_10GBT_CTRL_MS_VALUE (bit 14) to include/uapi/linux/mdio.h and also
> make use of it.
> RTL8261X_MS_AUTO would become
If this is all just standard 802.3, the function should be added to
phy-c45.c, so it can be used by any PHY driver.
Andrew
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
2026-05-28 7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
2026-05-28 9:08 ` Nicolai Buchwitz
2026-05-28 12:20 ` Daniel Golle
@ 2026-05-29 1:01 ` Andrew Lunn
2 siblings, 0 replies; 13+ messages in thread
From: Andrew Lunn @ 2026-05-29 1:01 UTC (permalink / raw)
To: javen
Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu,
netdev, linux-kernel, daniel, vladimir.oltean
> +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;
It looks like __packed should not be needed in both of
these. Everything is aligned so the compiler will naturally pack
them.
> + sub_magic = le32_to_cpu(hdr->sub_magic);
> + if (sub_magic != FW_SUB_MAGIC_8261C && sub_magic != FW_SUB_MAGIC_8261D) {
> + phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
> + return -EINVAL;
> + }
Should there be validation you are not trying to install C firmware on
D hardware? And visa versa?
> +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 = phy_read_mmd(phydev, devnum, reg);
> + if (ret < 0)
> + return ret;
> + reg_val = ret;
> +
> + reg_val &= ~GENMASK(msb, lsb);
> + reg_val |= (val << lsb) & GENMASK(msb, lsb);
The logic here is not very obvious. Please could you add some kdoc to
explain what this function does.
Andrew
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
2026-05-28 12:20 ` Daniel Golle
@ 2026-06-01 5:38 ` Javen
0 siblings, 0 replies; 13+ messages in thread
From: Javen @ 2026-06-01 5:38 UTC (permalink / raw)
To: Daniel Golle
Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
顾晓军,
netdev, linux-kernel, vladimir.oltean, Balázs Triszka
>On Thu, May 28, 2026 at 03:52:26PM +0800, javen wrote:
>> From: Javen Xu <javen_xu@realsil.com.cn>
>>
>> This patch adds support for loading firmware. Download some parameters
>> for RTL8261C.
>
>I'd like to bring to your attention that the OpenWrt community has
>impemented a more complete support for initial register patching using a
>extremely similar firmware format.
>
>https://github.com/openwrt/openwrt/blob/main/target/linux/generic/pendi
>ng-6.18/742-net-phy-realtek-add-5G-and-10G-PHY-support.patch
>
>We used 'RTK_PATCH_OP_*' while this series uses only 'OP_*' as macro
>names defining the initval patching operations.
>
>As the vendor downstream driver had the operations hard-coded in tables
>our community came up with a tool to generate the patching blobs from the
>GPL-licensed vendor driver:
>
>https://github.com/balika011/realtek_phy_firmware
>
>Given the similarity I wonder if this code has been taken from Balázs Triszka
>work (which clearly predates it) without giving him the due credit.
Thank you for bringing this to my attention and sharing the links to the OpenWrt community's and Balázs Triszka's work.
I have reviewed the repository and the patch you mentioned. I want to clarify that our code was developed independently. Since both implementations are tackling the exact same hardware requirements for Realtek PHYs, the fundamental hardware operations―such as read, write, mask, and delay―are strictly defined by the hardware. In this patch, we only kept some of the operations that are used, such as op_poll.
I hope this clears up the misunderstanding.
BRs,
Javen
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
2026-05-29 0:51 ` Andrew Lunn
@ 2026-06-03 2:35 ` Javen
0 siblings, 0 replies; 13+ messages in thread
From: Javen @ 2026-06-03 2:35 UTC (permalink / raw)
To: Andrew Lunn, Nicolai Buchwitz
Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni,
顾晓军,
netdev, linux-kernel, daniel, vladimir.oltean
>
>> > +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 = RTL8261X_MS_MASTER;
>> > + break;
>> > + case MASTER_SLAVE_CFG_SLAVE_FORCE:
>> > + val = RTL8261X_MS_SLAVE;
>> > + break;
>> > + case MASTER_SLAVE_CFG_UNKNOWN:
>> > + case MASTER_SLAVE_CFG_MASTER_PREFERRED:
>> > + case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
>> > + default:
>> > + val = RTL8261X_MS_AUTO;
>> > + break;
>> > + }
>> > +
>> > + return phy_modify_mmd(phydev, MDIO_MMD_AN,
>RTL8261X_MULTIG_CTRL,
>> > + RTL8261X_MASTER_SLAVE_MASK, val); }
>>
>> RTL8261X_MULTIG_CTRL is a IEEE-defined register, so it can use
>> MDIO_AN_10GBT_CTRL instead.
>> Also the define for RTL8261X_MULTIG_CTRL can be dropped.
>>
>> While at it I would suggest to add MDIO_AN_10GBT_CTRL_MS_ENABLE (bit
>> 15) and MDIO_AN_10GBT_CTRL_MS_VALUE (bit 14) to
>> include/uapi/linux/mdio.h and also make use of it.
>> RTL8261X_MS_AUTO would become
>
>If this is all just standard 802.3, the function should be added to phy-c45.c, so
>it can be used by any PHY driver.
>
> Andrew
Thanks for you suggestions. All the changes will be added into next version.
BRs,
Javen
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-06-03 2:35 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-28 7:52 [PATCH net-next v1 0/2] Add support for RTL8261c javen
2026-05-28 7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
2026-05-28 9:37 ` Nicolai Buchwitz
2026-05-29 0:51 ` Andrew Lunn
2026-06-03 2:35 ` Javen
2026-05-28 12:39 ` Andrew Lunn
2026-05-28 12:42 ` Andrew Lunn
2026-05-28 16:56 ` Aleksander Jan Bajkowski
2026-05-28 7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
2026-05-28 9:08 ` Nicolai Buchwitz
2026-05-28 12:20 ` Daniel Golle
2026-06-01 5:38 ` Javen
2026-05-29 1:01 ` Andrew Lunn
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®