| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/09 18:09 | flow | patch-triage |
5mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patched code is part of the Realtek RTL8365MB switch driver (drivers/net/dsa/realtek/rtl8365mb*), which drives physical Ethernet switch ASICs (RTL8365MB/RTL8367 family) connected via Device Tree over MDIO or SMI GPIO buses. Probe requires hardware communication to match specific chip IDs, and this hardware is not emulated in standard QEMU or GCE virtual environments. Therefore, the modified code is structurally unreachable for fuzzing.",
"WorthFuzzing": false
} |
| 1/1 | 2026/10/09 18:09 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit cab836bc4ba51e1fb0729e5b6440b455d284fefe\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Oct 9 18:09:00 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/dsa/realtek/rtl8365mb.h b/drivers/net/dsa/realtek/rtl8365mb.h\nnew file mode 100644\nindex 0000000000000..315de4c93c446\n--- /dev/null\n+++ b/drivers/net/dsa/realtek/rtl8365mb.h\n@@ -0,0 +1,14 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef _RTL8365MB_H\n+#define _RTL8365MB_H\n+\n+#include \"realtek.h\"\n+\n+enum rtl8365mb_family {\n+\tRTL8365MB_FAMILY_C,\n+\tRTL8365MB_FAMILY_D,\n+};\n+\n+enum rtl8365mb_family rtl8365mb_get_family(struct realtek_priv *priv);\n+\n+#endif /* _RTL8365MB_H */\ndiff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c\nindex da06f952a053d..a322c425ccf08 100644\n--- a/drivers/net/dsa/realtek/rtl8365mb_main.c\n+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c\n@@ -81,6 +81,7 @@\n * - RTL8367RB-VB\n * - RTL8367SB\n * - RTL8367S\n+ * - RTL8367S-VB\n * - RTL8370MB\n * - RTL8310SR\n *\n@@ -103,6 +104,7 @@\n #include \u003clinux/if_vlan.h\u003e\n #include \u003clinux/mdio.h\u003e\n #include \u003clinux/phylink.h\u003e\n+#include \u003clinux/workqueue.h\u003e\n \n #include \"realtek.h\"\n #include \"realtek-smi.h\"\n@@ -110,12 +112,14 @@\n #include \"rtl83xx.h\"\n #include \"rtl8365mb_l2.h\"\n #include \"rtl8365mb_vlan.h\"\n+#include \"rtl8365mb.h\"\n \n /* Family-specific data and limits */\n #define RTL8365MB_PHYADDRMAX\t\t7\n #define RTL8365MB_NUM_PHYREGS\t\t32\n #define RTL8365MB_PHYREGMAX\t\t(RTL8365MB_NUM_PHYREGS - 1)\n #define RTL8365MB_MAX_NUM_PORTS\t\t11\n+#define RTL8365MB_D_MAX_NUM_PORTS\t8\n /* Valid for the whole family except RTL8370B, which has 4160 entries.\n * RTL8370B is mentioned in vendor code but it might not even belong\n * to the same RTL8367C family.\n@@ -283,6 +287,7 @@\n #define RTL8365MB_SDS_INDACS_CMD_BUSY_MASK\tBIT(8)\n #define RTL8365MB_SDS_INDACS_CMD_RUN_MASK\tBIT(7)\n #define RTL8365MB_SDS_INDACS_CMD_WR_MASK\tBIT(6)\n+#define RTL8365MB_SDS_INDACS_CMD_INDEX_MASK\tGENMASK(5, 0)\n #define RTL8365MB_SDS_INDACS_ADR_REG\t\t0x6601\n #define RTL8365MB_SDS_INDACS_DATA_REG\t\t0x6602\n \n@@ -296,6 +301,23 @@\n #define RTL8365MB_SDS_MISC_SGMII_SPD_MASK\t\tGENMASK(8, 7)\n #define RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK\tBIT(6)\n \n+/* Re-latch retry interval and attempt budget. Each attempt parks the\n+ * SerDes for 20 ms, so a far end that never comes up must not be retried\n+ * at this rate forever, and the interval must not be so tight that\n+ * back-to-back parks starve a link that is about to latch. The count is\n+ * reset to 1 by pcs_config() and to 0 whenever the receiver latches;\n+ * attempts start at pcs_config(), well before the far end MAC is up, so\n+ * with the interval below the budget is roughly RELATCH_MAX_TRIES seconds,\n+ * generous enough to outlast a slow conduit.\n+ */\n+#define RTL8365MB_D_SDS_RELATCH_INTERVAL\t(HZ)\n+#define RTL8365MB_D_SDS_RELATCH_MAX_TRIES\t15\n+\n+/* How often a latched (or out-of-budget) SerDes is checked, and re-latched\n+ * if still down. Much slower than the re-latch interval.\n+ */\n+#define RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL\t(10 * HZ)\n+\n /* SerDes internal registers, accessed via the SDS_INDACS registers. The BMCR\n * data path reset holds BMCR_ANENABLE | BMCR_ISOLATE while toggling the\n * vendor-specific low bits from phase 1 to phase 2, which triggers a data path\n@@ -331,6 +353,27 @@\n #define RTL8365MB_SDS_OPTION_ARM_KEY\t\t0x0249\n #define RTL8365MB_SDS_OPTION_REG\t\t0x13C1\n \n+/* Family D uses the SDS13 indirect window for its MAC6 SerDes. */\n+#define RTL8365MB_D_SDS_EXT0_INDEX\t\t\t13\n+#define RTL8365MB_D_FIBER_CFG2_REG\t\t\t0x13E8\n+#define RTL8365MB_D_FIBER_CFG2_RX_DISABLE_MASK\tGENMASK(7, 6)\n+#define RTL8365MB_D_FIBER_CFG2_RX_DISABLE_SDS0\tBIT(6)\n+#define RTL8365MB_D_SDS_MISC_PA33PC_EN\t\t\tBIT(11)\n+#define RTL8365MB_D_SDS_MISC_PA12PC_EN\t\t\tBIT(10)\n+#define RTL8365MB_D_SDS_MISC_MAC6_SEL_SDS0\t\tBIT(9)\n+#define RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK\t\tGENMASK(4, 0)\n+#define RTL8365MB_D_SDS_MISC_MODE_SGMII\t\t\t0x02\n+#define RTL8365MB_D_SDS_MISC_MODE_HSGMII\t\t0x12\n+#define RTL8365MB_D_SDS_MISC_CFG_MASK\t\t\t\\\n+\t(RTL8365MB_D_SDS_MISC_PA33PC_EN |\t\t\\\n+\t RTL8365MB_D_SDS_MISC_PA12PC_EN |\t\t\\\n+\t RTL8365MB_D_SDS_MISC_MAC6_SEL_SDS0 |\t\t\\\n+\t RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK)\n+/* Shared \"disable\" encoding for both SDS_MISC's and SDS1_MISC0's\n+ * 5-bit mode fields.\n+ */\n+#define RTL8365MB_D_PORT_SDS_MODE_DISABLE\t\t0x1f\n+\n /* Embedded DW8051 microcontroller control registers. The microcontroller\n * can run firmware to manage the SerDes link, but this driver keeps it in\n * reset and disabled: phylink already performs the link management that\n@@ -343,6 +386,7 @@\n #define RTL8365MB_PORT_SPEED_10M\t0\n #define RTL8365MB_PORT_SPEED_100M\t1\n #define RTL8365MB_PORT_SPEED_1000M\t2\n+#define RTL8365MB_D_PORT_SPEED_2500M\t5\n \n /* External interface force configuration registers 0~2 */\n #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0\t\t0x1310 /* EXT0 */\n@@ -360,6 +404,20 @@\n #define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK\t\tBIT(4)\n #define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK\t\tBIT(2)\n #define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK\t\tGENMASK(1, 0)\n+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_WIDTH\t\t2 /* bits[1:0] */\n+\n+/* bits[13:12]; bit 13 reserved, no defined speed value uses it yet */\n+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_SPEED2_MASK\tGENMASK(13, 12)\n+\n+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG_BASE\t\t0x12c0\n+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG(_port) \\\n+\t\t(RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG_BASE + (_port))\n+\n+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_EN_BASE\t\t0x12c8\n+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG_EN(_port) \\\n+\t\t(RTL8365MB_D_DIGITAL_INTERFACE_FORCE_EN_BASE + (_port))\n+\n+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_EN_ALL_MASK\tGENMASK(15, 0)\n \n /* CPU port mask register - controls which ports are treated as CPU ports */\n #define RTL8365MB_CPU_PORT_MASK_REG\t0x1219\n@@ -441,6 +499,15 @@\n #define RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK\t\tGENMASK(5, 4)\n #define RTL8365MB_PORT_MISC_CFG_CONGESTION_SUSTAIN_TIME_MASK\tGENMASK(3, 0)\n \n+#define RTL8365MB_D_REG_EXT_TXC_DLY\t\t\t\t0x13f9\n+#define RTL8365MB_D_EXT1_RGMII_TX_DLY_MASK\t\t\tGENMASK(5, 3)\n+\n+#define RTL8365MB_D_REG_TOP_CON0\t\t\t\t0x1d70\n+#define RTL8365MB_D_MAC7_SEL_EXT1_MASK\t\t\tBIT(13)\n+#define RTL8365MB_D_MAC4_SEL_EXT1_MASK\t\t\tBIT(12)\n+\n+#define RTL8365MB_D_REG_SDS1_MISC0\t\t\t\t0x1d78\n+\n /**\n * enum rtl8365mb_vlan_egress_mode - port VLAN egress mode\n * @RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL: follow untag mask in VLAN4k table entry\n@@ -676,6 +743,21 @@ static const struct rtl8365mb_jam_tbl_entry rtl8365mb_sds_jam_hsgmii[] = {\n \t{ 0x0424, 0xD810 }, { 0x0001, 0x0F80 }, { 0x002E, 0x83F2 },\n };\n \n+/* Family D tuning tables from the Realtek vendor port API. */\n+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_d_sds_jam_sgmii[] = {\n+\t{ 0x0427, 0x4E0C }, { 0x0428, 0xAA00 }, { 0x0425, 0x5189 },\n+\t{ 0x0424, 0x8414 }, { 0x0423, 0x1020 }, { 0x0410, 0x0002 },\n+\t{ 0x0484, 0x011B }, { 0x0421, 0x8E13 }, { 0x0422, 0x1140 },\n+\t{ 0x0004, 0x074F },\n+};\n+\n+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_d_sds_jam_hsgmii[] = {\n+\t{ 0x0427, 0x4E0C }, { 0x0428, 0xAA00 }, { 0x0425, 0x5189 },\n+\t{ 0x0424, 0x8414 }, { 0x0423, 0x1020 }, { 0x0410, 0x0002 },\n+\t{ 0x0504, 0x051B }, { 0x0421, 0x8E13 }, { 0x0422, 0x1140 },\n+\t{ 0x0004, 0x074F },\n+};\n+\n enum rtl8365mb_phy_interface_mode {\n \tRTL8365MB_PHY_INTERFACE_MODE_INVAL = 0,\n \tRTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0),\n@@ -707,6 +789,7 @@ struct rtl8365mb_extint {\n * @name: human-readable chip name\n * @chip_id: chip identifier\n * @chip_ver: chip silicon revision\n+ * @family: chip family\n * @extints: available external interfaces\n * @jam_table: chip-specific initialization jam table\n * @jam_size: size of the chip's jam table\n@@ -719,6 +802,7 @@ struct rtl8365mb_chip_info {\n \tconst char *name;\n \tu32 chip_id;\n \tu32 chip_ver;\n+\tenum rtl8365mb_family family;\n \tconst struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS];\n \tconst struct rtl8365mb_jam_tbl_entry *jam_table;\n \tsize_t jam_size;\n@@ -731,6 +815,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {\n \t\t.name = \"RTL8365MB-VC\",\n \t\t.chip_id = 0x6367,\n \t\t.chip_ver = 0x0040,\n+\t\t.family = RTL8365MB_FAMILY_C,\n \t\t.extints = {\n \t\t\t{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |\n \t\t\t\tPHY_INTF(RMII) | PHY_INTF(RGMII) },\n@@ -742,6 +827,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {\n \t\t.name = \"RTL8367S\",\n \t\t.chip_id = 0x6367,\n \t\t.chip_ver = 0x00A0,\n+\t\t.family = RTL8365MB_FAMILY_C,\n \t\t.extints = {\n \t\t\t{ 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) },\n \t\t\t{ 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) |\n@@ -754,6 +840,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {\n \t\t.name = \"RTL8367SB\",\n \t\t.chip_id = 0x6367,\n \t\t.chip_ver = 0x0010,\n+\t\t.family = RTL8365MB_FAMILY_C,\n \t\t.extints = {\n \t\t\t{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |\n \t\t\t\tPHY_INTF(RMII) | PHY_INTF(RGMII) |\n@@ -768,6 +855,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {\n \t\t.name = \"RTL8367RB-VB\",\n \t\t.chip_id = 0x6367,\n \t\t.chip_ver = 0x0020,\n+\t\t.family = RTL8365MB_FAMILY_C,\n \t\t.extints = {\n \t\t\t{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |\n \t\t\t\tPHY_INTF(RMII) | PHY_INTF(RGMII) },\n@@ -777,6 +865,19 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {\n \t\t.jam_table = rtl8365mb_init_jam_8365mb_vc,\n \t\t.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),\n \t},\n+\t{\n+\t\t.name = \"RTL8367S-VB\",\n+\t\t.chip_id = 0x6642,\n+\t\t.chip_ver = 0x0010,\n+\t\t.family = RTL8365MB_FAMILY_D,\n+\t\t.extints = {\n+\t\t\t{ 6, 0, PHY_INTF(SGMII) | PHY_INTF(HSGMII) },\n+\t\t\t{ 7, 1, PHY_INTF(MII) | PHY_INTF(TMII) |\n+\t\t\t\tPHY_INTF(RMII) | PHY_INTF(RGMII) },\n+\t\t},\n+\t\t.jam_table = rtl8365mb_init_jam_8365mb_vc,\n+\t\t.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),\n+\t},\n };\n \n enum rtl8365mb_stp_state {\n@@ -867,13 +968,28 @@ struct rtl8365mb {\n \tconst struct rtl8365mb_chip_info *chip_info;\n \tstruct rtl8365mb_cpu cpu;\n \tstruct mutex mib_lock;\n+\t/* Serializes access to the shared SDS_INDACS ADR/CMD/DATA window and\n+\t * to RTL8365MB_SDS_MISC_REG, reachable both from phylink's PCS poll\n+\t * (mb-\u003epcs.poll) and from the family D SerDes re-latch work.\n+\t */\n+\tstruct mutex sds_lock;\n \tstruct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS];\n \tstruct phylink_pcs pcs;\n \tbool sds_supported;\n+\tstruct delayed_work sds_relatch;\t/* re-latch edges and link health check */\n+\tunsigned int sds_relatch_count;\t\t/* next attempt of the episode, 0 once latched */\n+\tu32 sds_misc_target_val;\n };\n \n #define pcs_to_rtl8365mb(_pcs) container_of((_pcs), struct rtl8365mb, pcs)\n \n+enum rtl8365mb_family rtl8365mb_get_family(struct realtek_priv *priv)\n+{\n+\tstruct rtl8365mb *mb = priv-\u003echip_data;\n+\n+\treturn mb-\u003echip_info-\u003efamily;\n+}\n+\n static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv)\n {\n \tu32 val;\n@@ -1164,6 +1280,7 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,\n \tstruct dsa_port *dp;\n \tint tx_delay = 0;\n \tint rx_delay = 0;\n+\tu32 data;\n \tu32 val;\n \tint ret;\n \n@@ -1233,43 +1350,85 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,\n \tif (ret)\n \t\treturn ret;\n \n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D \u0026\u0026 extint-\u003eid == 1) {\n+\t\tret = regmap_update_bits(priv-\u003emap,\n+\t\t\t\t\t RTL8365MB_D_REG_EXT_TXC_DLY,\n+\t\t\t\t\t RTL8365MB_D_EXT1_RGMII_TX_DLY_MASK, 0);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\t/* Configure RGMII/MII mux to port 7 if UTP_PORT4 is not RGMII mode */\n+\t\tret = regmap_read(priv-\u003emap, RTL8365MB_D_REG_TOP_CON0, \u0026data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tif ((data \u0026 RTL8365MB_D_MAC4_SEL_EXT1_MASK) == 0) {\n+\t\t\tret = regmap_update_bits(priv-\u003emap,\n+\t\t\t\t\t\t RTL8365MB_D_REG_TOP_CON0,\n+\t\t\t\t\t\t RTL8365MB_D_MAC7_SEL_EXT1_MASK,\n+\t\t\t\t\t\t RTL8365MB_D_MAC7_SEL_EXT1_MASK);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t\tret = regmap_update_bits(priv-\u003emap,\n+\t\t\t\t\t RTL8365MB_D_REG_SDS1_MISC0,\n+\t\t\t\t\t RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK,\n+\t\t\t\t\t RTL8365MB_D_PORT_SDS_MODE_DISABLE);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n \treturn 0;\n }\n \n-static int rtl8365mb_sds_write(struct realtek_priv *priv, u16 addr, u16 data)\n+static int rtl8365mb_sds_write(struct realtek_priv *priv, u8 index,\n+\t\t\t u16 addr, u16 data)\n {\n+\tstruct rtl8365mb *mb = priv-\u003echip_data;\n \tint ret;\n \n+\tmutex_lock(\u0026mb-\u003esds_lock);\n+\n \tret = regmap_write(priv-\u003emap, RTL8365MB_SDS_INDACS_DATA_REG, data);\n \tif (ret)\n-\t\treturn ret;\n+\t\tgoto out_unlock;\n \n \tret = regmap_write(priv-\u003emap, RTL8365MB_SDS_INDACS_ADR_REG, addr);\n \tif (ret)\n-\t\treturn ret;\n+\t\tgoto out_unlock;\n \n \t/* The SerDes indirect access engine completes the command within the\n \t * register write transaction, so there is no need to wait or poll for\n \t * completion before the next access, matching the vendor driver.\n \t */\n-\treturn regmap_write(priv-\u003emap, RTL8365MB_SDS_INDACS_CMD_REG,\n-\t\t\t RTL8365MB_SDS_INDACS_CMD_RUN_MASK |\n-\t\t\t RTL8365MB_SDS_INDACS_CMD_WR_MASK);\n+\tret = regmap_write(priv-\u003emap, RTL8365MB_SDS_INDACS_CMD_REG,\n+\t\t\t RTL8365MB_SDS_INDACS_CMD_RUN_MASK |\n+\t\t\t RTL8365MB_SDS_INDACS_CMD_WR_MASK |\n+\t\t\t FIELD_PREP(RTL8365MB_SDS_INDACS_CMD_INDEX_MASK,\n+\t\t\t\t index));\n+\n+out_unlock:\n+\tmutex_unlock(\u0026mb-\u003esds_lock);\n+\treturn ret;\n }\n \n-static int rtl8365mb_sds_read(struct realtek_priv *priv, u16 addr, u16 *data)\n+static int rtl8365mb_sds_read(struct realtek_priv *priv, u8 index,\n+\t\t\t u16 addr, u16 *data)\n {\n+\tstruct rtl8365mb *mb = priv-\u003echip_data;\n \tu32 val;\n \tint ret;\n \n+\tmutex_lock(\u0026mb-\u003esds_lock);\n+\n \tret = regmap_write(priv-\u003emap, RTL8365MB_SDS_INDACS_ADR_REG, addr);\n \tif (ret)\n-\t\treturn ret;\n+\t\tgoto out_unlock;\n \n \tret = regmap_write(priv-\u003emap, RTL8365MB_SDS_INDACS_CMD_REG,\n-\t\t\t RTL8365MB_SDS_INDACS_CMD_RUN_MASK);\n+\t\t\t RTL8365MB_SDS_INDACS_CMD_RUN_MASK |\n+\t\t\t FIELD_PREP(RTL8365MB_SDS_INDACS_CMD_INDEX_MASK,\n+\t\t\t\t index));\n \tif (ret)\n-\t\treturn ret;\n+\t\tgoto out_unlock;\n \n \t/* Wait for the indirect read to complete: the engine clears the BUSY\n \t * bit once the data register holds the result.\n@@ -1279,15 +1438,19 @@ static int rtl8365mb_sds_read(struct realtek_priv *priv, u16 addr, u16 *data)\n \t\t\t\t !(val \u0026 RTL8365MB_SDS_INDACS_CMD_BUSY_MASK),\n \t\t\t\t 10, 1000);\n \tif (ret)\n-\t\treturn ret;\n+\t\tgoto out_unlock;\n \n \tret = regmap_read(priv-\u003emap, RTL8365MB_SDS_INDACS_DATA_REG, \u0026val);\n \tif (ret)\n-\t\treturn ret;\n+\t\tgoto out_unlock;\n \n \t*data = val;\n \n-\treturn 0;\n+\tret = 0;\n+\n+out_unlock:\n+\tmutex_unlock(\u0026mb-\u003esds_lock);\n+\treturn ret;\n }\n \n /* The vendor driver selects between two sets of SerDes tuning parameters based\n@@ -1307,6 +1470,14 @@ static int rtl8365mb_sds_probe_option(struct realtek_priv *priv)\n \tint ret;\n \tint i;\n \n+\t/* Family D has a fixed SDS13 programming model and does not use the\n+\t * family C option register to select its tuning table.\n+\t */\n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D) {\n+\t\tmb-\u003esds_supported = true;\n+\t\treturn 0;\n+\t}\n+\n \t/* Nothing to probe if no external interface is wired to the SerDes */\n \tfor (i = 0; i \u003c RTL8365MB_MAX_NUM_EXTINTS; i++) {\n \t\textint = \u0026mb-\u003echip_info-\u003eextints[i];\n@@ -1385,28 +1556,54 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,\n \t\t\t\tconst unsigned long *advertising,\n \t\t\t\tbool permit_pause_to_mac)\n {\n-\tconst struct rtl8365mb_jam_tbl_entry *sds_jam;\n \tconst int id = RTL8365MB_SDS_EXT_INTERFACE_ID;\n+\tconst struct rtl8365mb_jam_tbl_entry *sds_jam;\n \tstruct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);\n-\tstruct realtek_priv *priv;\n+\tstruct realtek_priv *priv = mb-\u003epriv;\n \tsize_t sds_jam_size;\n-\tu32 mode;\n+\tu32 misc_mask;\n+\tu32 misc_val;\n+\tu32 sds_mode;\n+\tu8 sds_index;\n+\tbool is_d;\n \tu16 val;\n \tint ret;\n \tint i;\n \n-\tpriv = mb-\u003epriv;\n+\tis_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;\n \n+\t/* Cancel any in-flight re-latch edge before touching SDS_MISC: the\n+\t * work drops sds_lock across its sleep and could otherwise interleave\n+\t * with the reconfiguration below.\n+\t */\n+\tif (is_d)\n+\t\tcancel_delayed_work_sync(\u0026mb-\u003esds_relatch);\n+\n+\t/* Select the appropriate tuning table and SDS mode */\n \tif (interface == PHY_INTERFACE_MODE_2500BASEX) {\n-\t\tsds_jam = rtl8365mb_sds_jam_hsgmii;\n-\t\tsds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_hsgmii);\n-\t\tmode = RTL8365MB_EXT_PORT_MODE_HSGMII;\n+\t\tif (is_d) {\n+\t\t\tsds_jam = rtl8365mb_d_sds_jam_hsgmii;\n+\t\t\tsds_jam_size = ARRAY_SIZE(rtl8365mb_d_sds_jam_hsgmii);\n+\t\t\tsds_mode = RTL8365MB_D_SDS_MISC_MODE_HSGMII;\n+\t\t} else {\n+\t\t\tsds_jam = rtl8365mb_sds_jam_hsgmii;\n+\t\t\tsds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_hsgmii);\n+\t\t\tsds_mode = RTL8365MB_EXT_PORT_MODE_HSGMII;\n+\t\t}\n \t} else {\n-\t\tsds_jam = rtl8365mb_sds_jam_sgmii;\n-\t\tsds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_sgmii);\n-\t\tmode = RTL8365MB_EXT_PORT_MODE_SGMII;\n+\t\tif (is_d) {\n+\t\t\tsds_jam = rtl8365mb_d_sds_jam_sgmii;\n+\t\t\tsds_jam_size = ARRAY_SIZE(rtl8365mb_d_sds_jam_sgmii);\n+\t\t\tsds_mode = RTL8365MB_D_SDS_MISC_MODE_SGMII;\n+\t\t} else {\n+\t\t\tsds_jam = rtl8365mb_sds_jam_sgmii;\n+\t\t\tsds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_sgmii);\n+\t\t\tsds_mode = RTL8365MB_EXT_PORT_MODE_SGMII;\n+\t\t}\n \t}\n \n+\tsds_index = is_d ? RTL8365MB_D_SDS_EXT0_INDEX : 0;\n+\n \t/* Hold the embedded DW8051 microcontroller in reset and keep it\n \t * disabled. The vendor driver loads firmware into it to manage the\n \t * SerDes link, but the firmware only duplicates work that phylink\n@@ -1435,34 +1632,57 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,\n \n \t/* Tune the SerDes with vendor-prescribed parameters */\n \tfor (i = 0; i \u003c sds_jam_size; i++) {\n-\t\tret = rtl8365mb_sds_write(priv, sds_jam[i].reg,\n-\t\t\t\t\t sds_jam[i].val);\n+\t\tret = rtl8365mb_sds_write(priv, sds_index,\n+\t\t\t\t\t sds_jam[i].reg, sds_jam[i].val);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\t/* Family-specific post-tuning configuration */\n+\tif (is_d) {\n+\t\tret = regmap_update_bits(priv-\u003emap, RTL8365MB_D_FIBER_CFG2_REG,\n+\t\t\t\t\t RTL8365MB_D_FIBER_CFG2_RX_DISABLE_MASK,\n+\t\t\t\t\t RTL8365MB_D_FIBER_CFG2_RX_DISABLE_SDS0);\n \t\tif (ret)\n \t\t\treturn ret;\n+\n+\t\tmisc_mask = RTL8365MB_D_SDS_MISC_CFG_MASK;\n+\t\tmisc_val = RTL8365MB_D_SDS_MISC_PA33PC_EN |\n+\t\t\t RTL8365MB_D_SDS_MISC_PA12PC_EN |\n+\t\t\t RTL8365MB_D_SDS_MISC_MAC6_SEL_SDS0 | sds_mode;\n+\t} else {\n+\t\t/* Mux the SerDes to MAC8 in the requested mode */\n+\t\tmisc_mask = RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK |\n+\t\t\t RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK;\n+\t\tmisc_val = (sds_mode == RTL8365MB_EXT_PORT_MODE_SGMII) ?\n+\t\t\t RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK :\n+\t\t\t RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK;\n \t}\n \n-\t/* Mux the SerDes to MAC8 in the requested mode */\n+\tmutex_lock(\u0026mb-\u003esds_lock);\n \tret = regmap_update_bits(priv-\u003emap, RTL8365MB_SDS_MISC_REG,\n-\t\t\t\t RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK |\n-\t\t\t\t\t RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK,\n-\t\t\t\t mode == RTL8365MB_EXT_PORT_MODE_SGMII ?\n-\t\t\t\t\t RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK :\n-\t\t\t\t\t RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK);\n+\t\t\t\t misc_mask, misc_val);\n+\tmutex_unlock(\u0026mb-\u003esds_lock);\n \tif (ret)\n \t\treturn ret;\n \n-\tval = mode \u003c\u003c RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);\n-\tret = regmap_update_bits(priv-\u003emap,\n-\t\t\t\t RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id),\n-\t\t\t\t RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),\n-\t\t\t\t val);\n-\tif (ret)\n-\t\treturn ret;\n+\tif (is_d) {\n+\t\tWRITE_ONCE(mb-\u003esds_misc_target_val, misc_val);\n+\t} else {\n+\t\tval = sds_mode \u003c\u003c RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);\n+\t\tret = regmap_update_bits(priv-\u003emap,\n+\t\t\t\t\t RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id),\n+\t\t\t\t\t RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),\n+\t\t\t\t\t val);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n \n \t/* Take the SerDes out of reset. The vendor driver does this only\n \t * after the SerDes mux and the interface mode are configured.\n \t */\n-\tret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_RESET,\n+\tret = rtl8365mb_sds_write(priv, sds_index,\n+\t\t\t\t RTL8365MB_SDS_REG_RESET,\n \t\t\t\t RTL8365MB_SDS_RESET_DEASSERT);\n \tif (ret)\n \t\treturn ret;\n@@ -1472,12 +1692,14 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,\n \t * This flushes the FIFOs and ensures a clean state for the link,\n \t * preventing silent drops and CRC errors.\n \t */\n-\tret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,\n+\tret = rtl8365mb_sds_write(priv, sds_index,\n+\t\t\t\t RTL8365MB_SDS_REG_BMCR,\n \t\t\t\t RTL8365MB_SDS_BMCR_DPRST_PHASE1);\n \tif (ret)\n \t\treturn ret;\n \n-\tret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,\n+\tret = rtl8365mb_sds_write(priv, sds_index,\n+\t\t\t\t RTL8365MB_SDS_REG_BMCR,\n \t\t\t\t RTL8365MB_SDS_BMCR_DPRST_PHASE2);\n \tif (ret)\n \t\treturn ret;\n@@ -1485,14 +1707,148 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,\n \t/* Keep SGMII in-band autonegotiation disabled: the link parameters are\n \t * forced from rtl8365mb_pcs_link_up() instead.\n \t */\n-\tret = rtl8365mb_sds_read(priv, RTL8365MB_SDS_REG_NWAY, \u0026val);\n+\tret = rtl8365mb_sds_read(priv, sds_index,\n+\t\t\t\t RTL8365MB_SDS_REG_NWAY, \u0026val);\n \tif (ret)\n \t\treturn ret;\n \n \tval \u0026= ~RTL8365MB_SDS_NWAY_EN_MASK;\n \tval |= RTL8365MB_SDS_NWAY_RESTART_MASK;\n \n-\treturn rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_NWAY, val);\n+\tret = rtl8365mb_sds_write(priv, sds_index,\n+\t\t\t\t RTL8365MB_SDS_REG_NWAY, val);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (is_d) {\n+\t\t/* Start a new re-latch episode and kick off the first attempt;\n+\t\t * see rtl8365mb_sds_relatch_work() for why this cannot wait\n+\t\t * for phylink to ask again.\n+\t\t */\n+\t\tWRITE_ONCE(mb-\u003esds_relatch_count, 1);\n+\t\tschedule_delayed_work(\u0026mb-\u003esds_relatch, 0);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* The family D receiver latches on a DISABLE -\u003e mode edge in SDS_MISC\n+ * rather than on the value, and only once the far-end MAC has brought up\n+ * its half of the link. No local signal predicts that (bit 8 of the SDS\n+ * link-status word does not change ahead of it), so the work cannot wait\n+ * for it, only check afterwards whether an edge took. Nor does phylink\n+ * ever call rtl8365mb_pcs_get_state() for this fixed-link port\n+ * (mb-\u003epcs.poll only arms phylink's own poll for in-band links), so this\n+ * cannot rely on being polled at all. Instead, pcs_config() starts an\n+ * episode and this work re-arms itself after every edge, its next run\n+ * checking whether the edge took.\n+ *\n+ * An episode is capped at RTL8365MB_D_SDS_RELATCH_MAX_TRIES edges, spaced\n+ * RTL8365MB_D_SDS_RELATCH_INTERVAL apart, so that a far end which never\n+ * comes up costs a bounded burst of 20 ms parks. The run after the last\n+ * edge only checks whether it took and reports failure; from then on one\n+ * edge is driven per RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL, so a far end\n+ * that comes up late is recovered without a reconfiguration. Once the\n+ * receiver has latched the work just polls the link at the same slow\n+ * interval; finding it down then starts a new episode, e.g. after the far\n+ * end reset its PLL without a reconfiguration.\n+ *\n+ * sds_relatch_count is the number of the next attempt of the episode: 0\n+ * while latched, 1..MAX_TRIES in budget, MAX_TRIES + 1 for the run that\n+ * only reports, and above that once out of budget.\n+ */\n+static void rtl8365mb_sds_relatch_work(struct work_struct *work)\n+{\n+\tstruct rtl8365mb *mb = container_of(to_delayed_work(work),\n+\t\t\t\t\t struct rtl8365mb, sds_relatch);\n+\tstruct realtek_priv *priv = mb-\u003epriv;\n+\tu32 park = (READ_ONCE(mb-\u003esds_misc_target_val) \u0026\n+\t\t ~RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK) |\n+\t\t RTL8365MB_D_PORT_SDS_MODE_DISABLE;\n+\tunsigned long delay = RTL8365MB_D_SDS_RELATCH_INTERVAL;\n+\tunsigned int attempts;\n+\tu16 status;\n+\tint ret;\n+\n+\t/* The condition that queued this work may already be stale by the\n+\t * time it runs (e.g. an earlier edge from a prior attempt just\n+\t * latched). Parking an already-live link would drop it for no\n+\t * reason.\n+\t */\n+\tret = rtl8365mb_sds_read(priv, RTL8365MB_D_SDS_EXT0_INDEX,\n+\t\t\t\t RTL8365MB_SDS_REG_LINK_STATUS, \u0026status);\n+\tif (ret) {\n+\t\tdev_err_ratelimited(priv-\u003edev,\n+\t\t\t\t \"failed to read SerDes link status: %pe\\n\",\n+\t\t\t\t ERR_PTR(ret));\n+\t\tgoto rearm;\n+\t}\n+\tif (status \u0026 RTL8365MB_SDS_LINK_STATUS_LINK_MASK) {\n+\t\t/* Latched: end the episode, keep watching for a later loss. */\n+\t\tWRITE_ONCE(mb-\u003esds_relatch_count, 0);\n+\t\tdelay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;\n+\t\tgoto rearm;\n+\t}\n+\n+\tattempts = READ_ONCE(mb-\u003esds_relatch_count);\n+\tif (!attempts) {\n+\t\tdev_warn(priv-\u003edev,\n+\t\t\t \"SerDes link lost outside a reconfiguration; re-latching\\n\");\n+\t\tattempts = 1;\n+\t} else if (attempts == RTL8365MB_D_SDS_RELATCH_MAX_TRIES + 1) {\n+\t\t/* This run only checked whether the last in-budget edge\n+\t\t * took. The first out-of-budget edge comes one healthcheck\n+\t\t * interval from now, not on this pass.\n+\t\t */\n+\t\tWRITE_ONCE(mb-\u003esds_relatch_count, attempts + 1);\n+\t\tdev_warn(priv-\u003edev,\n+\t\t\t \"SerDes did not latch after %u re-latch attempts; still retrying every %d s\\n\",\n+\t\t\t RTL8365MB_D_SDS_RELATCH_MAX_TRIES,\n+\t\t\t RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL / HZ);\n+\t\tdelay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;\n+\t\tgoto rearm;\n+\t}\n+\n+\tif (attempts \u003c= RTL8365MB_D_SDS_RELATCH_MAX_TRIES)\n+\t\tWRITE_ONCE(mb-\u003esds_relatch_count, attempts + 1);\n+\telse\n+\t\t/* Past the fast-retry budget. The receiver latches only on a\n+\t\t * DISABLE -\u003e mode edge and nothing re-runs pcs_config() for a\n+\t\t * fixed-link port, so keep driving one edge per interval.\n+\t\t */\n+\t\tdelay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;\n+\n+\tmutex_lock(\u0026mb-\u003esds_lock);\n+\tret = regmap_update_bits(priv-\u003emap, RTL8365MB_SDS_MISC_REG,\n+\t\t\t\t RTL8365MB_D_SDS_MISC_CFG_MASK, park);\n+\tmutex_unlock(\u0026mb-\u003esds_lock);\n+\tif (ret) {\n+\t\tdev_err_ratelimited(priv-\u003edev,\n+\t\t\t\t \"failed to park SDS_MISC: %pe\\n\",\n+\t\t\t\t ERR_PTR(ret));\n+\t\tgoto rearm;\n+\t}\n+\n+\tusleep_range(20000, 21000);\n+\n+\tmutex_lock(\u0026mb-\u003esds_lock);\n+\tret = regmap_update_bits(priv-\u003emap, RTL8365MB_SDS_MISC_REG,\n+\t\t\t\t RTL8365MB_D_SDS_MISC_CFG_MASK,\n+\t\t\t\t READ_ONCE(mb-\u003esds_misc_target_val));\n+\tmutex_unlock(\u0026mb-\u003esds_lock);\n+\tif (ret) {\n+\t\tdev_err_ratelimited(priv-\u003edev,\n+\t\t\t\t \"failed to restore SDS_MISC: %pe\\n\",\n+\t\t\t\t ERR_PTR(ret));\n+\t\tgoto rearm;\n+\t}\n+\n+\tdev_dbg(priv-\u003edev, \"SerDes re-latch edge driven (attempt %u)\\n\",\n+\t\tattempts);\n+\n+\t/* This edge may not have taken either; the next run checks. */\n+rearm:\n+\tschedule_delayed_work(\u0026mb-\u003esds_relatch, delay);\n }\n \n static bool rtl8365mb_interface_is_serdes(phy_interface_t interface)\n@@ -1517,10 +1873,14 @@ static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs,\n {\n \tstruct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);\n \tstruct realtek_priv *priv = mb-\u003epriv;\n+\tu8 sds_index = 0;\n \tu16 status;\n+\tbool is_d;\n \tu32 val;\n \tint ret;\n \n+\tis_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;\n+\n \t/* In-band autonegotiation is not implemented, so the link parameters are\n \t * forced from rtl8365mb_pcs_link_up(). The real link state must still be\n \t * read from the SerDes itself: the embedded DW8051 microcontroller that\n@@ -1528,7 +1888,11 @@ static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs,\n \t * rtl8365mb_pcs_config()), so the link status register can be read\n \t * directly through the SDS_INDACS window without racing the auto-poll.\n \t */\n-\tret = rtl8365mb_sds_read(priv, RTL8365MB_SDS_REG_LINK_STATUS, \u0026status);\n+\tif (is_d)\n+\t\tsds_index = RTL8365MB_D_SDS_EXT0_INDEX;\n+\n+\tret = rtl8365mb_sds_read(priv, sds_index,\n+\t\t\t\t RTL8365MB_SDS_REG_LINK_STATUS, \u0026status);\n \tif (ret) {\n \t\tstate-\u003elink = false;\n \t\treturn;\n@@ -1539,10 +1903,19 @@ static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs,\n \tif (!state-\u003elink)\n \t\treturn;\n \n+\tif (is_d) {\n+\t\tstate-\u003eduplex = DUPLEX_FULL;\n+\t\tstate-\u003espeed = state-\u003einterface == PHY_INTERFACE_MODE_2500BASEX ?\n+\t\t\t\tSPEED_2500 : SPEED_1000;\n+\t\treturn;\n+\t}\n+\n \t/* The speed and duplex are forced; read them back from the values\n \t * programmed into the SerDes MISC register.\n \t */\n+\tmutex_lock(\u0026mb-\u003esds_lock);\n \tret = regmap_read(priv-\u003emap, RTL8365MB_SDS_MISC_REG, \u0026val);\n+\tmutex_unlock(\u0026mb-\u003esds_lock);\n \tif (ret) {\n \t\tstate-\u003elink = false;\n \t\treturn;\n@@ -1580,6 +1953,12 @@ static void rtl8365mb_pcs_link_up(struct phylink_pcs *pcs,\n \tu32 r_speed;\n \tint ret;\n \n+\t/* Family D forces the external MAC ability from mac_link_up(); its\n+\t * SDS_MISC fields do not share the family C link-force layout.\n+\t */\n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)\n+\t\treturn;\n+\n \t/* The speed field has no value for 2.5 Gbps: the rate is determined by\n \t * the HSGMII SerDes configuration, and the vendor driver programs the\n \t * 1 Gbps value here.\n@@ -1606,7 +1985,9 @@ static void rtl8365mb_pcs_link_up(struct phylink_pcs *pcs,\n \t * force from rtl8365mb_phylink_mac_link_up(), where the resolved pause\n \t * modes are known.\n \t */\n+\tmutex_lock(\u0026mb-\u003esds_lock);\n \tret = regmap_update_bits(priv-\u003emap, RTL8365MB_SDS_MISC_REG, mask, val);\n+\tmutex_unlock(\u0026mb-\u003esds_lock);\n \tif (ret) {\n \t\tdev_err(priv-\u003edev, \"failed to force SerDes link: %pe\\n\",\n \t\t\tERR_PTR(ret));\n@@ -1632,23 +2013,31 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,\n \tu32 r_duplex;\n \tu32 r_speed;\n \tu32 r_link;\n+\tbool is_d;\n \tint val;\n \tint ret;\n \n \tif (!extint)\n \t\treturn -ENODEV;\n \n+\tis_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;\n \tif (link) {\n \t\t/* Force the link up with the desired configuration */\n \t\tr_link = 1;\n \t\tr_rx_pause = rx_pause ? 1 : 0;\n \t\tr_tx_pause = tx_pause ? 1 : 0;\n \n-\t\t/* The speed field has no value for 2.5 Gbps: the rate is\n-\t\t * determined by the HSGMII SerDes configuration, and the\n-\t\t * vendor driver programs the 1 Gbps value here.\n-\t\t */\n-\t\tif (speed == SPEED_2500 || speed == SPEED_1000) {\n+\t\tif (speed == SPEED_2500) {\n+\t\t\tif (is_d) {\n+\t\t\t\tr_speed = RTL8365MB_D_PORT_SPEED_2500M;\n+\t\t\t} else {\n+\t\t\t\t/* The speed field has no value for 2.5 Gbps: the rate is\n+\t\t\t\t * determined by the HSGMII SerDes configuration, and the\n+\t\t\t\t * vendor driver programs the 1 Gbps value here.\n+\t\t\t\t */\n+\t\t\t\tr_speed = RTL8365MB_PORT_SPEED_1000M;\n+\t\t\t}\n+\t\t} else if (speed == SPEED_1000) {\n \t\t\tr_speed = RTL8365MB_PORT_SPEED_1000M;\n \t\t} else if (speed == SPEED_100) {\n \t\t\tr_speed = RTL8365MB_PORT_SPEED_100M;\n@@ -1678,8 +2067,7 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,\n \t\tr_duplex = 0;\n \t}\n \n-\tval = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1) |\n-\t FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK,\n+\tval = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK,\n \t\t\t r_tx_pause) |\n \t FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK,\n \t\t\t r_rx_pause) |\n@@ -1687,11 +2075,33 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,\n \t FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK,\n \t\t\t r_duplex) |\n \t FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK, r_speed);\n-\tret = regmap_write(priv-\u003emap,\n-\t\t\t RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(extint-\u003eid),\n-\t\t\t val);\n-\tif (ret)\n-\t\treturn ret;\n+\n+\tif (is_d) {\n+\t\t/* Speed is 3 bits on family D: bits[1:0] go into FORCE_SPEED,\n+\t\t * bit[2] goes into FORCE_SPEED2 (bit 12); only 2500M sets it,\n+\t\t * bit 13 of the field is unused.\n+\t\t */\n+\t\tval |= FIELD_PREP(RTL8365MB_D_DIGITAL_INTERFACE_FORCE_SPEED2_MASK,\n+\t\t\t\t r_speed \u003e\u003e RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_WIDTH);\n+\t\tret = regmap_write(priv-\u003emap,\n+\t\t\t\t RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG(port),\n+\t\t\t\t val);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tret = regmap_write(priv-\u003emap,\n+\t\t\t\t RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG_EN(port),\n+\t\t\t\t RTL8365MB_D_DIGITAL_INTERFACE_FORCE_EN_ALL_MASK);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t} else {\n+\t\tval |= FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1);\n+\t\tret = regmap_write(priv-\u003emap,\n+\t\t\t\t RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(extint-\u003eid),\n+\t\t\t\t val);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n \n \treturn 0;\n }\n@@ -1876,7 +2286,8 @@ static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config,\n \t\t * rtl8365mb_pcs_link_up() because pcs_link_up() carries no\n \t\t * pause information.\n \t\t */\n-\t\tif (rtl8365mb_interface_is_serdes(interface)) {\n+\t\tif (rtl8365mb_interface_is_serdes(interface) \u0026\u0026\n+\t\t rtl8365mb_get_family(priv) != RTL8365MB_FAMILY_D) {\n \t\t\tu32 val = 0;\n \n \t\t\tif (tx_pause)\n@@ -1884,11 +2295,13 @@ static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config,\n \t\t\tif (rx_pause)\n \t\t\t\tval |= RTL8365MB_SDS_MISC_SGMII_RXFC_MASK;\n \n+\t\t\tmutex_lock(\u0026mb-\u003esds_lock);\n \t\t\tret = regmap_update_bits(priv-\u003emap,\n \t\t\t\t\t\t RTL8365MB_SDS_MISC_REG,\n \t\t\t\t\t\t RTL8365MB_SDS_MISC_SGMII_TXFC_MASK |\n \t\t\t\t\t\t\t RTL8365MB_SDS_MISC_SGMII_RXFC_MASK,\n \t\t\t\t\t\t val);\n+\t\t\tmutex_unlock(\u0026mb-\u003esds_lock);\n \t\t\tif (ret)\n \t\t\t\tdev_err(priv-\u003edev,\n \t\t\t\t\t\"failed to force SerDes pause modes on port %d: %pe\\n\",\n@@ -3100,6 +3513,10 @@ static int rtl8365mb_setup(struct dsa_switch *ds)\n \t */\n \tmb-\u003epcs.poll = true;\n \n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)\n+\t\tINIT_DELAYED_WORK(\u0026mb-\u003esds_relatch,\n+\t\t\t\t rtl8365mb_sds_relatch_work);\n+\n \tret = rtl8365mb_reset_chip(priv);\n \tif (ret) {\n \t\tdev_err(priv-\u003edev, \"failed to reset chip: %pe\\n\",\n@@ -3284,6 +3701,10 @@ static int rtl8365mb_setup(struct dsa_switch *ds)\n static void rtl8365mb_teardown(struct dsa_switch *ds)\n {\n \tstruct realtek_priv *priv = ds-\u003epriv;\n+\tstruct rtl8365mb *mb = priv-\u003echip_data;\n+\n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)\n+\t\tcancel_delayed_work_sync(\u0026mb-\u003esds_relatch);\n \n \trtl8365mb_stats_teardown(priv);\n \trtl8365mb_irq_teardown(priv);\n@@ -3349,7 +3770,15 @@ static int rtl8365mb_detect(struct realtek_priv *priv)\n \n \tdev_info(priv-\u003edev, \"found an %s switch\\n\", mb-\u003echip_info-\u003ename);\n \n-\tpriv-\u003enum_ports = RTL8365MB_MAX_NUM_PORTS;\n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)\n+\t\tpriv-\u003enum_ports = RTL8365MB_D_MAX_NUM_PORTS;\n+\telse\n+\t\tpriv-\u003enum_ports = RTL8365MB_MAX_NUM_PORTS;\n+\n+\tret = devm_mutex_init(priv-\u003edev, \u0026mb-\u003esds_lock);\n+\tif (ret)\n+\t\treturn ret;\n+\n \tmb-\u003epriv = priv;\n \tmb-\u003ecpu.trap_port = RTL8365MB_MAX_NUM_PORTS;\n \tmb-\u003ecpu.insert = RTL8365MB_CPU_INSERT_TO_ALL;\ndiff --git a/drivers/net/dsa/realtek/rtl8365mb_vlan.c b/drivers/net/dsa/realtek/rtl8365mb_vlan.c\nindex 8d49ffadce926..da517b1aae208 100644\n--- a/drivers/net/dsa/realtek/rtl8365mb_vlan.c\n+++ b/drivers/net/dsa/realtek/rtl8365mb_vlan.c\n@@ -48,12 +48,14 @@\n \n #include \"rtl8365mb_vlan.h\"\n #include \"rtl8365mb_table.h\"\n+#include \"rtl8365mb.h\"\n #include \u003clinux/if_bridge.h\u003e\n #include \u003clinux/lockdep.h\u003e\n #include \u003clinux/regmap.h\u003e\n \n /* CVLAN (i.e. VLAN4k) table entry layout, u16[3] */\n #define RTL8365MB_CVLAN_ENTRY_SIZE\t\t\t3 /* 48-bits */\n+#define RTL8365MB_D_CVLAN_ENTRY_SIZE\t\t\t2 /* 32-bits, no 3rd word */\n #define RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK\t\tGENMASK(7, 0)\n #define RTL8365MB_CVLAN_MBR_LO_MASK\t\t\tGENMASK(7, 0)\n #define RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK\t\tGENMASK(15, 8)\n@@ -65,6 +67,10 @@\n #define RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK\t\tGENMASK(13, 9)\n #define RTL8365MB_CVLAN_METERIDX_LO_MASK\t\tGENMASK(4, 0)\n #define RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK\t\tGENMASK(14, 14)\n+#define RTL8365MB_D_CVLAN_ENTRY_D1_SVLAN_CHK_IVL_SVL_MASK \\\n+\t\t\t\t\t\t\tGENMASK(2, 2)\n+#define RTL8365MB_D_CVLAN_ENTRY_D1_IVL_EN_MASK\t\tGENMASK(3, 3)\n+#define RTL8365MB_D_CVLAN_ENTRY_D1_FID_MASK\t\tGENMASK(1, 0)\n /* extends RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK */\n #define RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK\t\tGENMASK(2, 0)\n #define RTL8365MB_CVLAN_MBR_HI_MASK\t\t\tGENMASK(10, 8)\n@@ -113,6 +119,11 @@\n #define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(_p) \\\n \t\t(0x1F \u003c\u003c RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p))\n \n+#define RTL8365MB_D_VLAN_PVID_CTRL_BASE\t\t\t0x0700\n+#define RTL8365MB_D_VLAN_PVID_CTRL_REG(port) \\\n+\t(RTL8365MB_D_VLAN_PVID_CTRL_BASE + (port))\n+#define RTL8365MB_D_VLAN_PVID_CTRL_MASK\t\t\tGENMASK(11, 0)\n+\n /* Frame type filtering registers */\n #define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE\t0x07aa\n #define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port) \\\n@@ -185,13 +196,16 @@ struct rtl8365mb_vlanmc {\n static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid,\n \t\t\t\t struct rtl8365mb_vlan4k *vlan4k)\n {\n+\tbool is_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;\n+\tsize_t entry_size = is_d ? RTL8365MB_D_CVLAN_ENTRY_SIZE :\n+\t\t\t\t RTL8365MB_CVLAN_ENTRY_SIZE;\n \tu16 data[RTL8365MB_CVLAN_ENTRY_SIZE];\n \tint val;\n \tint ret;\n \n \tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN,\n \t\t\t\t RTL8365MB_TABLE_OP_READ, \u0026vid, 0, 0,\n-\t\t\t\t data, ARRAY_SIZE(data));\n+\t\t\t\t data, entry_size);\n \tif (ret)\n \t\treturn ret;\n \n@@ -199,33 +213,51 @@ static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid,\n \tmemset(vlan4k, 0, sizeof(*vlan4k));\n \tvlan4k-\u003evid = vid;\n \n+\t/* member/untag: d0[7:0]/d0[15:8] on both families. Family C\n+\t * extends these into data[2] bits [2:0]/[5:3] for its 9th-11th\n+\t * ports; family D's die has only 8 ports and no third table\n+\t * word, so data[2] does not exist there and must not be read.\n+\t */\n \tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, data[0]);\n \tvlan4k-\u003emember = FIELD_PREP(RTL8365MB_CVLAN_MBR_LO_MASK, val);\n-\tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, data[2]);\n-\tvlan4k-\u003emember |= FIELD_PREP(RTL8365MB_CVLAN_MBR_HI_MASK, val);\n+\tif (!is_d) {\n+\t\tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, data[2]);\n+\t\tvlan4k-\u003emember |= FIELD_PREP(RTL8365MB_CVLAN_MBR_HI_MASK, val);\n+\t}\n \n \tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, data[0]);\n \tvlan4k-\u003euntag = FIELD_PREP(RTL8365MB_CVLAN_UNTAG_LO_MASK, val);\n-\tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, data[2]);\n-\tvlan4k-\u003euntag |= FIELD_PREP(RTL8365MB_CVLAN_UNTAG_HI_MASK, val);\n-\n-\tvlan4k-\u003efid = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, data[1]);\n-\tvlan4k-\u003epriority_en =\n-\t\tFIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, data[1]);\n-\tvlan4k-\u003epriority =\n-\t\tFIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, data[1]);\n-\tvlan4k-\u003epolicing_en =\n-\t\tFIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, data[1]);\n-\n-\tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, data[1]);\n-\tval = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);\n-\tvlan4k-\u003emeteridx = val;\n-\tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, data[2]);\n-\tval = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);\n-\tvlan4k-\u003emeteridx |= val;\n-\n-\tvlan4k-\u003eivl_en =\n-\t\tFIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, data[1]);\n+\tif (!is_d) {\n+\t\tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, data[2]);\n+\t\tvlan4k-\u003euntag |= FIELD_PREP(RTL8365MB_CVLAN_UNTAG_HI_MASK, val);\n+\t}\n+\n+\tif (is_d) {\n+\t\tvlan4k-\u003efid = FIELD_GET(RTL8365MB_D_CVLAN_ENTRY_D1_FID_MASK, data[1]);\n+\t\t/* Family D has no priority/meter fields in this entry -\n+\t\t * left zeroed by the memset() above.\n+\t\t */\n+\t\tvlan4k-\u003eivl_en =\n+\t\t\tFIELD_GET(RTL8365MB_D_CVLAN_ENTRY_D1_IVL_EN_MASK, data[1]);\n+\t} else {\n+\t\tvlan4k-\u003efid = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, data[1]);\n+\t\tvlan4k-\u003epriority_en =\n+\t\t\tFIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, data[1]);\n+\t\tvlan4k-\u003epriority =\n+\t\t\tFIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, data[1]);\n+\t\tvlan4k-\u003epolicing_en =\n+\t\t\tFIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, data[1]);\n+\n+\t\tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, data[1]);\n+\t\tval = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);\n+\t\tvlan4k-\u003emeteridx = val;\n+\t\tval = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, data[2]);\n+\t\tval = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);\n+\t\tvlan4k-\u003emeteridx |= val;\n+\n+\t\tvlan4k-\u003eivl_en =\n+\t\t\tFIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, data[1]);\n+\t}\n \n \treturn 0;\n }\n@@ -233,6 +265,9 @@ static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid,\n static int rtl8365mb_vlan_4k_write(struct realtek_priv *priv,\n \t\t\t\t const struct rtl8365mb_vlan4k *vlan4k)\n {\n+\tbool is_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;\n+\tsize_t entry_size = is_d ? RTL8365MB_D_CVLAN_ENTRY_SIZE :\n+\t\t\t\t RTL8365MB_CVLAN_ENTRY_SIZE;\n \tu16 data[RTL8365MB_CVLAN_ENTRY_SIZE] = { 0 };\n \tu16 vid;\n \tint val;\n@@ -244,36 +279,52 @@ static int rtl8365mb_vlan_4k_write(struct realtek_priv *priv,\n \tval = FIELD_GET(RTL8365MB_CVLAN_UNTAG_LO_MASK, vlan4k-\u003euntag);\n \tdata[0] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, val);\n \n-\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, vlan4k-\u003efid);\n-\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK,\n-\t\t\t vlan4k-\u003epriority_en);\n-\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK,\n-\t\t\t vlan4k-\u003epriority);\n-\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK,\n-\t\t\t vlan4k-\u003epolicing_en);\n-\n-\t/* FIELD_* does not play nice with struct bitfield. */\n-\tval = vlan4k-\u003emeteridx;\n-\tval = FIELD_GET(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);\n-\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, val);\n-\n-\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK,\n-\t\t\t vlan4k-\u003eivl_en);\n-\n-\tval = FIELD_GET(RTL8365MB_CVLAN_MBR_HI_MASK, vlan4k-\u003emember);\n-\tdata[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, val);\n-\n-\tval = FIELD_GET(RTL8365MB_CVLAN_UNTAG_HI_MASK, vlan4k-\u003euntag);\n-\tdata[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, val);\n-\n-\tval = vlan4k-\u003emeteridx;\n-\tval = FIELD_GET(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);\n-\tdata[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, val);\n+\tif (is_d) {\n+\t\t/* The chip supports both IVL and SVL, but the caller (see\n+\t\t * rtl8365mb_vlan_4k_port_set()) never requests SVL, so both\n+\t\t * IVL/SVL selector bits are forced here rather than threaded\n+\t\t * through from vlan4k-\u003eivl_en, which family C does honor.\n+\t\t */\n+\t\tdata[1] |= FIELD_PREP(RTL8365MB_D_CVLAN_ENTRY_D1_IVL_EN_MASK, 1) |\n+\t\t\t FIELD_PREP(RTL8365MB_D_CVLAN_ENTRY_D1_SVLAN_CHK_IVL_SVL_MASK, 1);\n+\t\tdata[1] |= FIELD_PREP(RTL8365MB_D_CVLAN_ENTRY_D1_FID_MASK, vlan4k-\u003efid);\n+\t\t/* No priority/meter/member-untag-extension fields exist in\n+\t\t * family D's 2-word entry - data[1] and data[0] above are\n+\t\t * the whole entry, and data[2] is not part of it at all.\n+\t\t */\n+\t} else {\n+\t\tval = vlan4k-\u003efid;\n+\t\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, val);\n+\t\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK,\n+\t\t\t\t vlan4k-\u003epriority_en);\n+\t\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK,\n+\t\t\t\t vlan4k-\u003epriority);\n+\t\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK,\n+\t\t\t\t vlan4k-\u003epolicing_en);\n+\n+\t\t/* FIELD_* does not play nice with struct bitfield. */\n+\t\tval = vlan4k-\u003emeteridx;\n+\t\tval = FIELD_GET(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);\n+\t\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, val);\n+\n+\t\tdata[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK,\n+\t\t\t\t vlan4k-\u003eivl_en);\n+\n+\t\tval = FIELD_GET(RTL8365MB_CVLAN_MBR_HI_MASK, vlan4k-\u003emember);\n+\t\tdata[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, val);\n+\n+\t\tval = FIELD_GET(RTL8365MB_CVLAN_UNTAG_HI_MASK, vlan4k-\u003euntag);\n+\t\tdata[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, val);\n+\n+\t\tval = vlan4k-\u003emeteridx;\n+\t\tval = FIELD_GET(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);\n+\t\tdata[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, val);\n+\t}\n \n \tvid = vlan4k-\u003evid;\n \treturn rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN,\n \t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026vid, 0, 0,\n-\t\t\t\t data, ARRAY_SIZE(data));\n+\t\t\t\t data, entry_size);\n }\n \n static int\n@@ -679,11 +730,22 @@ int rtl8365mb_vlan_port_get_pvid(struct realtek_priv *priv, int port, u16 *pvid)\n \tu8 vlanmc_idx;\n \tint ret;\n \n-\tret = rtl8365mb_vlan_get_pvid_mc(priv, port, \u0026vlanmc_idx, \u0026vlanmc);\n-\tif (ret)\n-\t\treturn ret;\n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D) {\n+\t\tu32 data;\n+\n+\t\tret = regmap_read(priv-\u003emap, RTL8365MB_D_VLAN_PVID_CTRL_REG(port), \u0026data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t*pvid = data \u0026 RTL8365MB_D_VLAN_PVID_CTRL_MASK;\n+\t} else {\n+\t\tret = rtl8365mb_vlan_get_pvid_mc(priv, port, \u0026vlanmc_idx, \u0026vlanmc);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t*pvid = vlanmc.evid;\n+\t}\n \n-\t*pvid = vlanmc.evid;\n \treturn 0;\n }\n \n@@ -748,6 +810,64 @@ rtl8365mb_vlan_port_set_framefilter(struct realtek_priv *priv,\n \t\t\t\t val);\n }\n \n+/*\n+ * rtl8365mb_vlan_pvid_port_set_direct() - Configure a port's PVID as a raw\n+ * VID written to its dedicated register, for chip families without a\n+ * working VLAN MC table (RTL8365MB_FAMILY_D)\n+ *\n+ * Reads back the previous PVID and frame filter first so both can be\n+ * restored if enabling the new PVID fails partway through, matching\n+ * the rollback behavior of the family-C implementation above.\n+ *\n+ * Context: Can sleep. Must be called with \u0026priv-\u003evlan_lock held.\n+ * Return: 0 on success, or a negative error code on failure.\n+ */\n+static int rtl8365mb_vlan_pvid_port_set_direct(struct realtek_priv *priv,\n+\t\t\t\t\t int port, u16 vid)\n+{\n+\tenum rtl8365mb_frame_ingress prev_accepted_frame;\n+\tu32 prev_pvid;\n+\tint ret;\n+\n+\tret = regmap_read(priv-\u003emap, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),\n+\t\t\t \u0026prev_pvid);\n+\tif (ret) {\n+\t\tdev_err(priv-\u003edev, \"Failed to read current PVID\\n\");\n+\t\treturn ret;\n+\t}\n+\tprev_pvid \u0026= RTL8365MB_D_VLAN_PVID_CTRL_MASK;\n+\n+\tret = rtl8365mb_vlan_port_get_framefilter(priv, port, \u0026prev_accepted_frame);\n+\tif (ret) {\n+\t\tdev_err(priv-\u003edev, \"Failed to get current framefilter\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = regmap_update_bits(priv-\u003emap, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),\n+\t\t\t\t RTL8365MB_D_VLAN_PVID_CTRL_MASK,\n+\t\t\t\t vid \u0026 RTL8365MB_D_VLAN_PVID_CTRL_MASK);\n+\tif (ret) {\n+\t\tdev_err(priv-\u003edev, \"Failed to set port PVID\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\t/* Changing accept frame is what enables PVID (if not enabled before) */\n+\tret = rtl8365mb_vlan_port_set_framefilter(priv, port,\n+\t\t\t\t\t\t RTL8365MB_FRAME_TYPE_ANY_FRAME);\n+\tif (ret) {\n+\t\tdev_err(priv-\u003edev, \"Failed to set port frame filter\\n\");\n+\t\tgoto undo_pvid_write;\n+\t}\n+\n+\treturn 0;\n+\n+undo_pvid_write:\n+\t(void)regmap_update_bits(priv-\u003emap, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),\n+\t\t\t\t RTL8365MB_D_VLAN_PVID_CTRL_MASK, prev_pvid);\n+\t(void)rtl8365mb_vlan_port_set_framefilter(priv, port, prev_accepted_frame);\n+\treturn ret;\n+}\n+\n /*\n * rtl8365mb_vlan_pvid_port_set() - Configure a port's PVID and associated\n * VLANMC entry\n@@ -777,6 +897,13 @@ int rtl8365mb_vlan_pvid_port_set(struct dsa_switch *ds, int port, u16 vid,\n \n \tlockdep_assert_held(\u0026priv-\u003evlan_lock);\n \n+\t/* This chip family has no VLAN MC table - PVID is a raw VID in a\n+\t * dedicated per-port register, and there is no separate membership\n+\t * table entry to allocate/track.\n+\t */\n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)\n+\t\treturn rtl8365mb_vlan_pvid_port_set_direct(priv, port, vid);\n+\n \t/* Read the old PVID exclusively to undo in case of error */\n \tret = rtl8365mb_vlan_get_pvid_mc(priv, port, \u0026prev_vlanmc_idx,\n \t\t\t\t\t \u0026prev_vlanmc);\n@@ -856,6 +983,74 @@ int rtl8365mb_vlan_pvid_port_set(struct dsa_switch *ds, int port, u16 vid,\n \treturn ret;\n }\n \n+/*\n+ * rtl8365mb_vlan_pvid_port_clear_direct() - Remove a port's raw-VID PVID\n+ * configuration, for chip families without a working VLAN MC table\n+ * (RTL8365MB_FAMILY_D)\n+ *\n+ * Reads back the previous frame filter first so it can be restored if\n+ * clearing the PVID register fails.\n+ *\n+ * Context: Can sleep. Must be called with \u0026priv-\u003evlan_lock held.\n+ * Return: 0 on success, or a negative error code on failure.\n+ */\n+static int rtl8365mb_vlan_pvid_port_clear_direct(struct dsa_switch *ds,\n+\t\t\t\t\t\t int port, u16 vid)\n+{\n+\tenum rtl8365mb_frame_ingress prev_accepted_frame;\n+\tstruct realtek_priv *priv = ds-\u003epriv;\n+\tbool filtering;\n+\tu32 cur_pvid;\n+\tint ret;\n+\n+\tret = regmap_read(priv-\u003emap, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),\n+\t\t\t \u0026cur_pvid);\n+\tif (ret) {\n+\t\tdev_err(priv-\u003edev, \"Failed to read current PVID\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\t/* Port is not using this VID as PVID. Nothing to remove. */\n+\tif ((cur_pvid \u0026 RTL8365MB_D_VLAN_PVID_CTRL_MASK) != vid)\n+\t\treturn 0;\n+\n+\tfiltering = dsa_port_is_vlan_filtering(dsa_to_port(ds, port));\n+\n+\t/* Changing accept frame is what really removes PVID. But only do\n+\t * that if VLAN filtering is enabled.\n+\t */\n+\tif (filtering) {\n+\t\tret = rtl8365mb_vlan_port_get_framefilter(priv, port,\n+\t\t\t\t\t\t\t \u0026prev_accepted_frame);\n+\t\tif (ret) {\n+\t\t\tdev_err(priv-\u003edev, \"Failed to get current framefilter\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = rtl8365mb_vlan_port_set_framefilter(\n+\t\t\tpriv, port, RTL8365MB_FRAME_TYPE_TAGGED_ONLY);\n+\t\tif (ret) {\n+\t\t\tdev_err(priv-\u003edev, \"Failed to set port frame filter\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tret = regmap_update_bits(priv-\u003emap, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),\n+\t\t\t\t RTL8365MB_D_VLAN_PVID_CTRL_MASK, 0);\n+\tif (ret) {\n+\t\tdev_err(priv-\u003edev, \"Failed to set port PVID to 0\\n\");\n+\t\tgoto undo_set_framefilter;\n+\t}\n+\n+\treturn 0;\n+\n+undo_set_framefilter:\n+\tif (filtering)\n+\t\t(void)rtl8365mb_vlan_port_set_framefilter(priv, port,\n+\t\t\t\t\t\t\t prev_accepted_frame);\n+\treturn ret;\n+}\n+\n /*\n * rtl8365mb_vlan_pvid_port_clear() - Remove a port's PVID configuration\n * @ds: dsa switch instance\n@@ -879,6 +1074,9 @@ int rtl8365mb_vlan_pvid_port_clear(struct dsa_switch *ds, int port, u16 vid)\n \n \tlockdep_assert_held(\u0026priv-\u003evlan_lock);\n \n+\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)\n+\t\treturn rtl8365mb_vlan_pvid_port_clear_direct(ds, port, vid);\n+\n \tret = rtl8365mb_vlan_get_pvid_mc(priv, port, \u0026vlanmc_idx,\n \t\t\t\t\t \u0026vlanmc);\n \tif (ret) {\n"
} |
| 2/1 | 2026/10/09 18:09 | agent | patch-evaluator |
5mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patched code is part of the Realtek RTL8365MB switch driver (drivers/net/dsa/realtek/rtl8365mb*), which drives physical Ethernet switch ASICs (RTL8365MB/RTL8367 family) connected via Device Tree over MDIO or SMI GPIO buses. Probe requires hardware communication to match specific chip IDs, and this hardware is not emulated in standard QEMU or GCE virtual environments. Therefore, the modified code is structurally unreachable for fuzzing.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit cab836bc4ba51e1fb0729e5b6440b455d284fefe
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 9 18:09:00 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/dsa/realtek/rtl8365mb.h b/drivers/net/dsa/realtek/rtl8365mb.h
new file mode 100644
index 0000000000000..315de4c93c446
--- /dev/null
+++ b/drivers/net/dsa/realtek/rtl8365mb.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _RTL8365MB_H
+#define _RTL8365MB_H
+
+#include "realtek.h"
+
+enum rtl8365mb_family {
+ RTL8365MB_FAMILY_C,
+ RTL8365MB_FAMILY_D,
+};
+
+enum rtl8365mb_family rtl8365mb_get_family(struct realtek_priv *priv);
+
+#endif /* _RTL8365MB_H */
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index da06f952a053d..a322c425ccf08 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -81,6 +81,7 @@
* - RTL8367RB-VB
* - RTL8367SB
* - RTL8367S
+ * - RTL8367S-VB
* - RTL8370MB
* - RTL8310SR
*
@@ -103,6 +104,7 @@
#include <linux/if_vlan.h>
#include <linux/mdio.h>
#include <linux/phylink.h>
+#include <linux/workqueue.h>
#include "realtek.h"
#include "realtek-smi.h"
@@ -110,12 +112,14 @@
#include "rtl83xx.h"
#include "rtl8365mb_l2.h"
#include "rtl8365mb_vlan.h"
+#include "rtl8365mb.h"
/* Family-specific data and limits */
#define RTL8365MB_PHYADDRMAX 7
#define RTL8365MB_NUM_PHYREGS 32
#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1)
#define RTL8365MB_MAX_NUM_PORTS 11
+#define RTL8365MB_D_MAX_NUM_PORTS 8
/* Valid for the whole family except RTL8370B, which has 4160 entries.
* RTL8370B is mentioned in vendor code but it might not even belong
* to the same RTL8367C family.
@@ -283,6 +287,7 @@
#define RTL8365MB_SDS_INDACS_CMD_BUSY_MASK BIT(8)
#define RTL8365MB_SDS_INDACS_CMD_RUN_MASK BIT(7)
#define RTL8365MB_SDS_INDACS_CMD_WR_MASK BIT(6)
+#define RTL8365MB_SDS_INDACS_CMD_INDEX_MASK GENMASK(5, 0)
#define RTL8365MB_SDS_INDACS_ADR_REG 0x6601
#define RTL8365MB_SDS_INDACS_DATA_REG 0x6602
@@ -296,6 +301,23 @@
#define RTL8365MB_SDS_MISC_SGMII_SPD_MASK GENMASK(8, 7)
#define RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK BIT(6)
+/* Re-latch retry interval and attempt budget. Each attempt parks the
+ * SerDes for 20 ms, so a far end that never comes up must not be retried
+ * at this rate forever, and the interval must not be so tight that
+ * back-to-back parks starve a link that is about to latch. The count is
+ * reset to 1 by pcs_config() and to 0 whenever the receiver latches;
+ * attempts start at pcs_config(), well before the far end MAC is up, so
+ * with the interval below the budget is roughly RELATCH_MAX_TRIES seconds,
+ * generous enough to outlast a slow conduit.
+ */
+#define RTL8365MB_D_SDS_RELATCH_INTERVAL (HZ)
+#define RTL8365MB_D_SDS_RELATCH_MAX_TRIES 15
+
+/* How often a latched (or out-of-budget) SerDes is checked, and re-latched
+ * if still down. Much slower than the re-latch interval.
+ */
+#define RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL (10 * HZ)
+
/* SerDes internal registers, accessed via the SDS_INDACS registers. The BMCR
* data path reset holds BMCR_ANENABLE | BMCR_ISOLATE while toggling the
* vendor-specific low bits from phase 1 to phase 2, which triggers a data path
@@ -331,6 +353,27 @@
#define RTL8365MB_SDS_OPTION_ARM_KEY 0x0249
#define RTL8365MB_SDS_OPTION_REG 0x13C1
+/* Family D uses the SDS13 indirect window for its MAC6 SerDes. */
+#define RTL8365MB_D_SDS_EXT0_INDEX 13
+#define RTL8365MB_D_FIBER_CFG2_REG 0x13E8
+#define RTL8365MB_D_FIBER_CFG2_RX_DISABLE_MASK GENMASK(7, 6)
+#define RTL8365MB_D_FIBER_CFG2_RX_DISABLE_SDS0 BIT(6)
+#define RTL8365MB_D_SDS_MISC_PA33PC_EN BIT(11)
+#define RTL8365MB_D_SDS_MISC_PA12PC_EN BIT(10)
+#define RTL8365MB_D_SDS_MISC_MAC6_SEL_SDS0 BIT(9)
+#define RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK GENMASK(4, 0)
+#define RTL8365MB_D_SDS_MISC_MODE_SGMII 0x02
+#define RTL8365MB_D_SDS_MISC_MODE_HSGMII 0x12
+#define RTL8365MB_D_SDS_MISC_CFG_MASK \
+ (RTL8365MB_D_SDS_MISC_PA33PC_EN | \
+ RTL8365MB_D_SDS_MISC_PA12PC_EN | \
+ RTL8365MB_D_SDS_MISC_MAC6_SEL_SDS0 | \
+ RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK)
+/* Shared "disable" encoding for both SDS_MISC's and SDS1_MISC0's
+ * 5-bit mode fields.
+ */
+#define RTL8365MB_D_PORT_SDS_MODE_DISABLE 0x1f
+
/* Embedded DW8051 microcontroller control registers. The microcontroller
* can run firmware to manage the SerDes link, but this driver keeps it in
* reset and disabled: phylink already performs the link management that
@@ -343,6 +386,7 @@
#define RTL8365MB_PORT_SPEED_10M 0
#define RTL8365MB_PORT_SPEED_100M 1
#define RTL8365MB_PORT_SPEED_1000M 2
+#define RTL8365MB_D_PORT_SPEED_2500M 5
/* External interface force configuration registers 0~2 */
#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 /* EXT0 */
@@ -360,6 +404,20 @@
#define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK BIT(4)
#define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK BIT(2)
#define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK GENMASK(1, 0)
+#define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_WIDTH 2 /* bits[1:0] */
+
+/* bits[13:12]; bit 13 reserved, no defined speed value uses it yet */
+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_SPEED2_MASK GENMASK(13, 12)
+
+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG_BASE 0x12c0
+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG(_port) \
+ (RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG_BASE + (_port))
+
+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_EN_BASE 0x12c8
+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG_EN(_port) \
+ (RTL8365MB_D_DIGITAL_INTERFACE_FORCE_EN_BASE + (_port))
+
+#define RTL8365MB_D_DIGITAL_INTERFACE_FORCE_EN_ALL_MASK GENMASK(15, 0)
/* CPU port mask register - controls which ports are treated as CPU ports */
#define RTL8365MB_CPU_PORT_MASK_REG 0x1219
@@ -441,6 +499,15 @@
#define RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK GENMASK(5, 4)
#define RTL8365MB_PORT_MISC_CFG_CONGESTION_SUSTAIN_TIME_MASK GENMASK(3, 0)
+#define RTL8365MB_D_REG_EXT_TXC_DLY 0x13f9
+#define RTL8365MB_D_EXT1_RGMII_TX_DLY_MASK GENMASK(5, 3)
+
+#define RTL8365MB_D_REG_TOP_CON0 0x1d70
+#define RTL8365MB_D_MAC7_SEL_EXT1_MASK BIT(13)
+#define RTL8365MB_D_MAC4_SEL_EXT1_MASK BIT(12)
+
+#define RTL8365MB_D_REG_SDS1_MISC0 0x1d78
+
/**
* enum rtl8365mb_vlan_egress_mode - port VLAN egress mode
* @RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL: follow untag mask in VLAN4k table entry
@@ -676,6 +743,21 @@ static const struct rtl8365mb_jam_tbl_entry rtl8365mb_sds_jam_hsgmii[] = {
{ 0x0424, 0xD810 }, { 0x0001, 0x0F80 }, { 0x002E, 0x83F2 },
};
+/* Family D tuning tables from the Realtek vendor port API. */
+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_d_sds_jam_sgmii[] = {
+ { 0x0427, 0x4E0C }, { 0x0428, 0xAA00 }, { 0x0425, 0x5189 },
+ { 0x0424, 0x8414 }, { 0x0423, 0x1020 }, { 0x0410, 0x0002 },
+ { 0x0484, 0x011B }, { 0x0421, 0x8E13 }, { 0x0422, 0x1140 },
+ { 0x0004, 0x074F },
+};
+
+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_d_sds_jam_hsgmii[] = {
+ { 0x0427, 0x4E0C }, { 0x0428, 0xAA00 }, { 0x0425, 0x5189 },
+ { 0x0424, 0x8414 }, { 0x0423, 0x1020 }, { 0x0410, 0x0002 },
+ { 0x0504, 0x051B }, { 0x0421, 0x8E13 }, { 0x0422, 0x1140 },
+ { 0x0004, 0x074F },
+};
+
enum rtl8365mb_phy_interface_mode {
RTL8365MB_PHY_INTERFACE_MODE_INVAL = 0,
RTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0),
@@ -707,6 +789,7 @@ struct rtl8365mb_extint {
* @name: human-readable chip name
* @chip_id: chip identifier
* @chip_ver: chip silicon revision
+ * @family: chip family
* @extints: available external interfaces
* @jam_table: chip-specific initialization jam table
* @jam_size: size of the chip's jam table
@@ -719,6 +802,7 @@ struct rtl8365mb_chip_info {
const char *name;
u32 chip_id;
u32 chip_ver;
+ enum rtl8365mb_family family;
const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS];
const struct rtl8365mb_jam_tbl_entry *jam_table;
size_t jam_size;
@@ -731,6 +815,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
.name = "RTL8365MB-VC",
.chip_id = 0x6367,
.chip_ver = 0x0040,
+ .family = RTL8365MB_FAMILY_C,
.extints = {
{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
PHY_INTF(RMII) | PHY_INTF(RGMII) },
@@ -742,6 +827,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
.name = "RTL8367S",
.chip_id = 0x6367,
.chip_ver = 0x00A0,
+ .family = RTL8365MB_FAMILY_C,
.extints = {
{ 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) },
{ 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) |
@@ -754,6 +840,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
.name = "RTL8367SB",
.chip_id = 0x6367,
.chip_ver = 0x0010,
+ .family = RTL8365MB_FAMILY_C,
.extints = {
{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
PHY_INTF(RMII) | PHY_INTF(RGMII) |
@@ -768,6 +855,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
.name = "RTL8367RB-VB",
.chip_id = 0x6367,
.chip_ver = 0x0020,
+ .family = RTL8365MB_FAMILY_C,
.extints = {
{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
PHY_INTF(RMII) | PHY_INTF(RGMII) },
@@ -777,6 +865,19 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
.jam_table = rtl8365mb_init_jam_8365mb_vc,
.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
},
+ {
+ .name = "RTL8367S-VB",
+ .chip_id = 0x6642,
+ .chip_ver = 0x0010,
+ .family = RTL8365MB_FAMILY_D,
+ .extints = {
+ { 6, 0, PHY_INTF(SGMII) | PHY_INTF(HSGMII) },
+ { 7, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
+ PHY_INTF(RMII) | PHY_INTF(RGMII) },
+ },
+ .jam_table = rtl8365mb_init_jam_8365mb_vc,
+ .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+ },
};
enum rtl8365mb_stp_state {
@@ -867,13 +968,28 @@ struct rtl8365mb {
const struct rtl8365mb_chip_info *chip_info;
struct rtl8365mb_cpu cpu;
struct mutex mib_lock;
+ /* Serializes access to the shared SDS_INDACS ADR/CMD/DATA window and
+ * to RTL8365MB_SDS_MISC_REG, reachable both from phylink's PCS poll
+ * (mb->pcs.poll) and from the family D SerDes re-latch work.
+ */
+ struct mutex sds_lock;
struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS];
struct phylink_pcs pcs;
bool sds_supported;
+ struct delayed_work sds_relatch; /* re-latch edges and link health check */
+ unsigned int sds_relatch_count; /* next attempt of the episode, 0 once latched */
+ u32 sds_misc_target_val;
};
#define pcs_to_rtl8365mb(_pcs) container_of((_pcs), struct rtl8365mb, pcs)
+enum rtl8365mb_family rtl8365mb_get_family(struct realtek_priv *priv)
+{
+ struct rtl8365mb *mb = priv->chip_data;
+
+ return mb->chip_info->family;
+}
+
static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv)
{
u32 val;
@@ -1164,6 +1280,7 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,
struct dsa_port *dp;
int tx_delay = 0;
int rx_delay = 0;
+ u32 data;
u32 val;
int ret;
@@ -1233,43 +1350,85 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,
if (ret)
return ret;
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D && extint->id == 1) {
+ ret = regmap_update_bits(priv->map,
+ RTL8365MB_D_REG_EXT_TXC_DLY,
+ RTL8365MB_D_EXT1_RGMII_TX_DLY_MASK, 0);
+ if (ret)
+ return ret;
+ /* Configure RGMII/MII mux to port 7 if UTP_PORT4 is not RGMII mode */
+ ret = regmap_read(priv->map, RTL8365MB_D_REG_TOP_CON0, &data);
+ if (ret)
+ return ret;
+ if ((data & RTL8365MB_D_MAC4_SEL_EXT1_MASK) == 0) {
+ ret = regmap_update_bits(priv->map,
+ RTL8365MB_D_REG_TOP_CON0,
+ RTL8365MB_D_MAC7_SEL_EXT1_MASK,
+ RTL8365MB_D_MAC7_SEL_EXT1_MASK);
+ if (ret)
+ return ret;
+ }
+ ret = regmap_update_bits(priv->map,
+ RTL8365MB_D_REG_SDS1_MISC0,
+ RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK,
+ RTL8365MB_D_PORT_SDS_MODE_DISABLE);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
-static int rtl8365mb_sds_write(struct realtek_priv *priv, u16 addr, u16 data)
+static int rtl8365mb_sds_write(struct realtek_priv *priv, u8 index,
+ u16 addr, u16 data)
{
+ struct rtl8365mb *mb = priv->chip_data;
int ret;
+ mutex_lock(&mb->sds_lock);
+
ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_DATA_REG, data);
if (ret)
- return ret;
+ goto out_unlock;
ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR_REG, addr);
if (ret)
- return ret;
+ goto out_unlock;
/* The SerDes indirect access engine completes the command within the
* register write transaction, so there is no need to wait or poll for
* completion before the next access, matching the vendor driver.
*/
- return regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
- RTL8365MB_SDS_INDACS_CMD_RUN_MASK |
- RTL8365MB_SDS_INDACS_CMD_WR_MASK);
+ ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
+ RTL8365MB_SDS_INDACS_CMD_RUN_MASK |
+ RTL8365MB_SDS_INDACS_CMD_WR_MASK |
+ FIELD_PREP(RTL8365MB_SDS_INDACS_CMD_INDEX_MASK,
+ index));
+
+out_unlock:
+ mutex_unlock(&mb->sds_lock);
+ return ret;
}
-static int rtl8365mb_sds_read(struct realtek_priv *priv, u16 addr, u16 *data)
+static int rtl8365mb_sds_read(struct realtek_priv *priv, u8 index,
+ u16 addr, u16 *data)
{
+ struct rtl8365mb *mb = priv->chip_data;
u32 val;
int ret;
+ mutex_lock(&mb->sds_lock);
+
ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR_REG, addr);
if (ret)
- return ret;
+ goto out_unlock;
ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
- RTL8365MB_SDS_INDACS_CMD_RUN_MASK);
+ RTL8365MB_SDS_INDACS_CMD_RUN_MASK |
+ FIELD_PREP(RTL8365MB_SDS_INDACS_CMD_INDEX_MASK,
+ index));
if (ret)
- return ret;
+ goto out_unlock;
/* Wait for the indirect read to complete: the engine clears the BUSY
* bit once the data register holds the result.
@@ -1279,15 +1438,19 @@ static int rtl8365mb_sds_read(struct realtek_priv *priv, u16 addr, u16 *data)
!(val & RTL8365MB_SDS_INDACS_CMD_BUSY_MASK),
10, 1000);
if (ret)
- return ret;
+ goto out_unlock;
ret = regmap_read(priv->map, RTL8365MB_SDS_INDACS_DATA_REG, &val);
if (ret)
- return ret;
+ goto out_unlock;
*data = val;
- return 0;
+ ret = 0;
+
+out_unlock:
+ mutex_unlock(&mb->sds_lock);
+ return ret;
}
/* The vendor driver selects between two sets of SerDes tuning parameters based
@@ -1307,6 +1470,14 @@ static int rtl8365mb_sds_probe_option(struct realtek_priv *priv)
int ret;
int i;
+ /* Family D has a fixed SDS13 programming model and does not use the
+ * family C option register to select its tuning table.
+ */
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D) {
+ mb->sds_supported = true;
+ return 0;
+ }
+
/* Nothing to probe if no external interface is wired to the SerDes */
for (i = 0; i < RTL8365MB_MAX_NUM_EXTINTS; i++) {
extint = &mb->chip_info->extints[i];
@@ -1385,28 +1556,54 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
const unsigned long *advertising,
bool permit_pause_to_mac)
{
- const struct rtl8365mb_jam_tbl_entry *sds_jam;
const int id = RTL8365MB_SDS_EXT_INTERFACE_ID;
+ const struct rtl8365mb_jam_tbl_entry *sds_jam;
struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
- struct realtek_priv *priv;
+ struct realtek_priv *priv = mb->priv;
size_t sds_jam_size;
- u32 mode;
+ u32 misc_mask;
+ u32 misc_val;
+ u32 sds_mode;
+ u8 sds_index;
+ bool is_d;
u16 val;
int ret;
int i;
- priv = mb->priv;
+ is_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;
+ /* Cancel any in-flight re-latch edge before touching SDS_MISC: the
+ * work drops sds_lock across its sleep and could otherwise interleave
+ * with the reconfiguration below.
+ */
+ if (is_d)
+ cancel_delayed_work_sync(&mb->sds_relatch);
+
+ /* Select the appropriate tuning table and SDS mode */
if (interface == PHY_INTERFACE_MODE_2500BASEX) {
- sds_jam = rtl8365mb_sds_jam_hsgmii;
- sds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_hsgmii);
- mode = RTL8365MB_EXT_PORT_MODE_HSGMII;
+ if (is_d) {
+ sds_jam = rtl8365mb_d_sds_jam_hsgmii;
+ sds_jam_size = ARRAY_SIZE(rtl8365mb_d_sds_jam_hsgmii);
+ sds_mode = RTL8365MB_D_SDS_MISC_MODE_HSGMII;
+ } else {
+ sds_jam = rtl8365mb_sds_jam_hsgmii;
+ sds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_hsgmii);
+ sds_mode = RTL8365MB_EXT_PORT_MODE_HSGMII;
+ }
} else {
- sds_jam = rtl8365mb_sds_jam_sgmii;
- sds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_sgmii);
- mode = RTL8365MB_EXT_PORT_MODE_SGMII;
+ if (is_d) {
+ sds_jam = rtl8365mb_d_sds_jam_sgmii;
+ sds_jam_size = ARRAY_SIZE(rtl8365mb_d_sds_jam_sgmii);
+ sds_mode = RTL8365MB_D_SDS_MISC_MODE_SGMII;
+ } else {
+ sds_jam = rtl8365mb_sds_jam_sgmii;
+ sds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_sgmii);
+ sds_mode = RTL8365MB_EXT_PORT_MODE_SGMII;
+ }
}
+ sds_index = is_d ? RTL8365MB_D_SDS_EXT0_INDEX : 0;
+
/* Hold the embedded DW8051 microcontroller in reset and keep it
* disabled. The vendor driver loads firmware into it to manage the
* SerDes link, but the firmware only duplicates work that phylink
@@ -1435,34 +1632,57 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
/* Tune the SerDes with vendor-prescribed parameters */
for (i = 0; i < sds_jam_size; i++) {
- ret = rtl8365mb_sds_write(priv, sds_jam[i].reg,
- sds_jam[i].val);
+ ret = rtl8365mb_sds_write(priv, sds_index,
+ sds_jam[i].reg, sds_jam[i].val);
+ if (ret)
+ return ret;
+ }
+
+ /* Family-specific post-tuning configuration */
+ if (is_d) {
+ ret = regmap_update_bits(priv->map, RTL8365MB_D_FIBER_CFG2_REG,
+ RTL8365MB_D_FIBER_CFG2_RX_DISABLE_MASK,
+ RTL8365MB_D_FIBER_CFG2_RX_DISABLE_SDS0);
if (ret)
return ret;
+
+ misc_mask = RTL8365MB_D_SDS_MISC_CFG_MASK;
+ misc_val = RTL8365MB_D_SDS_MISC_PA33PC_EN |
+ RTL8365MB_D_SDS_MISC_PA12PC_EN |
+ RTL8365MB_D_SDS_MISC_MAC6_SEL_SDS0 | sds_mode;
+ } else {
+ /* Mux the SerDes to MAC8 in the requested mode */
+ misc_mask = RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK |
+ RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK;
+ misc_val = (sds_mode == RTL8365MB_EXT_PORT_MODE_SGMII) ?
+ RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK :
+ RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK;
}
- /* Mux the SerDes to MAC8 in the requested mode */
+ mutex_lock(&mb->sds_lock);
ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
- RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK |
- RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK,
- mode == RTL8365MB_EXT_PORT_MODE_SGMII ?
- RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK :
- RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK);
+ misc_mask, misc_val);
+ mutex_unlock(&mb->sds_lock);
if (ret)
return ret;
- val = mode << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);
- ret = regmap_update_bits(priv->map,
- RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id),
- RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),
- val);
- if (ret)
- return ret;
+ if (is_d) {
+ WRITE_ONCE(mb->sds_misc_target_val, misc_val);
+ } else {
+ val = sds_mode << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);
+ ret = regmap_update_bits(priv->map,
+ RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id),
+ RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),
+ val);
+ if (ret)
+ return ret;
+ }
/* Take the SerDes out of reset. The vendor driver does this only
* after the SerDes mux and the interface mode are configured.
*/
- ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_RESET,
+ ret = rtl8365mb_sds_write(priv, sds_index,
+ RTL8365MB_SDS_REG_RESET,
RTL8365MB_SDS_RESET_DEASSERT);
if (ret)
return ret;
@@ -1472,12 +1692,14 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
* This flushes the FIFOs and ensures a clean state for the link,
* preventing silent drops and CRC errors.
*/
- ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,
+ ret = rtl8365mb_sds_write(priv, sds_index,
+ RTL8365MB_SDS_REG_BMCR,
RTL8365MB_SDS_BMCR_DPRST_PHASE1);
if (ret)
return ret;
- ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,
+ ret = rtl8365mb_sds_write(priv, sds_index,
+ RTL8365MB_SDS_REG_BMCR,
RTL8365MB_SDS_BMCR_DPRST_PHASE2);
if (ret)
return ret;
@@ -1485,14 +1707,148 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
/* Keep SGMII in-band autonegotiation disabled: the link parameters are
* forced from rtl8365mb_pcs_link_up() instead.
*/
- ret = rtl8365mb_sds_read(priv, RTL8365MB_SDS_REG_NWAY, &val);
+ ret = rtl8365mb_sds_read(priv, sds_index,
+ RTL8365MB_SDS_REG_NWAY, &val);
if (ret)
return ret;
val &= ~RTL8365MB_SDS_NWAY_EN_MASK;
val |= RTL8365MB_SDS_NWAY_RESTART_MASK;
- return rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_NWAY, val);
+ ret = rtl8365mb_sds_write(priv, sds_index,
+ RTL8365MB_SDS_REG_NWAY, val);
+ if (ret)
+ return ret;
+
+ if (is_d) {
+ /* Start a new re-latch episode and kick off the first attempt;
+ * see rtl8365mb_sds_relatch_work() for why this cannot wait
+ * for phylink to ask again.
+ */
+ WRITE_ONCE(mb->sds_relatch_count, 1);
+ schedule_delayed_work(&mb->sds_relatch, 0);
+ }
+
+ return 0;
+}
+
+/* The family D receiver latches on a DISABLE -> mode edge in SDS_MISC
+ * rather than on the value, and only once the far-end MAC has brought up
+ * its half of the link. No local signal predicts that (bit 8 of the SDS
+ * link-status word does not change ahead of it), so the work cannot wait
+ * for it, only check afterwards whether an edge took. Nor does phylink
+ * ever call rtl8365mb_pcs_get_state() for this fixed-link port
+ * (mb->pcs.poll only arms phylink's own poll for in-band links), so this
+ * cannot rely on being polled at all. Instead, pcs_config() starts an
+ * episode and this work re-arms itself after every edge, its next run
+ * checking whether the edge took.
+ *
+ * An episode is capped at RTL8365MB_D_SDS_RELATCH_MAX_TRIES edges, spaced
+ * RTL8365MB_D_SDS_RELATCH_INTERVAL apart, so that a far end which never
+ * comes up costs a bounded burst of 20 ms parks. The run after the last
+ * edge only checks whether it took and reports failure; from then on one
+ * edge is driven per RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL, so a far end
+ * that comes up late is recovered without a reconfiguration. Once the
+ * receiver has latched the work just polls the link at the same slow
+ * interval; finding it down then starts a new episode, e.g. after the far
+ * end reset its PLL without a reconfiguration.
+ *
+ * sds_relatch_count is the number of the next attempt of the episode: 0
+ * while latched, 1..MAX_TRIES in budget, MAX_TRIES + 1 for the run that
+ * only reports, and above that once out of budget.
+ */
+static void rtl8365mb_sds_relatch_work(struct work_struct *work)
+{
+ struct rtl8365mb *mb = container_of(to_delayed_work(work),
+ struct rtl8365mb, sds_relatch);
+ struct realtek_priv *priv = mb->priv;
+ u32 park = (READ_ONCE(mb->sds_misc_target_val) &
+ ~RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK) |
+ RTL8365MB_D_PORT_SDS_MODE_DISABLE;
+ unsigned long delay = RTL8365MB_D_SDS_RELATCH_INTERVAL;
+ unsigned int attempts;
+ u16 status;
+ int ret;
+
+ /* The condition that queued this work may already be stale by the
+ * time it runs (e.g. an earlier edge from a prior attempt just
+ * latched). Parking an already-live link would drop it for no
+ * reason.
+ */
+ ret = rtl8365mb_sds_read(priv, RTL8365MB_D_SDS_EXT0_INDEX,
+ RTL8365MB_SDS_REG_LINK_STATUS, &status);
+ if (ret) {
+ dev_err_ratelimited(priv->dev,
+ "failed to read SerDes link status: %pe\n",
+ ERR_PTR(ret));
+ goto rearm;
+ }
+ if (status & RTL8365MB_SDS_LINK_STATUS_LINK_MASK) {
+ /* Latched: end the episode, keep watching for a later loss. */
+ WRITE_ONCE(mb->sds_relatch_count, 0);
+ delay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;
+ goto rearm;
+ }
+
+ attempts = READ_ONCE(mb->sds_relatch_count);
+ if (!attempts) {
+ dev_warn(priv->dev,
+ "SerDes link lost outside a reconfiguration; re-latching\n");
+ attempts = 1;
+ } else if (attempts == RTL8365MB_D_SDS_RELATCH_MAX_TRIES + 1) {
+ /* This run only checked whether the last in-budget edge
+ * took. The first out-of-budget edge comes one healthcheck
+ * interval from now, not on this pass.
+ */
+ WRITE_ONCE(mb->sds_relatch_count, attempts + 1);
+ dev_warn(priv->dev,
+ "SerDes did not latch after %u re-latch attempts; still retrying every %d s\n",
+ RTL8365MB_D_SDS_RELATCH_MAX_TRIES,
+ RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL / HZ);
+ delay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;
+ goto rearm;
+ }
+
+ if (attempts <= RTL8365MB_D_SDS_RELATCH_MAX_TRIES)
+ WRITE_ONCE(mb->sds_relatch_count, attempts + 1);
+ else
+ /* Past the fast-retry budget. The receiver latches only on a
+ * DISABLE -> mode edge and nothing re-runs pcs_config() for a
+ * fixed-link port, so keep driving one edge per interval.
+ */
+ delay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;
+
+ mutex_lock(&mb->sds_lock);
+ ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
+ RTL8365MB_D_SDS_MISC_CFG_MASK, park);
+ mutex_unlock(&mb->sds_lock);
+ if (ret) {
+ dev_err_ratelimited(priv->dev,
+ "failed to park SDS_MISC: %pe\n",
+ ERR_PTR(ret));
+ goto rearm;
+ }
+
+ usleep_range(20000, 21000);
+
+ mutex_lock(&mb->sds_lock);
+ ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
+ RTL8365MB_D_SDS_MISC_CFG_MASK,
+ READ_ONCE(mb->sds_misc_target_val));
+ mutex_unlock(&mb->sds_lock);
+ if (ret) {
+ dev_err_ratelimited(priv->dev,
+ "failed to restore SDS_MISC: %pe\n",
+ ERR_PTR(ret));
+ goto rearm;
+ }
+
+ dev_dbg(priv->dev, "SerDes re-latch edge driven (attempt %u)\n",
+ attempts);
+
+ /* This edge may not have taken either; the next run checks. */
+rearm:
+ schedule_delayed_work(&mb->sds_relatch, delay);
}
static bool rtl8365mb_interface_is_serdes(phy_interface_t interface)
@@ -1517,10 +1873,14 @@ static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs,
{
struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
struct realtek_priv *priv = mb->priv;
+ u8 sds_index = 0;
u16 status;
+ bool is_d;
u32 val;
int ret;
+ is_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;
+
/* In-band autonegotiation is not implemented, so the link parameters are
* forced from rtl8365mb_pcs_link_up(). The real link state must still be
* read from the SerDes itself: the embedded DW8051 microcontroller that
@@ -1528,7 +1888,11 @@ static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs,
* rtl8365mb_pcs_config()), so the link status register can be read
* directly through the SDS_INDACS window without racing the auto-poll.
*/
- ret = rtl8365mb_sds_read(priv, RTL8365MB_SDS_REG_LINK_STATUS, &status);
+ if (is_d)
+ sds_index = RTL8365MB_D_SDS_EXT0_INDEX;
+
+ ret = rtl8365mb_sds_read(priv, sds_index,
+ RTL8365MB_SDS_REG_LINK_STATUS, &status);
if (ret) {
state->link = false;
return;
@@ -1539,10 +1903,19 @@ static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs,
if (!state->link)
return;
+ if (is_d) {
+ state->duplex = DUPLEX_FULL;
+ state->speed = state->interface == PHY_INTERFACE_MODE_2500BASEX ?
+ SPEED_2500 : SPEED_1000;
+ return;
+ }
+
/* The speed and duplex are forced; read them back from the values
* programmed into the SerDes MISC register.
*/
+ mutex_lock(&mb->sds_lock);
ret = regmap_read(priv->map, RTL8365MB_SDS_MISC_REG, &val);
+ mutex_unlock(&mb->sds_lock);
if (ret) {
state->link = false;
return;
@@ -1580,6 +1953,12 @@ static void rtl8365mb_pcs_link_up(struct phylink_pcs *pcs,
u32 r_speed;
int ret;
+ /* Family D forces the external MAC ability from mac_link_up(); its
+ * SDS_MISC fields do not share the family C link-force layout.
+ */
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)
+ return;
+
/* The speed field has no value for 2.5 Gbps: the rate is determined by
* the HSGMII SerDes configuration, and the vendor driver programs the
* 1 Gbps value here.
@@ -1606,7 +1985,9 @@ static void rtl8365mb_pcs_link_up(struct phylink_pcs *pcs,
* force from rtl8365mb_phylink_mac_link_up(), where the resolved pause
* modes are known.
*/
+ mutex_lock(&mb->sds_lock);
ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG, mask, val);
+ mutex_unlock(&mb->sds_lock);
if (ret) {
dev_err(priv->dev, "failed to force SerDes link: %pe\n",
ERR_PTR(ret));
@@ -1632,23 +2013,31 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
u32 r_duplex;
u32 r_speed;
u32 r_link;
+ bool is_d;
int val;
int ret;
if (!extint)
return -ENODEV;
+ is_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;
if (link) {
/* Force the link up with the desired configuration */
r_link = 1;
r_rx_pause = rx_pause ? 1 : 0;
r_tx_pause = tx_pause ? 1 : 0;
- /* The speed field has no value for 2.5 Gbps: the rate is
- * determined by the HSGMII SerDes configuration, and the
- * vendor driver programs the 1 Gbps value here.
- */
- if (speed == SPEED_2500 || speed == SPEED_1000) {
+ if (speed == SPEED_2500) {
+ if (is_d) {
+ r_speed = RTL8365MB_D_PORT_SPEED_2500M;
+ } else {
+ /* The speed field has no value for 2.5 Gbps: the rate is
+ * determined by the HSGMII SerDes configuration, and the
+ * vendor driver programs the 1 Gbps value here.
+ */
+ r_speed = RTL8365MB_PORT_SPEED_1000M;
+ }
+ } else if (speed == SPEED_1000) {
r_speed = RTL8365MB_PORT_SPEED_1000M;
} else if (speed == SPEED_100) {
r_speed = RTL8365MB_PORT_SPEED_100M;
@@ -1678,8 +2067,7 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
r_duplex = 0;
}
- val = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1) |
- FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK,
+ val = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK,
r_tx_pause) |
FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK,
r_rx_pause) |
@@ -1687,11 +2075,33 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK,
r_duplex) |
FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK, r_speed);
- ret = regmap_write(priv->map,
- RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(extint->id),
- val);
- if (ret)
- return ret;
+
+ if (is_d) {
+ /* Speed is 3 bits on family D: bits[1:0] go into FORCE_SPEED,
+ * bit[2] goes into FORCE_SPEED2 (bit 12); only 2500M sets it,
+ * bit 13 of the field is unused.
+ */
+ val |= FIELD_PREP(RTL8365MB_D_DIGITAL_INTERFACE_FORCE_SPEED2_MASK,
+ r_speed >> RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_WIDTH);
+ ret = regmap_write(priv->map,
+ RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG(port),
+ val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->map,
+ RTL8365MB_D_DIGITAL_INTERFACE_FORCE_REG_EN(port),
+ RTL8365MB_D_DIGITAL_INTERFACE_FORCE_EN_ALL_MASK);
+ if (ret)
+ return ret;
+ } else {
+ val |= FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1);
+ ret = regmap_write(priv->map,
+ RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(extint->id),
+ val);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -1876,7 +2286,8 @@ static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config,
* rtl8365mb_pcs_link_up() because pcs_link_up() carries no
* pause information.
*/
- if (rtl8365mb_interface_is_serdes(interface)) {
+ if (rtl8365mb_interface_is_serdes(interface) &&
+ rtl8365mb_get_family(priv) != RTL8365MB_FAMILY_D) {
u32 val = 0;
if (tx_pause)
@@ -1884,11 +2295,13 @@ static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config,
if (rx_pause)
val |= RTL8365MB_SDS_MISC_SGMII_RXFC_MASK;
+ mutex_lock(&mb->sds_lock);
ret = regmap_update_bits(priv->map,
RTL8365MB_SDS_MISC_REG,
RTL8365MB_SDS_MISC_SGMII_TXFC_MASK |
RTL8365MB_SDS_MISC_SGMII_RXFC_MASK,
val);
+ mutex_unlock(&mb->sds_lock);
if (ret)
dev_err(priv->dev,
"failed to force SerDes pause modes on port %d: %pe\n",
@@ -3100,6 +3513,10 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
*/
mb->pcs.poll = true;
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)
+ INIT_DELAYED_WORK(&mb->sds_relatch,
+ rtl8365mb_sds_relatch_work);
+
ret = rtl8365mb_reset_chip(priv);
if (ret) {
dev_err(priv->dev, "failed to reset chip: %pe\n",
@@ -3284,6 +3701,10 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
static void rtl8365mb_teardown(struct dsa_switch *ds)
{
struct realtek_priv *priv = ds->priv;
+ struct rtl8365mb *mb = priv->chip_data;
+
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)
+ cancel_delayed_work_sync(&mb->sds_relatch);
rtl8365mb_stats_teardown(priv);
rtl8365mb_irq_teardown(priv);
@@ -3349,7 +3770,15 @@ static int rtl8365mb_detect(struct realtek_priv *priv)
dev_info(priv->dev, "found an %s switch\n", mb->chip_info->name);
- priv->num_ports = RTL8365MB_MAX_NUM_PORTS;
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)
+ priv->num_ports = RTL8365MB_D_MAX_NUM_PORTS;
+ else
+ priv->num_ports = RTL8365MB_MAX_NUM_PORTS;
+
+ ret = devm_mutex_init(priv->dev, &mb->sds_lock);
+ if (ret)
+ return ret;
+
mb->priv = priv;
mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS;
mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL;
diff --git a/drivers/net/dsa/realtek/rtl8365mb_vlan.c b/drivers/net/dsa/realtek/rtl8365mb_vlan.c
index 8d49ffadce926..da517b1aae208 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_vlan.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_vlan.c
@@ -48,12 +48,14 @@
#include "rtl8365mb_vlan.h"
#include "rtl8365mb_table.h"
+#include "rtl8365mb.h"
#include <linux/if_bridge.h>
#include <linux/lockdep.h>
#include <linux/regmap.h>
/* CVLAN (i.e. VLAN4k) table entry layout, u16[3] */
#define RTL8365MB_CVLAN_ENTRY_SIZE 3 /* 48-bits */
+#define RTL8365MB_D_CVLAN_ENTRY_SIZE 2 /* 32-bits, no 3rd word */
#define RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK GENMASK(7, 0)
#define RTL8365MB_CVLAN_MBR_LO_MASK GENMASK(7, 0)
#define RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK GENMASK(15, 8)
@@ -65,6 +67,10 @@
#define RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK GENMASK(13, 9)
#define RTL8365MB_CVLAN_METERIDX_LO_MASK GENMASK(4, 0)
#define RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK GENMASK(14, 14)
+#define RTL8365MB_D_CVLAN_ENTRY_D1_SVLAN_CHK_IVL_SVL_MASK \
+ GENMASK(2, 2)
+#define RTL8365MB_D_CVLAN_ENTRY_D1_IVL_EN_MASK GENMASK(3, 3)
+#define RTL8365MB_D_CVLAN_ENTRY_D1_FID_MASK GENMASK(1, 0)
/* extends RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK */
#define RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK GENMASK(2, 0)
#define RTL8365MB_CVLAN_MBR_HI_MASK GENMASK(10, 8)
@@ -113,6 +119,11 @@
#define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(_p) \
(0x1F << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p))
+#define RTL8365MB_D_VLAN_PVID_CTRL_BASE 0x0700
+#define RTL8365MB_D_VLAN_PVID_CTRL_REG(port) \
+ (RTL8365MB_D_VLAN_PVID_CTRL_BASE + (port))
+#define RTL8365MB_D_VLAN_PVID_CTRL_MASK GENMASK(11, 0)
+
/* Frame type filtering registers */
#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE 0x07aa
#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port) \
@@ -185,13 +196,16 @@ struct rtl8365mb_vlanmc {
static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid,
struct rtl8365mb_vlan4k *vlan4k)
{
+ bool is_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;
+ size_t entry_size = is_d ? RTL8365MB_D_CVLAN_ENTRY_SIZE :
+ RTL8365MB_CVLAN_ENTRY_SIZE;
u16 data[RTL8365MB_CVLAN_ENTRY_SIZE];
int val;
int ret;
ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN,
RTL8365MB_TABLE_OP_READ, &vid, 0, 0,
- data, ARRAY_SIZE(data));
+ data, entry_size);
if (ret)
return ret;
@@ -199,33 +213,51 @@ static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid,
memset(vlan4k, 0, sizeof(*vlan4k));
vlan4k->vid = vid;
+ /* member/untag: d0[7:0]/d0[15:8] on both families. Family C
+ * extends these into data[2] bits [2:0]/[5:3] for its 9th-11th
+ * ports; family D's die has only 8 ports and no third table
+ * word, so data[2] does not exist there and must not be read.
+ */
val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, data[0]);
vlan4k->member = FIELD_PREP(RTL8365MB_CVLAN_MBR_LO_MASK, val);
- val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, data[2]);
- vlan4k->member |= FIELD_PREP(RTL8365MB_CVLAN_MBR_HI_MASK, val);
+ if (!is_d) {
+ val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, data[2]);
+ vlan4k->member |= FIELD_PREP(RTL8365MB_CVLAN_MBR_HI_MASK, val);
+ }
val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, data[0]);
vlan4k->untag = FIELD_PREP(RTL8365MB_CVLAN_UNTAG_LO_MASK, val);
- val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, data[2]);
- vlan4k->untag |= FIELD_PREP(RTL8365MB_CVLAN_UNTAG_HI_MASK, val);
-
- vlan4k->fid = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, data[1]);
- vlan4k->priority_en =
- FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, data[1]);
- vlan4k->priority =
- FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, data[1]);
- vlan4k->policing_en =
- FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, data[1]);
-
- val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, data[1]);
- val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);
- vlan4k->meteridx = val;
- val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, data[2]);
- val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);
- vlan4k->meteridx |= val;
-
- vlan4k->ivl_en =
- FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, data[1]);
+ if (!is_d) {
+ val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, data[2]);
+ vlan4k->untag |= FIELD_PREP(RTL8365MB_CVLAN_UNTAG_HI_MASK, val);
+ }
+
+ if (is_d) {
+ vlan4k->fid = FIELD_GET(RTL8365MB_D_CVLAN_ENTRY_D1_FID_MASK, data[1]);
+ /* Family D has no priority/meter fields in this entry -
+ * left zeroed by the memset() above.
+ */
+ vlan4k->ivl_en =
+ FIELD_GET(RTL8365MB_D_CVLAN_ENTRY_D1_IVL_EN_MASK, data[1]);
+ } else {
+ vlan4k->fid = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, data[1]);
+ vlan4k->priority_en =
+ FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, data[1]);
+ vlan4k->priority =
+ FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, data[1]);
+ vlan4k->policing_en =
+ FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, data[1]);
+
+ val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, data[1]);
+ val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);
+ vlan4k->meteridx = val;
+ val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, data[2]);
+ val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);
+ vlan4k->meteridx |= val;
+
+ vlan4k->ivl_en =
+ FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, data[1]);
+ }
return 0;
}
@@ -233,6 +265,9 @@ static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid,
static int rtl8365mb_vlan_4k_write(struct realtek_priv *priv,
const struct rtl8365mb_vlan4k *vlan4k)
{
+ bool is_d = rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D;
+ size_t entry_size = is_d ? RTL8365MB_D_CVLAN_ENTRY_SIZE :
+ RTL8365MB_CVLAN_ENTRY_SIZE;
u16 data[RTL8365MB_CVLAN_ENTRY_SIZE] = { 0 };
u16 vid;
int val;
@@ -244,36 +279,52 @@ static int rtl8365mb_vlan_4k_write(struct realtek_priv *priv,
val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_LO_MASK, vlan4k->untag);
data[0] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, val);
- data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, vlan4k->fid);
- data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK,
- vlan4k->priority_en);
- data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK,
- vlan4k->priority);
- data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK,
- vlan4k->policing_en);
-
- /* FIELD_* does not play nice with struct bitfield. */
- val = vlan4k->meteridx;
- val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);
- data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, val);
-
- data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK,
- vlan4k->ivl_en);
-
- val = FIELD_GET(RTL8365MB_CVLAN_MBR_HI_MASK, vlan4k->member);
- data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, val);
-
- val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_HI_MASK, vlan4k->untag);
- data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, val);
-
- val = vlan4k->meteridx;
- val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);
- data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, val);
+ if (is_d) {
+ /* The chip supports both IVL and SVL, but the caller (see
+ * rtl8365mb_vlan_4k_port_set()) never requests SVL, so both
+ * IVL/SVL selector bits are forced here rather than threaded
+ * through from vlan4k->ivl_en, which family C does honor.
+ */
+ data[1] |= FIELD_PREP(RTL8365MB_D_CVLAN_ENTRY_D1_IVL_EN_MASK, 1) |
+ FIELD_PREP(RTL8365MB_D_CVLAN_ENTRY_D1_SVLAN_CHK_IVL_SVL_MASK, 1);
+ data[1] |= FIELD_PREP(RTL8365MB_D_CVLAN_ENTRY_D1_FID_MASK, vlan4k->fid);
+ /* No priority/meter/member-untag-extension fields exist in
+ * family D's 2-word entry - data[1] and data[0] above are
+ * the whole entry, and data[2] is not part of it at all.
+ */
+ } else {
+ val = vlan4k->fid;
+ data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, val);
+ data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK,
+ vlan4k->priority_en);
+ data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK,
+ vlan4k->priority);
+ data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK,
+ vlan4k->policing_en);
+
+ /* FIELD_* does not play nice with struct bitfield. */
+ val = vlan4k->meteridx;
+ val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);
+ data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, val);
+
+ data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK,
+ vlan4k->ivl_en);
+
+ val = FIELD_GET(RTL8365MB_CVLAN_MBR_HI_MASK, vlan4k->member);
+ data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, val);
+
+ val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_HI_MASK, vlan4k->untag);
+ data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, val);
+
+ val = vlan4k->meteridx;
+ val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);
+ data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, val);
+ }
vid = vlan4k->vid;
return rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN,
RTL8365MB_TABLE_OP_WRITE, &vid, 0, 0,
- data, ARRAY_SIZE(data));
+ data, entry_size);
}
static int
@@ -679,11 +730,22 @@ int rtl8365mb_vlan_port_get_pvid(struct realtek_priv *priv, int port, u16 *pvid)
u8 vlanmc_idx;
int ret;
- ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx, &vlanmc);
- if (ret)
- return ret;
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D) {
+ u32 data;
+
+ ret = regmap_read(priv->map, RTL8365MB_D_VLAN_PVID_CTRL_REG(port), &data);
+ if (ret)
+ return ret;
+
+ *pvid = data & RTL8365MB_D_VLAN_PVID_CTRL_MASK;
+ } else {
+ ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx, &vlanmc);
+ if (ret)
+ return ret;
+
+ *pvid = vlanmc.evid;
+ }
- *pvid = vlanmc.evid;
return 0;
}
@@ -748,6 +810,64 @@ rtl8365mb_vlan_port_set_framefilter(struct realtek_priv *priv,
val);
}
+/*
+ * rtl8365mb_vlan_pvid_port_set_direct() - Configure a port's PVID as a raw
+ * VID written to its dedicated register, for chip families without a
+ * working VLAN MC table (RTL8365MB_FAMILY_D)
+ *
+ * Reads back the previous PVID and frame filter first so both can be
+ * restored if enabling the new PVID fails partway through, matching
+ * the rollback behavior of the family-C implementation above.
+ *
+ * Context: Can sleep. Must be called with &priv->vlan_lock held.
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int rtl8365mb_vlan_pvid_port_set_direct(struct realtek_priv *priv,
+ int port, u16 vid)
+{
+ enum rtl8365mb_frame_ingress prev_accepted_frame;
+ u32 prev_pvid;
+ int ret;
+
+ ret = regmap_read(priv->map, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),
+ &prev_pvid);
+ if (ret) {
+ dev_err(priv->dev, "Failed to read current PVID\n");
+ return ret;
+ }
+ prev_pvid &= RTL8365MB_D_VLAN_PVID_CTRL_MASK;
+
+ ret = rtl8365mb_vlan_port_get_framefilter(priv, port, &prev_accepted_frame);
+ if (ret) {
+ dev_err(priv->dev, "Failed to get current framefilter\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(priv->map, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),
+ RTL8365MB_D_VLAN_PVID_CTRL_MASK,
+ vid & RTL8365MB_D_VLAN_PVID_CTRL_MASK);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set port PVID\n");
+ return ret;
+ }
+
+ /* Changing accept frame is what enables PVID (if not enabled before) */
+ ret = rtl8365mb_vlan_port_set_framefilter(priv, port,
+ RTL8365MB_FRAME_TYPE_ANY_FRAME);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set port frame filter\n");
+ goto undo_pvid_write;
+ }
+
+ return 0;
+
+undo_pvid_write:
+ (void)regmap_update_bits(priv->map, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),
+ RTL8365MB_D_VLAN_PVID_CTRL_MASK, prev_pvid);
+ (void)rtl8365mb_vlan_port_set_framefilter(priv, port, prev_accepted_frame);
+ return ret;
+}
+
/*
* rtl8365mb_vlan_pvid_port_set() - Configure a port's PVID and associated
* VLANMC entry
@@ -777,6 +897,13 @@ int rtl8365mb_vlan_pvid_port_set(struct dsa_switch *ds, int port, u16 vid,
lockdep_assert_held(&priv->vlan_lock);
+ /* This chip family has no VLAN MC table - PVID is a raw VID in a
+ * dedicated per-port register, and there is no separate membership
+ * table entry to allocate/track.
+ */
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)
+ return rtl8365mb_vlan_pvid_port_set_direct(priv, port, vid);
+
/* Read the old PVID exclusively to undo in case of error */
ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &prev_vlanmc_idx,
&prev_vlanmc);
@@ -856,6 +983,74 @@ int rtl8365mb_vlan_pvid_port_set(struct dsa_switch *ds, int port, u16 vid,
return ret;
}
+/*
+ * rtl8365mb_vlan_pvid_port_clear_direct() - Remove a port's raw-VID PVID
+ * configuration, for chip families without a working VLAN MC table
+ * (RTL8365MB_FAMILY_D)
+ *
+ * Reads back the previous frame filter first so it can be restored if
+ * clearing the PVID register fails.
+ *
+ * Context: Can sleep. Must be called with &priv->vlan_lock held.
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int rtl8365mb_vlan_pvid_port_clear_direct(struct dsa_switch *ds,
+ int port, u16 vid)
+{
+ enum rtl8365mb_frame_ingress prev_accepted_frame;
+ struct realtek_priv *priv = ds->priv;
+ bool filtering;
+ u32 cur_pvid;
+ int ret;
+
+ ret = regmap_read(priv->map, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),
+ &cur_pvid);
+ if (ret) {
+ dev_err(priv->dev, "Failed to read current PVID\n");
+ return ret;
+ }
+
+ /* Port is not using this VID as PVID. Nothing to remove. */
+ if ((cur_pvid & RTL8365MB_D_VLAN_PVID_CTRL_MASK) != vid)
+ return 0;
+
+ filtering = dsa_port_is_vlan_filtering(dsa_to_port(ds, port));
+
+ /* Changing accept frame is what really removes PVID. But only do
+ * that if VLAN filtering is enabled.
+ */
+ if (filtering) {
+ ret = rtl8365mb_vlan_port_get_framefilter(priv, port,
+ &prev_accepted_frame);
+ if (ret) {
+ dev_err(priv->dev, "Failed to get current framefilter\n");
+ return ret;
+ }
+
+ ret = rtl8365mb_vlan_port_set_framefilter(
+ priv, port, RTL8365MB_FRAME_TYPE_TAGGED_ONLY);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set port frame filter\n");
+ return ret;
+ }
+ }
+
+ ret = regmap_update_bits(priv->map, RTL8365MB_D_VLAN_PVID_CTRL_REG(port),
+ RTL8365MB_D_VLAN_PVID_CTRL_MASK, 0);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set port PVID to 0\n");
+ goto undo_set_framefilter;
+ }
+
+ return 0;
+
+undo_set_framefilter:
+ if (filtering)
+ (void)rtl8365mb_vlan_port_set_framefilter(priv, port,
+ prev_accepted_frame);
+ return ret;
+}
+
/*
* rtl8365mb_vlan_pvid_port_clear() - Remove a port's PVID configuration
* @ds: dsa switch instance
@@ -879,6 +1074,9 @@ int rtl8365mb_vlan_pvid_port_clear(struct dsa_switch *ds, int port, u16 vid)
lockdep_assert_held(&priv->vlan_lock);
+ if (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)
+ return rtl8365mb_vlan_pvid_port_clear_direct(ds, port, vid);
+
ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx,
&vlanmc);
if (ret) {
|
| 3/2 | 2026/10/09 18:09 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25491 output: 36 thoughts: 243 Thoughts: **Analyzing a Linux Kernel Commit for Fuzzing Potential** Okay, so I need to assess this Linux kernel commit and decide if it's a good candidate for syzkaller fuzzing. Let's see... the commit in question is `cab836bc4ba51e1fb0729e5b6440b455d284fefe`. Right, the files modified are: `drivers/net/dsa/realtek/rtl8365mb.h`, `drivers/net/dsa/realtek/rtl8365mb_main.c`, and `drivers/net/dsa/realtek/rtl8365mb_vlan.c`. Those files point to a Realtek DSA (Distributed Switch Architecture) switch driver, specifically the `rtl8365mb` driver. Now, to determine fuzzing potential, I need to understand the attack surface. How is this driver accessed? Is it an MDIO, SMI, or platform/OF driver? How is the `rtl8365mb` driver probed and initialized? I'll probably need to use `grepper` to search the kernel source for probe/driver registration or `realtek dsa` to understand how this driver is instantiated and attached. The key is finding how the driver receives input and processes it. That will inform whether fuzzing it is worth the effort, and if so, how to structure the fuzzing setup. |
| 4/2 | 2026/10/09 18:09 | tool | grepper |
0mArgs:{
"Expression": "rtl8365mb",
"PathPrefixes": [
"drivers/net/dsa/realtek"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 1891 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (10 files in total):\ndrivers/net/dsa/realtek/Makefile:6\ndrivers/net/dsa/realtek/realtek.h:1\ndrivers/net/dsa/realtek/rtl8365mb.h:2\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:41\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:7\ndrivers/net/dsa/realtek/rtl8365mb_main.c:413\ndrivers/net/dsa/realtek/rtl8365mb_table.c:12\ndrivers/net/dsa/realtek/rtl8365mb_table.h:15\ndrivers/net/dsa/realtek/rtl8365mb_vlan.c:101\ndrivers/net/dsa/realtek/rtl8365mb_vlan.h:11\n\ndrivers/net/dsa/realtek/Makefile=17=endif\ndrivers/net/dsa/realtek/Makefile:18:obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o\ndrivers/net/dsa/realtek/Makefile:19:rtl8365mb-objs := rtl8365mb_main.o \\\ndrivers/net/dsa/realtek/Makefile:20:\t\t rtl8365mb_table.o \\\ndrivers/net/dsa/realtek/Makefile:21:\t\t rtl8365mb_vlan.o \\\ndrivers/net/dsa/realtek/Makefile:22:\t\t rtl8365mb_l2.o \\\ndrivers/net/dsa/realtek/Makefile:23:# end of rtl8365mb-objs\n--\ndrivers/net/dsa/realtek/realtek.h=197=extern const struct realtek_variant rtl8366rb_variant;\ndrivers/net/dsa/realtek/realtek.h:198:extern const struct realtek_variant rtl8365mb_variant;\ndrivers/net/dsa/realtek/realtek.h-199-\n--\ndrivers/net/dsa/realtek/rtl8365mb.h-6-\ndrivers/net/dsa/realtek/rtl8365mb.h:7:enum rtl8365mb_family {\ndrivers/net/dsa/realtek/rtl8365mb.h-8-\tRTL8365MB_FAMILY_C,\n--\ndrivers/net/dsa/realtek/rtl8365mb.h-11-\ndrivers/net/dsa/realtek/rtl8365mb.h:12:enum rtl8365mb_family rtl8365mb_get_family(struct realtek_priv *priv);\ndrivers/net/dsa/realtek/rtl8365mb.h-13-\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-1-// SPDX-License-Identifier: GPL-2.0\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:2:/* Forwarding and multicast database interface for the rtl8365mb switch family\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-3- *\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-8-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:9:#include \"rtl8365mb_l2.h\"\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:10:#include \"rtl8365mb_table.h\"\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-11-#include \u003clinux/regmap.h\u003e\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-91-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:92:struct rtl8365mb_l2_uc_key {\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-93-\tu8 mac_addr[ETH_ALEN];\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-99-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:100:struct rtl8365mb_l2_uc {\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:101:\tstruct rtl8365mb_l2_uc_key key;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-102-\tu8 port;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-113-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:114:struct rtl8365mb_l2_mc_key {\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-115-\tu8 mac_addr[ETH_ALEN];\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-122-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:123:struct rtl8365mb_l2_mc {\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:124:\tstruct rtl8365mb_l2_mc_key key;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-125-\tu16 member;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-133-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:134:static void rtl8365mb_l2_data_to_uc(const u16 *data, struct rtl8365mb_l2_uc *uc)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-135-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-163-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:164:static void rtl8365mb_l2_uc_to_data(const struct rtl8365mb_l2_uc *uc, u16 *data)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-165-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-202-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:203:static void rtl8365mb_l2_data_to_mc(const u16 *data, struct rtl8365mb_l2_mc *mc)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-204-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-230-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:231:static void rtl8365mb_l2_mc_to_data(const struct rtl8365mb_l2_mc *mc, u16 *data)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-232-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-261-/*\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:262: * rtl8365mb_l2_get_next_uc() - get the next Unicast L2 entry\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-263- * @priv: realtek_priv pointer\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-281- **/\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:282:int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-283-\t\t\t struct realtek_fdb_entry *entry)\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-285-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:286:\tstruct rtl8365mb_l2_uc uc;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-287-\tint ret;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-288-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:289:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-290-\t\t\t\t RTL8365MB_TABLE_OP_READ, addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-295-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:296:\trtl8365mb_l2_data_to_uc(data, \u0026uc);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-297-\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-304-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:305:int rtl8365mb_l2_add_uc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-306-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-309-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:310:\tstruct rtl8365mb_l2_uc uc = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-311-\tu16 addr;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-328-\tuc.age = 1;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:329:\trtl8365mb_l2_uc_to_data(\u0026uc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-330-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-331-\t/* add the new entry or update an existing one */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:332:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-333-\t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-344-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:345:int rtl8365mb_l2_del_uc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-346-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-349-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:350:\tstruct rtl8365mb_l2_uc uc = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-351-\tu16 addr;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-360-\tuc.age = 0;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:361:\trtl8365mb_l2_uc_to_data(\u0026uc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-362-\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-367-\t */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:368:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-369-\t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-383-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:384:int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-385-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-448-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:449:int rtl8365mb_l2_add_mc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-450-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-453-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:454:\tstruct rtl8365mb_l2_mc mc = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-455-\tu16 addr;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-465-\tmc.is_static = 1;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:466:\trtl8365mb_l2_mc_to_data(\u0026mc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-467-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-468-\t/* First look for an existing entry (to get existing port members) */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:469:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-470-\t\t\t\t RTL8365MB_TABLE_OP_READ, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-474-\t\t/* There is already an entry... */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:475:\t\trtl8365mb_l2_data_to_mc(data, \u0026mc);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-476-\t\tdev_dbg(priv-\u003edev,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-489-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:490:\t\trtl8365mb_l2_mc_to_data(\u0026mc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-491-\t} else if (ret == -ENOENT) {\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-507-\t/* add the new entry or update an existing one */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:508:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-509-\t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-519-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:520:int rtl8365mb_l2_del_mc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-521-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-524-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:525:\tstruct rtl8365mb_l2_mc mc = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-526-\tu16 addr;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-531-\tmc.key.ivl = true;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:532:\trtl8365mb_l2_mc_to_data(\u0026mc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-533-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-534-\t/* First look for an existing entry (to get existing port members) */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:535:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-536-\t\t\t\t RTL8365MB_TABLE_OP_READ, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-549-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:550:\trtl8365mb_l2_data_to_mc(data, \u0026mc);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-551-\tdev_dbg(priv-\u003edev,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-567-\t}\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:568:\trtl8365mb_l2_mc_to_data(\u0026mc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-569-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-570-\t/* update the existing entry. */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:571:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-572-\t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-1-/* SPDX-License-Identifier: GPL-2.0 */\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:2:/* Forwarding and multicast database interface for the rtl8365mb switch family\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-3- *\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-14-\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:15:int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-16-\t\t\t struct realtek_fdb_entry *entry);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:17:int rtl8365mb_l2_add_uc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-18-\t\t\tconst unsigned char addr[static ETH_ALEN],\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-19-\t\t\tu16 efid, u16 vid);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:20:int rtl8365mb_l2_del_uc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-21-\t\t\tconst unsigned char addr[static ETH_ALEN],\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-22-\t\t\tu16 efid, u16 vid);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:23:int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-24-\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:25:int rtl8365mb_l2_add_mc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-26-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-27-\t\t\tu16 vid);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:28:int rtl8365mb_l2_del_mc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-29-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-64- * require the rtl8367d vendor driver. With all this uncertainty, the driver has\ndrivers/net/dsa/realtek/rtl8365mb_main.c:65: * been modestly named rtl8365mb. Future implementors may wish to rename things\ndrivers/net/dsa/realtek/rtl8365mb_main.c-66- * accordingly.\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-112-#include \"rtl83xx.h\"\ndrivers/net/dsa/realtek/rtl8365mb_main.c:113:#include \"rtl8365mb_l2.h\"\ndrivers/net/dsa/realtek/rtl8365mb_main.c:114:#include \"rtl8365mb_vlan.h\"\ndrivers/net/dsa/realtek/rtl8365mb_main.c:115:#include \"rtl8365mb.h\"\ndrivers/net/dsa/realtek/rtl8365mb_main.c-116-\n\n"
} |
| 5/2 | 2026/10/09 18:09 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32699 output: 38 thoughts: 48 Thoughts: Okay, let's dive into this. I need to understand how `rtl8365mb_variant` is integrated and initialized within the Realtek DSA driver framework. My initial approach is to perform a targeted code search. I'll execute a `grep` command specifically within the `drivers/net/dsa/realtek/` directory, focusing my search query on `rtl8365mb_variant`. This should efficiently reveal where and how this particular variable or structure is declared, used within functions, and ultimately, how it's probed or accessed during device initialization. I expect this will quickly illuminate the data flow and operational context surrounding `rtl8365mb_variant`. |
| 6/2 | 2026/10/09 18:09 | tool | grepper |
0mArgs:{
"Expression": "rtl8365mb_variant",
"PathPrefixes": [
"drivers/net/dsa/realtek"
]
}Results: {
"Output": "drivers/net/dsa/realtek/realtek.h=197=extern const struct realtek_variant rtl8366rb_variant;\ndrivers/net/dsa/realtek/realtek.h:198:extern const struct realtek_variant rtl8365mb_variant;\ndrivers/net/dsa/realtek/realtek.h-199-\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c=3901=static const char *const rtl8365mb_supplies[] = {\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3904-\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3905:const struct realtek_variant rtl8365mb_variant = {\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3906-\t.ds_ops = \u0026rtl8365mb_switch_ops,\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c=3917=static const struct of_device_id rtl8365mb_of_match[] = {\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3918:\t{ .compatible = \"realtek,rtl8365mb\", .data = \u0026rtl8365mb_variant, },\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3919-\t{ /* sentinel */ }\n"
} |
| 7/2 | 2026/10/09 18:09 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33337 output: 35 thoughts: 35 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating `rtl8365mb_variant` and `rtl8365mb_of_match` Kernel References** Right, so, before we proceed further, I need to get a clear picture of the landscape. Specifically, I should check all the kernel code where either `rtl8365mb_variant` or `rtl8365mb_of_match` are used. This isn't just a casual search; I'm looking for *all* references. That means function calls, data structure definitions (if any), macro usage, driver probes – everything. It's the only way to build a complete picture of the dependencies, usage patterns, and potential impact of any changes to these identifiers. This thorough approach will prevent me from introducing regressions due to unforeseen side effects when I come to modify this code. I need to understand the full scope before I even consider making a change. I need to be methodical. |
| 8/2 | 2026/10/09 18:09 | tool | grepper |
0mArgs:{
"Expression": "(rtl8365mb_variant|rtl8365mb_of_match)"
}Results: {
"Output": "drivers/net/dsa/realtek/realtek.h=197=extern const struct realtek_variant rtl8366rb_variant;\ndrivers/net/dsa/realtek/realtek.h:198:extern const struct realtek_variant rtl8365mb_variant;\ndrivers/net/dsa/realtek/realtek.h-199-\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c=3901=static const char *const rtl8365mb_supplies[] = {\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3904-\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3905:const struct realtek_variant rtl8365mb_variant = {\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3906-\t.ds_ops = \u0026rtl8365mb_switch_ops,\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3916-\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3917:static const struct of_device_id rtl8365mb_of_match[] = {\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3918:\t{ .compatible = \"realtek,rtl8365mb\", .data = \u0026rtl8365mb_variant, },\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3919-\t{ /* sentinel */ }\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3920-};\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3921:MODULE_DEVICE_TABLE(of, rtl8365mb_of_match);\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3922-\ndrivers/net/dsa/realtek/rtl8365mb_main.c=3923=static struct platform_driver rtl8365mb_smi_driver = {\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3925-\t\t.name = \"rtl8365mb-smi\",\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3926:\t\t.of_match_table = rtl8365mb_of_match,\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3927-\t},\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c=3933=static struct mdio_driver rtl8365mb_mdio_driver = {\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3935-\t\t.name = \"rtl8365mb-mdio\",\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3936:\t\t.of_match_table = rtl8365mb_of_match,\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3937-\t},\n"
} |
| 9/2 | 2026/10/09 18:09 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34492 output: 49 thoughts: 138 Thoughts: Okay, let's break this down. **Initial Module Analysis** My initial thought here is to examine how the `rtl8365mb` driver, specifically the SMI portion, is being initialized. I can see that `rtl8365mb_smi_driver` is registered as a standard `platform_driver`, and it's being matched using Device Tree, specifically through the `rtl8365mb_of_match` table. That's good – Device Tree is the preferred method these days. And then, the MDIO part is handled by `rtl8365mb_mdio_driver` which is also matched with the same Device Tree setup. Now, I want to understand the module initialization sequence. Since I know this is a Realtek driver, I'm pretty sure I'll find the init and exit functions within `drivers/net/dsa/realtek/rtl8365mb_main.c`. Specifically, I'll focus on lines 3900 through 3960 to see the module entry point, platform driver registration, and any related device probing or initialization calls. That's typically where I find the core setup. Knowing the initialization flow helps me determine dependencies and how the driver integrates with the hardware platform and MDIO bus. |
| 10/2 | 2026/10/09 18:09 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/rtl8365mb_main.c",
"FirstLine": 3900,
"LineCount": 65
}Results: {
"Contents": "3900:\t\n3901:\tstatic const char *const rtl8365mb_supplies[] = {\n3902:\t\t\"avddh\", \"avddl\", \"dvddio\", \"dvddio1\", \"dvddl\", \"pllvddl\",\n3903:\t};\n3904:\t\n3905:\tconst struct realtek_variant rtl8365mb_variant = {\n3906:\t\t.ds_ops = \u0026rtl8365mb_switch_ops,\n3907:\t\t.ops = \u0026rtl8365mb_ops,\n3908:\t\t.phylink_mac_ops = \u0026rtl8365mb_phylink_mac_ops,\n3909:\t\t.clk_delay = 10,\n3910:\t\t.cmd_read = 0xb9,\n3911:\t\t.cmd_write = 0xb8,\n3912:\t\t.chip_data_sz = sizeof(struct rtl8365mb),\n3913:\t\t.supplies = rtl8365mb_supplies,\n3914:\t\t.num_supplies = ARRAY_SIZE(rtl8365mb_supplies),\n3915:\t};\n3916:\t\n3917:\tstatic const struct of_device_id rtl8365mb_of_match[] = {\n3918:\t\t{ .compatible = \"realtek,rtl8365mb\", .data = \u0026rtl8365mb_variant, },\n3919:\t\t{ /* sentinel */ }\n3920:\t};\n3921:\tMODULE_DEVICE_TABLE(of, rtl8365mb_of_match);\n3922:\t\n3923:\tstatic struct platform_driver rtl8365mb_smi_driver = {\n3924:\t\t.driver = {\n3925:\t\t\t.name = \"rtl8365mb-smi\",\n3926:\t\t\t.of_match_table = rtl8365mb_of_match,\n3927:\t\t},\n3928:\t\t.probe = realtek_smi_probe,\n3929:\t\t.remove = realtek_smi_remove,\n3930:\t\t.shutdown = realtek_smi_shutdown,\n3931:\t};\n3932:\t\n3933:\tstatic struct mdio_driver rtl8365mb_mdio_driver = {\n3934:\t\t.mdiodrv.driver = {\n3935:\t\t\t.name = \"rtl8365mb-mdio\",\n3936:\t\t\t.of_match_table = rtl8365mb_of_match,\n3937:\t\t},\n3938:\t\t.probe = realtek_mdio_probe,\n3939:\t\t.remove = realtek_mdio_remove,\n3940:\t\t.shutdown = realtek_mdio_shutdown,\n3941:\t};\n3942:\t\n3943:\tstatic int rtl8365mb_init(void)\n3944:\t{\n3945:\t\tint ret;\n3946:\t\n3947:\t\tret = realtek_mdio_driver_register(\u0026rtl8365mb_mdio_driver);\n3948:\t\tif (ret)\n3949:\t\t\treturn ret;\n3950:\t\n3951:\t\tret = realtek_smi_driver_register(\u0026rtl8365mb_smi_driver);\n3952:\t\tif (ret) {\n3953:\t\t\trealtek_mdio_driver_unregister(\u0026rtl8365mb_mdio_driver);\n3954:\t\t\treturn ret;\n3955:\t\t}\n3956:\t\n3957:\t\treturn 0;\n3958:\t}\n3959:\tmodule_init(rtl8365mb_init);\n3960:\t\n3961:\tstatic void __exit rtl8365mb_exit(void)\n3962:\t{\n3963:\t\trealtek_smi_driver_unregister(\u0026rtl8365mb_smi_driver);\n3964:\t\trealtek_mdio_driver_unregister(\u0026rtl8365mb_mdio_driver);\n"
} |
| 11/2 | 2026/10/09 18:09 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36066 output: 45 thoughts: 45 Thoughts: Okay, let's dive into this Realtek situation. **Probing the Realtek Landscape** Alright, my immediate thought is to get a handle on how these Realtek SMI and MDIO interfaces are being discovered and initialized. My first instinct is to zero in on the probe functions, specifically `realtek_smi_probe` and `realtek_mdio_probe`. I need to understand the fundamental logic behind their detection and setup. What are the key checks? What hardware IDs are they looking for? How are the resources mapped and initialized? Understanding the probing mechanisms will give me the clearest picture of how these interfaces come to life within the system and will be key to understanding how to approach any problems. |
| 12/2 | 2026/10/09 18:09 | tool | grepper |
0mArgs:{
"Expression": "realtek_smi_probe|realtek_mdio_probe",
"PathPrefixes": [
"drivers/net/dsa/realtek"
]
}Results: {
"Output": "drivers/net/dsa/realtek/realtek-mdio.c=104=static const struct realtek_interface_info realtek_mdio_info = {\n--\ndrivers/net/dsa/realtek/realtek-mdio.c-109-/**\ndrivers/net/dsa/realtek/realtek-mdio.c:110: * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch\ndrivers/net/dsa/realtek/realtek-mdio.c-111- * @mdiodev: mdio_device to probe on.\n--\ndrivers/net/dsa/realtek/realtek-mdio.c-120- */\ndrivers/net/dsa/realtek/realtek-mdio.c:121:int realtek_mdio_probe(struct mdio_device *mdiodev)\ndrivers/net/dsa/realtek/realtek-mdio.c-122-{\n--\ndrivers/net/dsa/realtek/realtek-mdio.c-142-}\ndrivers/net/dsa/realtek/realtek-mdio.c:143:EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, \"REALTEK_DSA\");\ndrivers/net/dsa/realtek/realtek-mdio.c-144-\n--\ndrivers/net/dsa/realtek/realtek-mdio.h=13=static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv)\n--\ndrivers/net/dsa/realtek/realtek-mdio.h-17-\ndrivers/net/dsa/realtek/realtek-mdio.h:18:int realtek_mdio_probe(struct mdio_device *mdiodev);\ndrivers/net/dsa/realtek/realtek-mdio.h-19-void realtek_mdio_remove(struct mdio_device *mdiodev);\n--\ndrivers/net/dsa/realtek/realtek-mdio.h=29=static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv)\n--\ndrivers/net/dsa/realtek/realtek-mdio.h-32-\ndrivers/net/dsa/realtek/realtek-mdio.h:33:static inline int realtek_mdio_probe(struct mdio_device *mdiodev)\ndrivers/net/dsa/realtek/realtek-mdio.h-34-{\n--\ndrivers/net/dsa/realtek/realtek-smi.c=314=static const struct realtek_interface_info realtek_smi_info = {\n--\ndrivers/net/dsa/realtek/realtek-smi.c-319-/**\ndrivers/net/dsa/realtek/realtek-smi.c:320: * realtek_smi_probe() - Probe a platform device for an SMI-connected switch\ndrivers/net/dsa/realtek/realtek-smi.c-321- * @pdev: platform_device to probe on.\n--\ndrivers/net/dsa/realtek/realtek-smi.c-330- */\ndrivers/net/dsa/realtek/realtek-smi.c:331:int realtek_smi_probe(struct platform_device *pdev)\ndrivers/net/dsa/realtek/realtek-smi.c-332-{\n--\ndrivers/net/dsa/realtek/realtek-smi.c-363-}\ndrivers/net/dsa/realtek/realtek-smi.c:364:EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, \"REALTEK_DSA\");\ndrivers/net/dsa/realtek/realtek-smi.c-365-\n--\ndrivers/net/dsa/realtek/realtek-smi.h=13=static inline void realtek_smi_driver_unregister(struct platform_driver *drv)\n--\ndrivers/net/dsa/realtek/realtek-smi.h-17-\ndrivers/net/dsa/realtek/realtek-smi.h:18:int realtek_smi_probe(struct platform_device *pdev);\ndrivers/net/dsa/realtek/realtek-smi.h-19-void realtek_smi_remove(struct platform_device *pdev);\n--\ndrivers/net/dsa/realtek/realtek-smi.h=29=static inline void realtek_smi_driver_unregister(struct platform_driver *drv)\n--\ndrivers/net/dsa/realtek/realtek-smi.h-32-\ndrivers/net/dsa/realtek/realtek-smi.h:33:static inline int realtek_smi_probe(struct platform_device *pdev)\ndrivers/net/dsa/realtek/realtek-smi.h-34-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c=3923=static struct platform_driver rtl8365mb_smi_driver = {\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3927-\t},\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3928:\t.probe = realtek_smi_probe,\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3929-\t.remove = realtek_smi_remove,\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c=3933=static struct mdio_driver rtl8365mb_mdio_driver = {\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3937-\t},\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3938:\t.probe = realtek_mdio_probe,\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3939-\t.remove = realtek_mdio_remove,\n--\ndrivers/net/dsa/realtek/rtl8366rb.c=1846=static struct platform_driver rtl8366rb_smi_driver = {\n--\ndrivers/net/dsa/realtek/rtl8366rb.c-1850-\t},\ndrivers/net/dsa/realtek/rtl8366rb.c:1851:\t.probe = realtek_smi_probe,\ndrivers/net/dsa/realtek/rtl8366rb.c-1852-\t.remove = realtek_smi_remove,\n--\ndrivers/net/dsa/realtek/rtl8366rb.c=1856=static struct mdio_driver rtl8366rb_mdio_driver = {\n--\ndrivers/net/dsa/realtek/rtl8366rb.c-1860-\t},\ndrivers/net/dsa/realtek/rtl8366rb.c:1861:\t.probe = realtek_mdio_probe,\ndrivers/net/dsa/realtek/rtl8366rb.c-1862-\t.remove = realtek_mdio_remove,\n"
} |
| 13/2 | 2026/10/09 18:09 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38157 output: 45 thoughts: 44 Thoughts: Here's my take on the situation: **Probing the Realtek Drivers** Okay, time to dive into this. I need to refresh my memory on the probe functions for these Realtek SMI and MDIO interfaces. I'll need to scrutinize the `realtek_smi_probe` and `realtek_mdio_probe` implementations. I'll head straight to the source files, `realtek-smi.c` and `realtek-mdio.c`, respectively, to get a clear picture of how they're handling device detection and initialization. It's the only way to get a good understanding of what might be happening at this low level. This is the starting point for understanding their behavior. |
| 14/2 | 2026/10/09 18:10 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/realtek-smi.c",
"FirstLine": 330,
"LineCount": 40
}Results: {
"Contents": " 330:\t */\n 331:\tint realtek_smi_probe(struct platform_device *pdev)\n 332:\t{\n 333:\t\tstruct device *dev = \u0026pdev-\u003edev;\n 334:\t\tstruct realtek_priv *priv;\n 335:\t\tint ret;\n 336:\t\n 337:\t\tpriv = rtl83xx_probe(dev, \u0026realtek_smi_info);\n 338:\t\tif (IS_ERR(priv))\n 339:\t\t\treturn PTR_ERR(priv);\n 340:\t\n 341:\t\t/* Fetch MDIO pins */\n 342:\t\tpriv-\u003emdc = devm_gpiod_get_optional(dev, \"mdc\", GPIOD_OUT_LOW);\n 343:\t\tif (IS_ERR(priv-\u003emdc)) {\n 344:\t\t\trtl83xx_remove(priv);\n 345:\t\t\treturn PTR_ERR(priv-\u003emdc);\n 346:\t\t}\n 347:\t\n 348:\t\tpriv-\u003emdio = devm_gpiod_get_optional(dev, \"mdio\", GPIOD_OUT_LOW);\n 349:\t\tif (IS_ERR(priv-\u003emdio)) {\n 350:\t\t\trtl83xx_remove(priv);\n 351:\t\t\treturn PTR_ERR(priv-\u003emdio);\n 352:\t\t}\n 353:\t\n 354:\t\tpriv-\u003ewrite_reg_noack = realtek_smi_write_reg_noack;\n 355:\t\n 356:\t\tret = rtl83xx_register_switch(priv);\n 357:\t\tif (ret) {\n 358:\t\t\trtl83xx_remove(priv);\n 359:\t\t\treturn ret;\n 360:\t\t}\n 361:\t\n 362:\t\treturn 0;\n 363:\t}\n 364:\tEXPORT_SYMBOL_NS_GPL(realtek_smi_probe, \"REALTEK_DSA\");\n 365:\t\n 366:\t/**\n 367:\t * realtek_smi_remove() - Remove the driver of a SMI-connected switch\n 368:\t * @pdev: platform_device to be removed.\n 369:\t *\n"
} |
| 15/2 | 2026/10/09 18:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39013 output: 45 thoughts: 15 Thoughts: Okay, let's dive into this. Here's my take on examining that `realtek-mdio.c` file: **Analyzing the `realtek-mdio.c` File** Alright, I'm thinking, let's see what's going on within `realtek-mdio.c`. Since I'm working on this driver, I know this file is critical for managing the MDIO interface related to Realtek PHYs. I need to understand its role in configuring and controlling the physical layer transceiver. I'm going to look for the core functions handling MDIO transactions: read, write, and possibly the initialization routines. I want to see how it's interacting with the MDIO bus and if it uses any specific Realtek-specific registers or commands. I will be looking for interrupt handling, any error checking, and clock setup details. I will also make sure the data structures used for PHY configuration are well-defined. I'll mentally map out the overall driver architecture for it, and then make a proper plan of what to do. |
| 16/2 | 2026/10/09 18:10 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/realtek-mdio.c",
"FirstLine": 115,
"LineCount": 40
}Results: {
"Contents": " 115:\t * values specific for MDIO-connected devices. Finally, it calls a common\n 116:\t * function to register the DSA switch.\n 117:\t *\n 118:\t * Context: Can sleep. Takes and releases priv-\u003emap_lock.\n 119:\t * Return: Returns 0 on success, a negative error on failure.\n 120:\t */\n 121:\tint realtek_mdio_probe(struct mdio_device *mdiodev)\n 122:\t{\n 123:\t\tstruct device *dev = \u0026mdiodev-\u003edev;\n 124:\t\tstruct realtek_priv *priv;\n 125:\t\tint ret;\n 126:\t\n 127:\t\tpriv = rtl83xx_probe(dev, \u0026realtek_mdio_info);\n 128:\t\tif (IS_ERR(priv))\n 129:\t\t\treturn PTR_ERR(priv);\n 130:\t\n 131:\t\tpriv-\u003ebus = mdiodev-\u003ebus;\n 132:\t\tpriv-\u003emdio_addr = mdiodev-\u003eaddr;\n 133:\t\tpriv-\u003ewrite_reg_noack = realtek_mdio_write;\n 134:\t\n 135:\t\tret = rtl83xx_register_switch(priv);\n 136:\t\tif (ret) {\n 137:\t\t\trtl83xx_remove(priv);\n 138:\t\t\treturn ret;\n 139:\t\t}\n 140:\t\n 141:\t\treturn 0;\n 142:\t}\n 143:\tEXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, \"REALTEK_DSA\");\n 144:\t\n 145:\t/**\n 146:\t * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch\n 147:\t * @mdiodev: mdio_device to be removed.\n 148:\t *\n 149:\t * This function should be used as the .remove in an mdio_driver. First\n 150:\t * it unregisters the DSA switch and then it calls the common remove function.\n 151:\t *\n 152:\t * Context: Can sleep.\n 153:\t * Return: Nothing.\n 154:\t */\n"
} |
| 17/2 | 2026/10/09 18:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39910 output: 38 thoughts: 42 Thoughts: Here's my summary, as if I were thinking these thoughts: **Examining the Realtek Switch Driver Initialization** Okay, time to dig into the Realtek switch driver. I need to refresh my memory on how this initialization process works. Let's start by scrutinizing the `rtl83xx_probe` function. I want to see how the driver actually gets attached to the device – what resources it claims, how it sets up the initial hardware state, and if there are any obvious gotchas in the probe sequence itself. Then, I'll move on to `rtl83xx_register_switch`. This is where the DSA (Distributed Switch Architecture) aspects come into play. I'll need to understand how the switch is integrated into the DSA framework, how it's represented as a network device, and how the various control and data paths are established. I'll pay close attention to the order of operations in both functions, to make sure there aren't any potential race conditions or resource conflicts. Good, methodical approach is key here. Let's see what we've got in `drivers/net/dsa/realtek/rtl83xx.c`. |
| 18/2 | 2026/10/09 18:10 | tool | grepper |
0mArgs:{
"Expression": "rtl83xx_register_switch",
"PathPrefixes": [
"drivers/net/dsa/realtek"
]
}Results: {
"Output": "drivers/net/dsa/realtek/realtek-mdio.c=121=int realtek_mdio_probe(struct mdio_device *mdiodev)\n--\ndrivers/net/dsa/realtek/realtek-mdio.c-134-\ndrivers/net/dsa/realtek/realtek-mdio.c:135:\tret = rtl83xx_register_switch(priv);\ndrivers/net/dsa/realtek/realtek-mdio.c-136-\tif (ret) {\n--\ndrivers/net/dsa/realtek/realtek-smi.c=331=int realtek_smi_probe(struct platform_device *pdev)\n--\ndrivers/net/dsa/realtek/realtek-smi.c-355-\ndrivers/net/dsa/realtek/realtek-smi.c:356:\tret = rtl83xx_register_switch(priv);\ndrivers/net/dsa/realtek/realtek-smi.c-357-\tif (ret) {\n--\ndrivers/net/dsa/realtek/rtl83xx.c=256=EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, \"REALTEK_DSA\");\n--\ndrivers/net/dsa/realtek/rtl83xx.c-258-/**\ndrivers/net/dsa/realtek/rtl83xx.c:259: * rtl83xx_register_switch() - detects and register a switch\ndrivers/net/dsa/realtek/rtl83xx.c-260- * @priv: realtek_priv pointer\n--\ndrivers/net/dsa/realtek/rtl83xx.c-267- */\ndrivers/net/dsa/realtek/rtl83xx.c:268:int rtl83xx_register_switch(struct realtek_priv *priv)\ndrivers/net/dsa/realtek/rtl83xx.c-269-{\n--\ndrivers/net/dsa/realtek/rtl83xx.c-292-}\ndrivers/net/dsa/realtek/rtl83xx.c:293:EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, \"REALTEK_DSA\");\ndrivers/net/dsa/realtek/rtl83xx.c-294-\n--\ndrivers/net/dsa/realtek/rtl83xx.h=15=rtl83xx_probe(struct device *dev,\ndrivers/net/dsa/realtek/rtl83xx.h-16-\t const struct realtek_interface_info *interface_info);\ndrivers/net/dsa/realtek/rtl83xx.h:17:int rtl83xx_register_switch(struct realtek_priv *priv);\ndrivers/net/dsa/realtek/rtl83xx.h-18-void rtl83xx_unregister_switch(struct realtek_priv *priv);\n"
} |
| 19/2 | 2026/10/09 18:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40986 output: 44 thoughts: 43 Thoughts: Here's my summary, as if I were thinking it: **Digging into the Realtek RTL83xx Initialization** Okay, I need to understand how the private data structure (`priv`) is initialized and the probe process works for this Realtek switch driver. Time to jump into `drivers/net/dsa/realtek/rtl83xx.c` and focus on lines 200 through 295. I'll be looking closely at how the `priv` variable, which surely contains all the board specific and driver instance data, is handled. I need to see how the system identifies the switch, loads its registers, and gets it ready for use. This should give me a solid understanding of the foundation before I look into the more complicated aspects of the driver. |
| 20/2 | 2026/10/09 18:10 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/rtl83xx.c",
"FirstLine": 210,
"LineCount": 85
}Results: {
"Contents": " 210:\t\tpriv-\u003evariant = var;\n 211:\t\tpriv-\u003eops = var-\u003eops;\n 212:\t\tpriv-\u003echip_data = (void *)priv + sizeof(*priv);\n 213:\t\n 214:\t\tspin_lock_init(\u0026priv-\u003elock);\n 215:\t\n 216:\t\tpriv-\u003eleds_disabled = of_property_read_bool(dev-\u003eof_node,\n 217:\t\t\t\t\t\t\t \"realtek,disable-leds\");\n 218:\t\n 219:\t\t/* Enable the supplies before the reset line is requested and driven,\n 220:\t\t * so the chip is powered before its pins are driven.\n 221:\t\t */\n 222:\t\tif (var-\u003enum_supplies) {\n 223:\t\t\tret = devm_regulator_bulk_get_enable(dev, var-\u003enum_supplies,\n 224:\t\t\t\t\t\t\t var-\u003esupplies);\n 225:\t\t\tif (ret)\n 226:\t\t\t\treturn dev_err_ptr_probe(dev, ret, \"failed to enable supplies\\n\");\n 227:\t\t}\n 228:\t\n 229:\t\tpriv-\u003ereset_ctl = devm_reset_control_get_optional(dev, NULL);\n 230:\t\tif (IS_ERR(priv-\u003ereset_ctl))\n 231:\t\t\treturn dev_err_cast_probe(dev, priv-\u003ereset_ctl,\n 232:\t\t\t\t\t\t \"failed to get reset control\\n\");\n 233:\t\n 234:\t\tpriv-\u003ereset = devm_gpiod_get_optional(dev, \"reset\", GPIOD_OUT_LOW);\n 235:\t\tif (IS_ERR(priv-\u003ereset))\n 236:\t\t\treturn dev_err_cast_probe(dev, priv-\u003ereset, \"failed to get RESET GPIO\\n\");\n 237:\t\n 238:\t\tdev_set_drvdata(dev, priv);\n 239:\t\n 240:\t\tif (priv-\u003ereset_ctl || priv-\u003ereset) {\n 241:\t\t\trtl83xx_reset_assert(priv);\n 242:\t\t\tdev_dbg(dev, \"asserted RESET\\n\");\n 243:\t\t\tmsleep(REALTEK_HW_STOP_DELAY);\n 244:\t\t\trtl83xx_reset_deassert(priv);\n 245:\t\t\tmsleep(REALTEK_HW_START_DELAY);\n 246:\t\t\tdev_dbg(dev, \"deasserted RESET\\n\");\n 247:\t\t} else if (var-\u003enum_supplies) {\n 248:\t\t\t/* Powered but no reset line: still wait for the chip to boot\n 249:\t\t\t * before the first register access.\n 250:\t\t\t */\n 251:\t\t\tmsleep(REALTEK_HW_START_DELAY);\n 252:\t\t}\n 253:\t\n 254:\t\treturn priv;\n 255:\t}\n 256:\tEXPORT_SYMBOL_NS_GPL(rtl83xx_probe, \"REALTEK_DSA\");\n 257:\t\n 258:\t/**\n 259:\t * rtl83xx_register_switch() - detects and register a switch\n 260:\t * @priv: realtek_priv pointer\n 261:\t *\n 262:\t * This function first checks the switch chip ID and register a DSA\n 263:\t * switch.\n 264:\t *\n 265:\t * Context: Can sleep. Takes and releases priv-\u003emap_lock.\n 266:\t * Return: 0 on success, negative value for failure.\n 267:\t */\n 268:\tint rtl83xx_register_switch(struct realtek_priv *priv)\n 269:\t{\n 270:\t\tstruct dsa_switch *ds = \u0026priv-\u003eds;\n 271:\t\tint ret;\n 272:\t\n 273:\t\tret = priv-\u003eops-\u003edetect(priv);\n 274:\t\tif (ret) {\n 275:\t\t\tdev_err_probe(priv-\u003edev, ret, \"unable to detect switch\\n\");\n 276:\t\t\treturn ret;\n 277:\t\t}\n 278:\t\n 279:\t\tds-\u003epriv = priv;\n 280:\t\tds-\u003edev = priv-\u003edev;\n 281:\t\tds-\u003eops = priv-\u003evariant-\u003eds_ops;\n 282:\t\tds-\u003ephylink_mac_ops = priv-\u003evariant-\u003ephylink_mac_ops;\n 283:\t\tds-\u003enum_ports = priv-\u003enum_ports;\n 284:\t\n 285:\t\tret = dsa_register_switch(ds);\n 286:\t\tif (ret) {\n 287:\t\t\tdev_err_probe(priv-\u003edev, ret, \"unable to register switch\\n\");\n 288:\t\t\treturn ret;\n 289:\t\t}\n 290:\t\n 291:\t\treturn 0;\n 292:\t}\n 293:\tEXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, \"REALTEK_DSA\");\n 294:\t\n"
} |
| 21/2 | 2026/10/09 18:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42579 output: 49 thoughts: 70 Thoughts: Here's my thought process, breaking down this code snippet: **Debugging the rtl8365mb Driver Detection** Okay, so I'm digging into this `rtl8365mb` driver, and I need to understand how the hardware is being detected. The first thing I'm seeing is a function call: `priv->ops->detect(priv)`. This is a classic pattern – the `priv` structure likely holds driver-specific data, and `ops` is a function pointer table, a virtual method table if you will. The `detect` function pointer within that table is the key to actual hardware probing. So, where is this `detect` function actually defined? The code mentions `rtl8365mb_main.c`, and that's where I need to look. It looks like `rtl8365mb_detect(priv)` gets called during the switch registration process. Good, that makes sense – we expect the hardware initialization and detection to happen at that point. Now, let's get into the specifics of `rtl8365mb_detect` itself. I'll need to examine that function directly to understand the exact hardware detection sequence: are they checking registers, performing specific I/O operations, or maybe even interacting with the PHY layer? I'm expecting to see something like that in order to identify the hardware. This is where the core logic will lie, and this is where I need to be focused. |
| 22/2 | 2026/10/09 18:10 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/rtl8365mb_main.c",
"FirstLine": 3300,
"LineCount": 85
}Results: {
"Contents": "3300:\t\t\tdev_err(priv-\u003edev, \"unsupported irq trigger type %u\\n\",\n3301:\t\t\t\tirq_trig);\n3302:\t\t\tret = -EINVAL;\n3303:\t\t\tgoto out_remove_irqdomain;\n3304:\t\t}\n3305:\t\n3306:\t\tret = regmap_update_bits(priv-\u003emap, RTL8365MB_INTR_POLARITY_REG,\n3307:\t\t\t\t\t RTL8365MB_INTR_POLARITY_MASK,\n3308:\t\t\t\t\t FIELD_PREP(RTL8365MB_INTR_POLARITY_MASK, val));\n3309:\t\tif (ret)\n3310:\t\t\tgoto out_remove_irqdomain;\n3311:\t\n3312:\t\t/* Disable the interrupt in case the chip has it enabled on reset */\n3313:\t\tret = rtl8365mb_irq_disable(priv);\n3314:\t\tif (ret)\n3315:\t\t\tgoto out_remove_irqdomain;\n3316:\t\n3317:\t\t/* Clear the interrupt status register */\n3318:\t\tret = regmap_write(priv-\u003emap, RTL8365MB_INTR_STATUS_REG,\n3319:\t\t\t\t RTL8365MB_INTR_ALL_MASK);\n3320:\t\tif (ret)\n3321:\t\t\tgoto out_remove_irqdomain;\n3322:\t\n3323:\t\tret = request_threaded_irq(irq, NULL, rtl8365mb_irq, IRQF_ONESHOT,\n3324:\t\t\t\t\t \"rtl8365mb\", priv);\n3325:\t\tif (ret) {\n3326:\t\t\tdev_err(priv-\u003edev, \"failed to request irq: %pe\\n\",\n3327:\t\t\t\tERR_PTR(ret));\n3328:\t\t\tgoto out_remove_irqdomain;\n3329:\t\t}\n3330:\t\n3331:\t\tret = rtl8365mb_irq_enable(priv);\n3332:\t\tif (ret)\n3333:\t\t\tgoto out_free_irq;\n3334:\t\n3335:\t\tof_node_put(intc);\n3336:\t\n3337:\t\treturn 0;\n3338:\t\n3339:\tout_free_irq:\n3340:\t\tfree_irq(mb-\u003eirq, priv);\n3341:\t\n3342:\tout_remove_irqdomain:\n3343:\t\tdsa_switch_for_each_port(dp, ds) {\n3344:\t\t\tvirq = irq_find_mapping(priv-\u003eirqdomain, dp-\u003eindex);\n3345:\t\n3346:\t\t\tif (virq)\n3347:\t\t\t\tirq_dispose_mapping(virq);\n3348:\t\t}\n3349:\t\n3350:\t\tirq_domain_remove(priv-\u003eirqdomain);\n3351:\t\tpriv-\u003eirqdomain = NULL;\n3352:\t\n3353:\tout_put_node:\n3354:\t\tmb-\u003eirq = 0;\n3355:\t\tof_node_put(intc);\n3356:\t\n3357:\t\treturn ret;\n3358:\t}\n3359:\t\n3360:\tstatic void rtl8365mb_irq_teardown(struct realtek_priv *priv)\n3361:\t{\n3362:\t\tstruct rtl8365mb *mb = priv-\u003echip_data;\n3363:\t\tstruct dsa_switch *ds = \u0026priv-\u003eds;\n3364:\t\tstruct dsa_port *dp;\n3365:\t\tint virq;\n3366:\t\n3367:\t\tif (mb-\u003eirq) {\n3368:\t\t\tfree_irq(mb-\u003eirq, priv);\n3369:\t\t\tmb-\u003eirq = 0;\n3370:\t\t}\n3371:\t\n3372:\t\tif (priv-\u003eirqdomain) {\n3373:\t\t\t/* Unused ports with a linked PHY still have an active IRQ\n3374:\t\t\t * mapping that must be disposed of during teardown. Loop\n3375:\t\t\t * through all ports.\n3376:\t\t\t */\n3377:\t\t\tdsa_switch_for_each_port(dp, ds) {\n3378:\t\t\t\tvirq = irq_find_mapping(priv-\u003eirqdomain, dp-\u003eindex);\n3379:\t\n3380:\t\t\t\tif (virq)\n3381:\t\t\t\t\tirq_dispose_mapping(virq);\n3382:\t\t\t}\n3383:\t\n3384:\t\t\tirq_domain_remove(priv-\u003eirqdomain);\n"
} |
| 23/2 | 2026/10/09 18:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44296 output: 49 thoughts: 18 Thoughts: Okay, here's my interpretation of that request, formatted as requested: **Diving Deeper: Investigating rtl8365mb_detect** Alright, let's keep going down this rabbit hole. My initial analysis is done, but the real insights often lie in the details. The previous context is understood, so now I need to zero in on what this `rtl8365mb_detect` function actually does. I'm expecting this to be a crucial piece, likely involved in the hardware detection or initialization process for that particular RTL chipset. Understanding its purpose and how it fits into the overall system architecture is the next logical step in forming a complete picture. This is where the core of the problem may lay. |
| 24/2 | 2026/10/09 18:10 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/rtl8365mb_main.c",
"FirstLine": 3385,
"LineCount": 70
}Results: {
"Contents": "3385:\t\t\tpriv-\u003eirqdomain = NULL;\n3386:\t\t}\n3387:\t}\n3388:\t\n3389:\tstatic int rtl8365mb_cpu_config(struct realtek_priv *priv)\n3390:\t{\n3391:\t\tstruct rtl8365mb *mb = priv-\u003echip_data;\n3392:\t\tstruct rtl8365mb_cpu *cpu = \u0026mb-\u003ecpu;\n3393:\t\tu32 val;\n3394:\t\tint ret;\n3395:\t\n3396:\t\tret = regmap_update_bits(priv-\u003emap, RTL8365MB_CPU_PORT_MASK_REG,\n3397:\t\t\t\t\t RTL8365MB_CPU_PORT_MASK_MASK,\n3398:\t\t\t\t\t FIELD_PREP(RTL8365MB_CPU_PORT_MASK_MASK,\n3399:\t\t\t\t\t\t cpu-\u003emask));\n3400:\t\tif (ret)\n3401:\t\t\treturn ret;\n3402:\t\n3403:\t\tval = FIELD_PREP(RTL8365MB_CPU_CTRL_EN_MASK, cpu-\u003eenable ? 1 : 0) |\n3404:\t\t FIELD_PREP(RTL8365MB_CPU_CTRL_INSERTMODE_MASK, cpu-\u003einsert) |\n3405:\t\t FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_POSITION_MASK, cpu-\u003eposition) |\n3406:\t\t FIELD_PREP(RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK, cpu-\u003erx_length) |\n3407:\t\t FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK, cpu-\u003eformat) |\n3408:\t\t FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu-\u003etrap_port \u0026 0x7) |\n3409:\t\t FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK,\n3410:\t\t\t\t cpu-\u003etrap_port \u003e\u003e 3 \u0026 0x1);\n3411:\t\tret = regmap_write(priv-\u003emap, RTL8365MB_CPU_CTRL_REG, val);\n3412:\t\tif (ret)\n3413:\t\t\treturn ret;\n3414:\t\n3415:\t\treturn 0;\n3416:\t}\n3417:\t\n3418:\tstatic int rtl8365mb_change_tag_protocol(struct dsa_switch *ds,\n3419:\t\t\t\t\t\t enum dsa_tag_protocol proto)\n3420:\t{\n3421:\t\tstruct realtek_priv *priv = ds-\u003epriv;\n3422:\t\tstruct rtl8365mb_cpu *cpu;\n3423:\t\tstruct rtl8365mb *mb;\n3424:\t\n3425:\t\tmb = priv-\u003echip_data;\n3426:\t\tcpu = \u0026mb-\u003ecpu;\n3427:\t\n3428:\t\tswitch (proto) {\n3429:\t\tcase DSA_TAG_PROTO_RTL8_4:\n3430:\t\t\tcpu-\u003eformat = RTL8365MB_CPU_FORMAT_8BYTES;\n3431:\t\t\tcpu-\u003eposition = RTL8365MB_CPU_POS_AFTER_SA;\n3432:\t\t\tbreak;\n3433:\t\tcase DSA_TAG_PROTO_RTL8_4T:\n3434:\t\t\tcpu-\u003eformat = RTL8365MB_CPU_FORMAT_8BYTES;\n3435:\t\t\tcpu-\u003eposition = RTL8365MB_CPU_POS_BEFORE_CRC;\n3436:\t\t\tbreak;\n3437:\t\t/* The switch also supports a 4-byte format, similar to rtl4a but with\n3438:\t\t * the same 0x04 8-bit version and probably 8-bit port source/dest.\n3439:\t\t * There is no public doc about it. Not supported yet and it will probably\n3440:\t\t * never be.\n3441:\t\t */\n3442:\t\tdefault:\n3443:\t\t\treturn -EPROTONOSUPPORT;\n3444:\t\t}\n3445:\t\n3446:\t\treturn rtl8365mb_cpu_config(priv);\n3447:\t}\n3448:\t\n3449:\tstatic int rtl8365mb_switch_init(struct realtek_priv *priv)\n3450:\t{\n3451:\t\tstruct rtl8365mb *mb = priv-\u003echip_data;\n3452:\t\tconst struct rtl8365mb_chip_info *ci;\n3453:\t\tint ret;\n3454:\t\tint i;\n"
} |
| 25/2 | 2026/10/09 18:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 45776 output: 49 thoughts: 42 Thoughts: Here's my take on the RTL8365MB detection routine, based on the specified code section: **Diving into the RTL8365MB Detection** Okay, so we're focusing on lines 3750-3810 within `rtl8365mb_main.c`, specifically the `rtl8365mb_detect` function. That gives me a very specific target to examine – the core mechanism for identifying the hardware. I need to understand how the driver determines if an RTL8365MB switch is actually present and, ideally, how it validates that detection. I'm immediately looking for things like: * **Register accesses:** What registers are being read or written to? Are they standard I2C, SPI, or a custom interface? That will immediately tell me the hardware interface strategy. * **Magic numbers/signatures:** Does it rely on specific values read back from registers to confirm the chip ID? Those are the giveaways. * **Error handling:** Is there any error checking? What kind of checks are performed, and what happens if the device doesn't respond as expected? That helps me know how robust the detection method is. * **Sequence of operations:** What's the detection flow? Are there pre-initialization steps? Does it go through a specific detection sequence, such as reading an ID register, performing a reset, then re-checking? * **Assumptions:** Are any presumptions being made about the hardware? For instance, that the switch is on a specific I2C or SPI address? Any of those will determine the likelihood that the detection method will work. I expect to see the basic steps here: initialization of the hardware interface, attempting to communicate with the device, and then verifying the device's presence based on responses. The presence verification could range from looking at a single register all the way to performing a series of tests to validate the chip's identity. I am now looking to map out how that is happening. Let's dig in. |
| 26/2 | 2026/10/09 18:10 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/rtl8365mb_main.c",
"FirstLine": 3735,
"LineCount": 60
}Results: {
"Contents": "3735:\t\t\treturn ret;\n3736:\t\n3737:\t\treturn 0;\n3738:\t}\n3739:\t\n3740:\tstatic int rtl8365mb_detect(struct realtek_priv *priv)\n3741:\t{\n3742:\t\tstruct rtl8365mb *mb = priv-\u003echip_data;\n3743:\t\tu32 chip_id;\n3744:\t\tu32 chip_ver;\n3745:\t\tint ret;\n3746:\t\tint i;\n3747:\t\n3748:\t\tret = rtl8365mb_get_chip_id_and_ver(priv-\u003emap, \u0026chip_id, \u0026chip_ver);\n3749:\t\tif (ret) {\n3750:\t\t\tdev_err(priv-\u003edev, \"failed to read chip id and version: %pe\\n\",\n3751:\t\t\t\tERR_PTR(ret));\n3752:\t\t\treturn ret;\n3753:\t\t}\n3754:\t\n3755:\t\tfor (i = 0; i \u003c ARRAY_SIZE(rtl8365mb_chip_infos); i++) {\n3756:\t\t\tconst struct rtl8365mb_chip_info *ci = \u0026rtl8365mb_chip_infos[i];\n3757:\t\n3758:\t\t\tif (ci-\u003echip_id == chip_id \u0026\u0026 ci-\u003echip_ver == chip_ver) {\n3759:\t\t\t\tmb-\u003echip_info = ci;\n3760:\t\t\t\tbreak;\n3761:\t\t\t}\n3762:\t\t}\n3763:\t\n3764:\t\tif (!mb-\u003echip_info) {\n3765:\t\t\tdev_err(priv-\u003edev,\n3766:\t\t\t\t\"unrecognized switch (id=0x%04x, ver=0x%04x)\", chip_id,\n3767:\t\t\t\tchip_ver);\n3768:\t\t\treturn -ENODEV;\n3769:\t\t}\n3770:\t\n3771:\t\tdev_info(priv-\u003edev, \"found an %s switch\\n\", mb-\u003echip_info-\u003ename);\n3772:\t\n3773:\t\tif (rtl8365mb_get_family(priv) == RTL8365MB_FAMILY_D)\n3774:\t\t\tpriv-\u003enum_ports = RTL8365MB_D_MAX_NUM_PORTS;\n3775:\t\telse\n3776:\t\t\tpriv-\u003enum_ports = RTL8365MB_MAX_NUM_PORTS;\n3777:\t\n3778:\t\tret = devm_mutex_init(priv-\u003edev, \u0026mb-\u003esds_lock);\n3779:\t\tif (ret)\n3780:\t\t\treturn ret;\n3781:\t\n3782:\t\tmb-\u003epriv = priv;\n3783:\t\tmb-\u003ecpu.trap_port = RTL8365MB_MAX_NUM_PORTS;\n3784:\t\tmb-\u003ecpu.insert = RTL8365MB_CPU_INSERT_TO_ALL;\n3785:\t\tmb-\u003ecpu.position = RTL8365MB_CPU_POS_AFTER_SA;\n3786:\t\tmb-\u003ecpu.rx_length = RTL8365MB_CPU_RXLEN_64BYTES;\n3787:\t\tmb-\u003ecpu.format = RTL8365MB_CPU_FORMAT_8BYTES;\n3788:\t\n3789:\t\treturn 0;\n3790:\t}\n3791:\t\n3792:\tstatic int rtl8365mb_phylink_mac_enable_tx_lpi(struct phylink_config *config,\n3793:\t\t\t\t\t\t u32 timer, bool tx_clock_stop)\n3794:\t{\n"
} |
| 27/2 | 2026/10/09 18:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47296 output: 46 thoughts: 848 Thoughts: **Analyzing the Reachability of the rtl8365mb Driver**
Okay, so the `rtl8365mb_detect` function is called during the probe sequence. My immediate question is, how does this driver ever actually get instantiated? Looking at the `rtl8365mb_init` function, it seems to register two drivers: `rtl8365mb_mdio_driver` and `rtl8365mb_smi_driver`. Both have an `of_match_table` entry targeting "realtek,rtl8365mb."
The core question here is whether syzkaller or a virtual environment like GCE or QEMU can probe either a `platform_driver` or an `mdio_driver` for a physical Ethernet switch ASIC. That's the real blocker. I need to figure out if this driver is even *reachable* in a standard virtualized setup.
Let me go back and re-read the prompt, specifically Section 2, because the "REACHABILITY IS THE PRIMARY GATE." It's clear that I shouldn't bother fuzzing code that can't execute in a standard virtual environment. The prompt is very specific: I should return `WorthFuzzing=false` if the code falls into one of these categories:
* Structurally unreachable hardware: This includes vendor-specific PCIe switches and SmartNICs.
* Unreachable execution paths: such as driver teardown or architectures other than the target.
The question then becomes: Is this Realtek DSA switch hardware-specific? Can the `rtl8365mb` driver be probed without the actual hardware? I need to determine if there are any mock or virtual buses involved or if this relies on a physical MDIO/SMI bus and a physical chip that actually responds on the bus.
Taking a closer look at the `rtl8365mb_detect` function:
```c
ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver);
if (ret) {
dev_err(priv->dev, "failed to read chip id and version: %pe\n",
ERR_PTR(ret));
return ret;
}
for (i = 0; i < ARRAY_SIZE(rtl8365mb_chip_infos); i++) {
const struct rtl8365mb_chip_info *ci = &rtl8365mb_chip_infos[i];
if (ci->chip_id == chip_id && ci->chip_ver == chip_ver) {
mb->chip_info = ci;
break;
}
}
if (!mb->chip_info) {
dev_err(priv->dev,
"unrecognized switch (id=0x%04x, ver=0x%04x)", chip_id,
chip_ver);
return -ENODEV;
}
```
Okay, the crucial part here is the call to `rtl8365mb_get_chip_id_and_ver`. This is where it's going to probe for the actual chip ID and version. I *must* investigate the implementation of `rtl8365mb_get_chip_id_and_ver` to understand whether it depends on direct hardware access. This will determine if the driver is realistically reachable in a virtualized environment. Without that, this is a dead end.
|
| 28/2 | 2026/10/09 18:11 | tool | grepper |
0mArgs:{
"Expression": "rtl8365mb_get_chip_id_and_ver",
"PathPrefixes": [
"drivers/net/dsa/realtek"
]
}Results: {
"Output": "drivers/net/dsa/realtek/rtl8365mb_main.c=3701=static void rtl8365mb_teardown(struct dsa_switch *ds)\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3712-\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3713:static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver)\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3714-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c=3740=static int rtl8365mb_detect(struct realtek_priv *priv)\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3747-\ndrivers/net/dsa/realtek/rtl8365mb_main.c:3748:\tret = rtl8365mb_get_chip_id_and_ver(priv-\u003emap, \u0026chip_id, \u0026chip_ver);\ndrivers/net/dsa/realtek/rtl8365mb_main.c-3749-\tif (ret) {\n"
} |
| 29/2 | 2026/10/09 18:11 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 49312 output: 49 thoughts: 22 Thoughts: Okay, here's my interpretation of what's going through my mind as I approach this function, written from a first-person perspective, assuming I'm a seasoned embedded systems developer: **Analyzing `rtl8365mb_get_chip_id_and_ver`** Alright, let's dive into `rtl8365mb_get_chip_id_and_ver`. The name itself is fairly descriptive, suggesting it's responsible for retrieving the chip identification and version information from this RTL8365MB device. That's a fundamental operation, likely needed during initialization, device discovery, or maybe for debugging and feature-checking. I'm immediately thinking about a few things: * **How is this function going to interact with the hardware?** Is it using I2C, SPI, or some other interface? I'll need to look at the function's internals to see the bus transactions. Are there any hardware register access macros or functions involved? * **What are the expected data structures?** I expect a `chip_id` and a `version` (or maybe separate registers for each), likely in a struct or defined as individual return values. Are the ID and version fields fixed-width, or variable? I need to know how these values are packed together. * **Error handling is paramount.** Reading from hardware can fail for various reasons (bus issues, chip unresponsive). I'll be looking closely at how the function handles potential errors. Is there a return code to indicate success/failure, or is an error flag set? What specific error conditions are considered? * **Are there any assumptions about the hardware state?** Does it assume the chip is already powered on and initialized in some way? Or does it handle initial power-up sequencing? * **Optimization considerations.** While this is probably a relatively simple operation, I'll still be looking to see if there are any opportunities for optimization. Are there any unnecessary register reads or writes? Is the data handling efficient? Is the code portable across different platforms (I might need to adapt it later). My expertise is in embedded software and low-level hardware interaction, so I'm looking at the code through that lens. I'm going to start by understanding the basics of the chip, then I'll trace through the code, looking for critical points and edge cases. I expect to be focusing on the following: 1. **Hardware interface** - What bus transactions does it use? 2. **Register access** - What registers are read, and in what order? 3. **Data parsing** - What do the bytes mean, and how are they translated into a chip ID and version? 4. **Error detection and handling** - What failures are considered? How are they handled? Is the function robust? |
| 30/2 | 2026/10/09 18:11 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/rtl8365mb_main.c",
"FirstLine": 3713,
"LineCount": 25
}Results: {
"Contents": "3713:\tstatic int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver)\n3714:\t{\n3715:\t\tint ret;\n3716:\t\n3717:\t\t/* For some reason we have to write a magic value to an arbitrary\n3718:\t\t * register whenever accessing the chip ID/version registers.\n3719:\t\t */\n3720:\t\tret = regmap_write(map, RTL8365MB_MAGIC_REG, RTL8365MB_MAGIC_VALUE);\n3721:\t\tif (ret)\n3722:\t\t\treturn ret;\n3723:\t\n3724:\t\tret = regmap_read(map, RTL8365MB_CHIP_ID_REG, id);\n3725:\t\tif (ret)\n3726:\t\t\treturn ret;\n3727:\t\n3728:\t\tret = regmap_read(map, RTL8365MB_CHIP_VER_REG, ver);\n3729:\t\tif (ret)\n3730:\t\t\treturn ret;\n3731:\t\n3732:\t\t/* Reset magic register */\n3733:\t\tret = regmap_write(map, RTL8365MB_MAGIC_REG, 0);\n3734:\t\tif (ret)\n3735:\t\t\treturn ret;\n3736:\t\n3737:\t\treturn 0;\n"
} |
| 31/2 | 2026/10/09 18:11 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 50377 output: 30 thoughts: 337 Thoughts: **Analyzing the RTL8365MB Driver in a Virtual Environment** Okay, so `rtl8365mb_get_chip_id_and_ver` is reaching out to the physical device through `regmap` to grab the chip ID and version. That makes sense. Now, where are these `regmap` buses actually defined? Let's dig into `realtek-smi.c` and `realtek-mdio.c` and see. Right, `realtek-smi.c`... It's a two-wire GPIO bit-banged SMI bus, using MDC and MDIO GPIOs, or it could be vendor SMI protocol. Over in `realtek-mdio.c`, it's an MDIO bus device – a `struct mdio_device`, specifically. The problem is, as I suspected, neither SMI nor MDIO switches are emulated in standard QEMU or Google Compute Engine environments. Plus, let's be real, this driver is *specifically* for Realtek Ethernet switch ASICs like the RTL8365MB, RTL8367S, RTL8367SB, and so on. These are physical hardware Ethernet switch ICs that you'd find in embedded routers and consumer network hardware. So, even if someone were to compile this driver into, say, an amd64 kernel (just for argument's sake), there's no emulated RTL8365MB switch for it to talk to in QEMU. It's a dead end as it stands. Hold on, though. Let's not completely give up. Is there any possibility of a virtual bus, a "mock" driver, perhaps something like `netdevsim`, or maybe a DSA mock driver, that could *instantiate* or bind with the `rtl8365mb` driver? I need to check `drivers/net/dsa/` and the `netdevsim` driver. There might be a slim chance of some clever virtual setup, but I'm not holding my breath. |
| 32/2 | 2026/10/09 18:12 | tool | grepper |
0mArgs:{
"Expression": "rtl8365mb",
"PathPrefixes": [
"drivers/net"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 1916 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (11 files in total):\ndrivers/net/dsa/realtek/Makefile:6\ndrivers/net/dsa/realtek/realtek.h:1\ndrivers/net/dsa/realtek/rtl8365mb.h:2\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:41\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:7\ndrivers/net/dsa/realtek/rtl8365mb_main.c:413\ndrivers/net/dsa/realtek/rtl8365mb_table.c:12\ndrivers/net/dsa/realtek/rtl8365mb_table.h:15\ndrivers/net/dsa/realtek/rtl8365mb_vlan.c:101\ndrivers/net/dsa/realtek/rtl8365mb_vlan.h:11\ndrivers/net/phy/realtek/realtek_main.c:6\n\ndrivers/net/dsa/realtek/Makefile=17=endif\ndrivers/net/dsa/realtek/Makefile:18:obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o\ndrivers/net/dsa/realtek/Makefile:19:rtl8365mb-objs := rtl8365mb_main.o \\\ndrivers/net/dsa/realtek/Makefile:20:\t\t rtl8365mb_table.o \\\ndrivers/net/dsa/realtek/Makefile:21:\t\t rtl8365mb_vlan.o \\\ndrivers/net/dsa/realtek/Makefile:22:\t\t rtl8365mb_l2.o \\\ndrivers/net/dsa/realtek/Makefile:23:# end of rtl8365mb-objs\n--\ndrivers/net/dsa/realtek/realtek.h=197=extern const struct realtek_variant rtl8366rb_variant;\ndrivers/net/dsa/realtek/realtek.h:198:extern const struct realtek_variant rtl8365mb_variant;\ndrivers/net/dsa/realtek/realtek.h-199-\n--\ndrivers/net/dsa/realtek/rtl8365mb.h-6-\ndrivers/net/dsa/realtek/rtl8365mb.h:7:enum rtl8365mb_family {\ndrivers/net/dsa/realtek/rtl8365mb.h-8-\tRTL8365MB_FAMILY_C,\n--\ndrivers/net/dsa/realtek/rtl8365mb.h-11-\ndrivers/net/dsa/realtek/rtl8365mb.h:12:enum rtl8365mb_family rtl8365mb_get_family(struct realtek_priv *priv);\ndrivers/net/dsa/realtek/rtl8365mb.h-13-\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-1-// SPDX-License-Identifier: GPL-2.0\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:2:/* Forwarding and multicast database interface for the rtl8365mb switch family\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-3- *\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-8-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:9:#include \"rtl8365mb_l2.h\"\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:10:#include \"rtl8365mb_table.h\"\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-11-#include \u003clinux/regmap.h\u003e\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-91-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:92:struct rtl8365mb_l2_uc_key {\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-93-\tu8 mac_addr[ETH_ALEN];\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-99-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:100:struct rtl8365mb_l2_uc {\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:101:\tstruct rtl8365mb_l2_uc_key key;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-102-\tu8 port;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-113-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:114:struct rtl8365mb_l2_mc_key {\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-115-\tu8 mac_addr[ETH_ALEN];\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-122-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:123:struct rtl8365mb_l2_mc {\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:124:\tstruct rtl8365mb_l2_mc_key key;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-125-\tu16 member;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-133-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:134:static void rtl8365mb_l2_data_to_uc(const u16 *data, struct rtl8365mb_l2_uc *uc)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-135-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-163-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:164:static void rtl8365mb_l2_uc_to_data(const struct rtl8365mb_l2_uc *uc, u16 *data)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-165-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-202-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:203:static void rtl8365mb_l2_data_to_mc(const u16 *data, struct rtl8365mb_l2_mc *mc)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-204-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-230-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:231:static void rtl8365mb_l2_mc_to_data(const struct rtl8365mb_l2_mc *mc, u16 *data)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-232-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-261-/*\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:262: * rtl8365mb_l2_get_next_uc() - get the next Unicast L2 entry\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-263- * @priv: realtek_priv pointer\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-281- **/\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:282:int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-283-\t\t\t struct realtek_fdb_entry *entry)\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-285-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:286:\tstruct rtl8365mb_l2_uc uc;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-287-\tint ret;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-288-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:289:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-290-\t\t\t\t RTL8365MB_TABLE_OP_READ, addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-295-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:296:\trtl8365mb_l2_data_to_uc(data, \u0026uc);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-297-\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-304-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:305:int rtl8365mb_l2_add_uc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-306-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-309-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:310:\tstruct rtl8365mb_l2_uc uc = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-311-\tu16 addr;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-328-\tuc.age = 1;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:329:\trtl8365mb_l2_uc_to_data(\u0026uc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-330-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-331-\t/* add the new entry or update an existing one */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:332:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-333-\t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-344-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:345:int rtl8365mb_l2_del_uc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-346-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-349-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:350:\tstruct rtl8365mb_l2_uc uc = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-351-\tu16 addr;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-360-\tuc.age = 0;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:361:\trtl8365mb_l2_uc_to_data(\u0026uc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-362-\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-367-\t */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:368:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-369-\t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-383-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:384:int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid)\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-385-{\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-448-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:449:int rtl8365mb_l2_add_mc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-450-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-453-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:454:\tstruct rtl8365mb_l2_mc mc = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-455-\tu16 addr;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-465-\tmc.is_static = 1;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:466:\trtl8365mb_l2_mc_to_data(\u0026mc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-467-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-468-\t/* First look for an existing entry (to get existing port members) */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:469:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-470-\t\t\t\t RTL8365MB_TABLE_OP_READ, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-474-\t\t/* There is already an entry... */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:475:\t\trtl8365mb_l2_data_to_mc(data, \u0026mc);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-476-\t\tdev_dbg(priv-\u003edev,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-489-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:490:\t\trtl8365mb_l2_mc_to_data(\u0026mc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-491-\t} else if (ret == -ENOENT) {\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-507-\t/* add the new entry or update an existing one */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:508:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-509-\t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-519-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:520:int rtl8365mb_l2_del_mc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-521-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-524-\tu16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:525:\tstruct rtl8365mb_l2_mc mc = { 0 };\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-526-\tu16 addr;\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-531-\tmc.key.ivl = true;\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:532:\trtl8365mb_l2_mc_to_data(\u0026mc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-533-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-534-\t/* First look for an existing entry (to get existing port members) */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:535:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-536-\t\t\t\t RTL8365MB_TABLE_OP_READ, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-549-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:550:\trtl8365mb_l2_data_to_mc(data, \u0026mc);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-551-\tdev_dbg(priv-\u003edev,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-567-\t}\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:568:\trtl8365mb_l2_mc_to_data(\u0026mc, data);\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-569-\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-570-\t/* update the existing entry. */\ndrivers/net/dsa/realtek/rtl8365mb_l2.c:571:\tret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,\ndrivers/net/dsa/realtek/rtl8365mb_l2.c-572-\t\t\t\t RTL8365MB_TABLE_OP_WRITE, \u0026addr,\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-1-/* SPDX-License-Identifier: GPL-2.0 */\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:2:/* Forwarding and multicast database interface for the rtl8365mb switch family\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-3- *\n--\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-14-\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:15:int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-16-\t\t\t struct realtek_fdb_entry *entry);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:17:int rtl8365mb_l2_add_uc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-18-\t\t\tconst unsigned char addr[static ETH_ALEN],\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-19-\t\t\tu16 efid, u16 vid);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:20:int rtl8365mb_l2_del_uc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-21-\t\t\tconst unsigned char addr[static ETH_ALEN],\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-22-\t\t\tu16 efid, u16 vid);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:23:int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-24-\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:25:int rtl8365mb_l2_add_mc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-26-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-27-\t\t\tu16 vid);\ndrivers/net/dsa/realtek/rtl8365mb_l2.h:28:int rtl8365mb_l2_del_mc(struct realtek_priv *priv, int port,\ndrivers/net/dsa/realtek/rtl8365mb_l2.h-29-\t\t\tconst unsigned char mac_addr[static ETH_ALEN],\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-64- * require the rtl8367d vendor driver. With all this uncertainty, the driver has\ndrivers/net/dsa/realtek/rtl8365mb_main.c:65: * been modestly named rtl8365mb. Future implementors may wish to rename things\ndrivers/net/dsa/realtek/rtl8365mb_main.c-66- * accordingly.\n--\ndrivers/net/dsa/realtek/rtl8365mb_main.c-112-#include \"rtl83xx.h\"\ndrivers/net/dsa/realtek/rtl8365mb_main.c:113:#include \"rtl8365mb_l2.h\"\ndrivers/net/dsa/realtek/rtl8365mb_main.c:114:#include \"rtl8365mb_vlan.h\"\ndrivers/net/dsa/realtek/rtl8365mb_main.c:115:#include \"rtl8365mb.h\"\ndrivers/net/dsa/realtek/rtl8365mb_main.c-116-\n\n"
} |
| 33/2 | 2026/10/09 18:12 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 57817 output: 42 thoughts: 25 Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience, and in the first person: **Analyzing the Kconfig for NET_DSA_REALTEK_RTL8365MB** Alright, let's dive in. My immediate thought is, "Right, time to check the Kconfig for `NET_DSA_REALTEK_RTL8365MB`". I need to understand the configuration options and dependencies associated with this particular Realtek RTL8365MB switch driver within the DSA framework. I'm thinking about the various aspects I need to quickly grasp. I want to quickly ascertain: Are there any potentially problematic dependencies? Are there any potentially interesting or obscure configuration flags I should be aware of? And most importantly, can I get a quick overview of whether this driver is likely to play nice with other network components already configured in the build? Are there any obvious conflicts to avoid, any important considerations for this specific target? I need to build a mental picture of its configuration landscape. |
| 34/2 | 2026/10/09 18:12 | tool | grepper |
0mArgs:{
"Expression": "NET_DSA_REALTEK_RTL8365MB",
"PathPrefixes": [
"drivers/net/dsa"
]
}Results: {
"Output": "drivers/net/dsa/realtek/Kconfig=25=config NET_DSA_REALTEK_SMI\n--\ndrivers/net/dsa/realtek/Kconfig-31-\ndrivers/net/dsa/realtek/Kconfig:32:config NET_DSA_REALTEK_RTL8365MB\ndrivers/net/dsa/realtek/Kconfig-33-\ttristate \"Realtek RTL8365MB switch driver\"\n--\ndrivers/net/dsa/realtek/Makefile=17=endif\ndrivers/net/dsa/realtek/Makefile:18:obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o\ndrivers/net/dsa/realtek/Makefile-19-rtl8365mb-objs := rtl8365mb_main.o \\\n"
} |
| 35/2 | 2026/10/09 18:12 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 58329 output: 38 thoughts: 18 Thoughts: Okay, here's my interpretation of that prompt, and my response: **Initial Thoughts on Kconfig and Network Driver Configuration** Alright, let's dive in. The prompt tells me to look at `drivers/net/dsa/realtek/Kconfig`. Right away, my expert intuition kicks in. This tells me we're dealing with the configuration for a Realtek-based DSA (Distributed Switch Architecture) network driver. Kconfig files are the heart of how we configure and build the Linux kernel, so this is where the meat of the driver's options and dependencies are defined. I'm expecting to find a structured hierarchy of configuration variables, dependencies on other kernel features, and probably a few conditional statements based on the specific Realtek chipsets supported. I'm also anticipating things like enabling or disabling various hardware features – VLAN support, QoS, possibly even PHY configuration options depending on how tightly integrated the driver is. This is going to give me insight into how granular the controls are for users, and what the maintainers have prioritized. Let's see what’s inside. |
| 36/2 | 2026/10/09 18:12 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/realtek/Kconfig",
"FirstLine": 1,
"LineCount": 55
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tmenuconfig NET_DSA_REALTEK\n 3:\t\ttristate \"Realtek Ethernet switch family support\"\n 4:\t\tdepends on NET_DSA\n 5:\t\tselect FIXED_PHY\n 6:\t\tselect IRQ_DOMAIN\n 7:\t\tselect REALTEK_PHY\n 8:\t\tselect REGMAP\n 9:\t\thelp\n 10:\t\t Select to enable support for Realtek Ethernet switch chips.\n 11:\t\n 12:\t\t Note that at least one interface driver must be enabled for the\n 13:\t\t subdrivers to be loaded. Moreover, an interface driver cannot achieve\n 14:\t\t anything without at least one subdriver enabled.\n 15:\t\n 16:\tif NET_DSA_REALTEK\n 17:\t\n 18:\tconfig NET_DSA_REALTEK_MDIO\n 19:\t\tbool \"Realtek MDIO interface support\"\n 20:\t\tdepends on OF\n 21:\t\thelp\n 22:\t\t Select to enable support for registering switches configured\n 23:\t\t through MDIO.\n 24:\t\n 25:\tconfig NET_DSA_REALTEK_SMI\n 26:\t\tbool \"Realtek SMI interface support\"\n 27:\t\tdepends on OF\n 28:\t\thelp\n 29:\t\t Select to enable support for registering switches connected\n 30:\t\t through SMI.\n 31:\t\n 32:\tconfig NET_DSA_REALTEK_RTL8365MB\n 33:\t\ttristate \"Realtek RTL8365MB switch driver\"\n 34:\t\tdepends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO\n 35:\t\tselect NET_DSA_TAG_RTL8_4\n 36:\t\thelp\n 37:\t\t Select to enable support for Realtek RTL8365MB-VC and RTL8367S.\n 38:\t\n 39:\tconfig NET_DSA_REALTEK_RTL8366RB\n 40:\t\ttristate \"Realtek RTL8366RB switch driver\"\n 41:\t\tdepends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO\n 42:\t\tselect NET_DSA_TAG_RTL4_A\n 43:\t\thelp\n 44:\t\t Select to enable support for Realtek RTL8366RB.\n 45:\t\n 46:\tconfig NET_DSA_REALTEK_RTL8366RB_LEDS\n 47:\t\tbool\n 48:\t\tdepends on (LEDS_CLASS=y || LEDS_CLASS=NET_DSA_REALTEK_RTL8366RB)\n 49:\t\tdepends on NET_DSA_REALTEK_RTL8366RB\n 50:\t\tdefault NET_DSA_REALTEK_RTL8366RB\n 51:\t\n 52:\tendif\n"
} |
| 37/2 | 2026/10/09 18:12 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 59375 output: 35 thoughts: 291 Thoughts: **Analyzing the Realtek RTL8365MB Driver Dependencies** Okay, let's break this down. That `NET_DSA_REALTEK_RTL8365MB` driver caught my eye. Right, it depends on either `NET_DSA_REALTEK_SMI` or `NET_DSA_REALTEK_MDIO`, and *both* of those depend on `OF` – Open Firmware, the Device Tree. Makes sense, they're expecting to find a physical switch, a Realtek, connected through OF nodes. And that's exactly what I suspected: `NET_DSA_REALTEK_MDIO` and `NET_DSA_REALTEK_SMI` are the drivers handling communication with that hardware. The probe function is the tell, it's going to use MDIO or SMI GPIO pins to read the chip ID (`rtl8365mb_get_chip_id_and_ver`), and compare it against those hardcoded values, `0x6367` and `0x6642`. If it doesn't get a valid ID back, the probe's going to fail with `-ENODEV` or some other error, right? And that's the kicker: standard virtualized environments, like QEMU or Google Compute Engine running on amd64, they *don't* emulate Realtek RTL8365MB or RTL8367 switch chips over MDIO or SMI. So, trying to run this driver in those environments is going to be a dead end. Now, a question is brewing. Can we even *mock* DSA devices in syzkaller? It's a key question for fuzzing these types of drivers. I should probably check for any DSA mock devices, specifically looking at the kernel or syzkaller. Time to grep the `drivers/net/dsa/` directory. |
| 38/2 | 2026/10/09 18:12 | tool | grepper |
0mArgs:{
"Expression": "mock|virt|dummy|loopback",
"PathPrefixes": [
"drivers/net/dsa"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 386 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (18 files in total):\ndrivers/net/dsa/Kconfig:2\ndrivers/net/dsa/dsa_loop.c:3\ndrivers/net/dsa/lan9303-core.c:5\ndrivers/net/dsa/microchip/ksz8.c:9\ndrivers/net/dsa/microchip/ksz8_reg.h:1\ndrivers/net/dsa/microchip/ksz_common.c:1\ndrivers/net/dsa/mv88e6xxx/chip.c:13\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:9\ndrivers/net/dsa/ocelot/felix_vsc9959.c:7\ndrivers/net/dsa/qca/ar9331.c:2\ndrivers/net/dsa/qca/qca8k.h:1\ndrivers/net/dsa/sja1105/sja1105.h:1\ndrivers/net/dsa/sja1105/sja1105_devlink.c:1\ndrivers/net/dsa/sja1105/sja1105_dynamic_config.c:3\ndrivers/net/dsa/sja1105/sja1105_main.c:1\ndrivers/net/dsa/sja1105/sja1105_tas.c:11\ndrivers/net/dsa/sja1105/sja1105_tas.h:1\ndrivers/net/dsa/sja1105/sja1105_vl.c:17\n\ndrivers/net/dsa/Kconfig=19=config NET_DSA_LOOP\ndrivers/net/dsa/Kconfig:20:\ttristate \"DSA mock-up Ethernet switch chip support\"\ndrivers/net/dsa/Kconfig-21-\tselect NET_DSA_TAG_NONE\n--\ndrivers/net/dsa/Kconfig-23-\thelp\ndrivers/net/dsa/Kconfig:24:\t This enables support for a fake mock-up switch chip which\ndrivers/net/dsa/Kconfig-25-\t exercises the DSA APIs.\n--\ndrivers/net/dsa/dsa_loop.c-2-/*\ndrivers/net/dsa/dsa_loop.c:3: * Distributed Switch Architecture loopback driver\ndrivers/net/dsa/dsa_loop.c-4- *\n--\ndrivers/net/dsa/dsa_loop.c=446=static int __init dsa_loop_create_switch_mdiodev(void)\n--\ndrivers/net/dsa/dsa_loop.c-455-\t\t},\ndrivers/net/dsa/dsa_loop.c:456:\t\t.name = \"DSA mockup driver\",\ndrivers/net/dsa/dsa_loop.c-457-\t\t.enabled_ports = 0x1f,\n--\ndrivers/net/dsa/dsa_loop.c=515=MODULE_AUTHOR(\"Florian Fainelli\");\ndrivers/net/dsa/dsa_loop.c:516:MODULE_DESCRIPTION(\"DSA loopback driver\");\n--\ndrivers/net/dsa/lan9303-core.c=199=static const struct regmap_range lan9303_valid_regs[] = {\n--\ndrivers/net/dsa/lan9303-core.c-208-\tregmap_reg_range(0x6d, 0x6f), /* misc */\ndrivers/net/dsa/lan9303-core.c:209:\tregmap_reg_range(0x70, 0x77), /* virtual phy */\ndrivers/net/dsa/lan9303-core.c-210-\tregmap_reg_range(0x78, 0x7a), /* GPIO */\n--\ndrivers/net/dsa/lan9303-core.c=264=static int lan9303_read_wait(struct lan9303 *chip, int offset, u32 mask)\n--\ndrivers/net/dsa/lan9303-core.c-285-\ndrivers/net/dsa/lan9303-core.c:286:static int lan9303_virt_phy_reg_read(struct lan9303 *chip, int regnum)\ndrivers/net/dsa/lan9303-core.c-287-{\n--\ndrivers/net/dsa/lan9303-core.c-300-\ndrivers/net/dsa/lan9303-core.c:301:static int lan9303_virt_phy_reg_write(struct lan9303 *chip, int regnum, u16 val)\ndrivers/net/dsa/lan9303-core.c-302-{\n--\ndrivers/net/dsa/lan9303-core.c=1079=static int lan9303_phy_read(struct dsa_switch *ds, int port, int regnum)\n--\ndrivers/net/dsa/lan9303-core.c-1084-\tif (port == 0)\ndrivers/net/dsa/lan9303-core.c:1085:\t\treturn lan9303_virt_phy_reg_read(chip, regnum);\ndrivers/net/dsa/lan9303-core.c-1086-\tif (port \u003e 2)\n--\ndrivers/net/dsa/lan9303-core.c=1092=static int lan9303_phy_write(struct dsa_switch *ds, int port, int regnum,\n--\ndrivers/net/dsa/lan9303-core.c-1098-\tif (port == 0)\ndrivers/net/dsa/lan9303-core.c:1099:\t\treturn lan9303_virt_phy_reg_write(chip, regnum, val);\ndrivers/net/dsa/lan9303-core.c-1100-\tif (port \u003e 2)\n--\ndrivers/net/dsa/microchip/ksz8.c=905=static void ksz8_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan)\n--\ndrivers/net/dsa/microchip/ksz8.c-921-/**\ndrivers/net/dsa/microchip/ksz8.c:922: * ksz879x_get_loopback - KSZ879x specific function to get loopback\ndrivers/net/dsa/microchip/ksz8.c-923- * configuration status for a specific port\n--\ndrivers/net/dsa/microchip/ksz8.c-927- *\ndrivers/net/dsa/microchip/ksz8.c:928: * This function reads the SMI registers to determine whether loopback mode\ndrivers/net/dsa/microchip/ksz8.c-929- * is enabled for a specific port.\n--\ndrivers/net/dsa/microchip/ksz8.c-932- */\ndrivers/net/dsa/microchip/ksz8.c:933:static int ksz879x_get_loopback(struct ksz_device *dev, u16 port,\ndrivers/net/dsa/microchip/ksz8.c-934-\t\t\t\tu16 *val)\n--\ndrivers/net/dsa/microchip/ksz8.c-949-/**\ndrivers/net/dsa/microchip/ksz8.c:950: * ksz879x_set_loopback - KSZ879x specific function to set loopback mode for\ndrivers/net/dsa/microchip/ksz8.c-951- *\t\t\t a specific port\n--\ndrivers/net/dsa/microchip/ksz8.c-953- * @port: Port number to modify.\ndrivers/net/dsa/microchip/ksz8.c:954: * @val: Value indicating whether to enable or disable loopback mode.\ndrivers/net/dsa/microchip/ksz8.c-955- *\ndrivers/net/dsa/microchip/ksz8.c:956: * This function translates loopback bit of the BMCR register into the\ndrivers/net/dsa/microchip/ksz8.c-957- * corresponding hardware register bit value and writes it to the SMI interface.\n--\ndrivers/net/dsa/microchip/ksz8.c-960- */\ndrivers/net/dsa/microchip/ksz8.c:961:static int ksz879x_set_loopback(struct ksz_device *dev, u16 port, u16 val)\ndrivers/net/dsa/microchip/ksz8.c-962-{\n--\ndrivers/net/dsa/microchip/ksz8.c=1089=static int ksz8_r_phy_bmcr(struct ksz_device *dev, u16 port, u16 *val)\n--\ndrivers/net/dsa/microchip/ksz8.c-1118-\t} else {\ndrivers/net/dsa/microchip/ksz8.c:1119:\t\tret = ksz879x_get_loopback(dev, port, val);\ndrivers/net/dsa/microchip/ksz8.c-1120-\t\tif (ret)\n--\ndrivers/net/dsa/microchip/ksz8.c=1370=static int ksz8_w_phy_bmcr(struct ksz_device *dev, u16 port, u16 val)\n--\ndrivers/net/dsa/microchip/ksz8.c-1444-\t} else {\ndrivers/net/dsa/microchip/ksz8.c:1445:\t\tret = ksz879x_set_loopback(dev, port, val);\ndrivers/net/dsa/microchip/ksz8.c-1446-\t\tif (ret)\n--\ndrivers/net/dsa/microchip/ksz8_reg.h-737-\ndrivers/net/dsa/microchip/ksz8_reg.h:738:/* Vendor-specific Clause 22 PHY registers (virtualized) */\ndrivers/net/dsa/microchip/ksz8_reg.h-739-#define PHY_REG_KSZ87XX_SHORT_CABLE\t\t0x1A\n--\ndrivers/net/dsa/microchip/ksz_common.c=140=static const struct ksz_drive_strength ksz9477_drive_strengths[] = {\n--\ndrivers/net/dsa/microchip/ksz_common.c-154- *\ndrivers/net/dsa/microchip/ksz_common.c:155: * This function is a dummy handler. See ksz_phylink_mac_enable_tx_lpi() for\ndrivers/net/dsa/microchip/ksz_common.c-156- * a detailed explanation of EEE/LPI handling in KSZ switches.\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=1523=static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-1542-\t\t}\ndrivers/net/dsa/mv88e6xxx/chip.c:1543:\t/* dev is a virtual bridge */\ndrivers/net/dsa/mv88e6xxx/chip.c-1544-\t} else {\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-1558-\ndrivers/net/dsa/mv88e6xxx/chip.c:1559:\t/* Prevent frames from unknown switch or virtual bridge */\ndrivers/net/dsa/mv88e6xxx/chip.c-1560-\tif (!found)\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3004=static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3035-\ndrivers/net/dsa/mv88e6xxx/chip.c:3036:/* Treat the software bridge as a virtual single-port switch behind the\ndrivers/net/dsa/mv88e6xxx/chip.c-3037- * CPU and map in the PVT. First dst-\u003elast_switch elements are taken by\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3039- */\ndrivers/net/dsa/mv88e6xxx/chip.c:3040:static int mv88e6xxx_map_virtual_bridge_to_pvt(struct dsa_switch *ds,\ndrivers/net/dsa/mv88e6xxx/chip.c-3041-\t\t\t\t\t unsigned int bridge_num)\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3049=static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3071-\tif (mv88e6xxx_has_pvt(chip)) {\ndrivers/net/dsa/mv88e6xxx/chip.c:3072:\t\terr = mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num);\ndrivers/net/dsa/mv88e6xxx/chip.c-3073-\t\tif (err)\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3085=static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3093-\tif (bridge.tx_fwd_offload \u0026\u0026\ndrivers/net/dsa/mv88e6xxx/chip.c:3094:\t mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num))\ndrivers/net/dsa/mv88e6xxx/chip.c-3095-\t\tdev_err(ds-\u003edev, \"failed to remap cross-chip Port VLAN\\n\");\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3116=static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds,\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3128-\terr = mv88e6xxx_pvt_map(chip, sw_index, port);\ndrivers/net/dsa/mv88e6xxx/chip.c:3129:\terr = err ? : mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num);\ndrivers/net/dsa/mv88e6xxx/chip.c-3130-\tmv88e6xxx_reg_unlock(chip);\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3135=static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds,\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3145-\tif (mv88e6xxx_pvt_map(chip, sw_index, port) ||\ndrivers/net/dsa/mv88e6xxx/chip.c:3146:\t mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num))\ndrivers/net/dsa/mv88e6xxx/chip.c-3147-\t\tdev_err(ds-\u003edev, \"failed to remap cross-chip Port VLAN\\n\");\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3373=static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3495-\t/* Bind MV88E6XXX_VID_STANDALONE to MV88E6XXX_FID_STANDALONE by\ndrivers/net/dsa/mv88e6xxx/chip.c:3496:\t * virtue of the fact that mv88e6xxx_atu_new() will pick it as\ndrivers/net/dsa/mv88e6xxx/chip.c-3497-\t * the first free FID. This will be used as the private PVID for\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3508-\t/* Associate MV88E6XXX_VID_BRIDGED with MV88E6XXX_FID_BRIDGED in the\ndrivers/net/dsa/mv88e6xxx/chip.c:3509:\t * ATU by virtue of the fact that mv88e6xxx_atu_new() will pick it as\ndrivers/net/dsa/mv88e6xxx/chip.c-3510-\t * the first free FID after MV88E6XXX_FID_STANDALONE. This will be used\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3681=static int mv88e6320_setup_errata(struct mv88e6xxx_chip *chip)\ndrivers/net/dsa/mv88e6xxx/chip.c-3682-{\ndrivers/net/dsa/mv88e6xxx/chip.c:3683:\tu16 dummy;\ndrivers/net/dsa/mv88e6xxx/chip.c-3684-\tint err;\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3692-\ndrivers/net/dsa/mv88e6xxx/chip.c:3693:\treturn mv88e6xxx_port_hidden_read(chip, 0, 0xf, 0x7, \u0026dummy);\ndrivers/net/dsa/mv88e6xxx/chip.c-3694-}\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3989=static int mv88e6xxx_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-4002-\ndrivers/net/dsa/mv88e6xxx/chip.c:4003:\t/* Since virtual bridges are mapped in the PVT, the number we support\ndrivers/net/dsa/mv88e6xxx/chip.c-4004-\t * depends on the physical switch topology. We need to let DSA figure\n--\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h=928=struct mxl862xx_extendedvlan_treatment_vlan {\n--\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h-954- * @dscp2pcp_map: DSCP to PCP mapping table (64 entries)\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:955: * @loopback_enable: Enable loopback\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h-956- * @da_sa_swap_enable: Enable DA/SA swap\n--\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h=959=struct mxl862xx_extendedvlan_treatment {\n--\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h-974-\tu8 dscp2pcp_map[64];\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:975:\tu8 loopback_enable;\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h-976-\tu8 da_sa_swap_enable;\n--\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h=1414=union mxl862xx_xpcs_an_word {\n--\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h-1428-/**\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:1429: * enum mxl862xx_xpcs_loopback_mode - XPCS loopback mode\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:1430: * @MXL862XX_XPCS_LB_DISABLE: disable all loopback\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:1431: * @MXL862XX_XPCS_LB_PCS_SERIAL: PCS TX-to-RX serial loopback\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:1432: * @MXL862XX_XPCS_LB_PCS_PARALLEL: PCS RX-to-TX parallel loopback\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:1433: * @MXL862XX_XPCS_LB_PMA_SERIAL: PMA TX-to-RX serial loopback\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:1434: * @MXL862XX_XPCS_LB_PMA_PARALLEL: PMA RX-to-TX parallel loopback\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h-1435- */\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h:1436:enum mxl862xx_xpcs_loopback_mode {\ndrivers/net/dsa/mxl862xx/mxl862xx-api.h-1437-\tMXL862XX_XPCS_LB_DISABLE = 0,\n--\ndrivers/net/dsa/ocelot/felix_vsc9959.c=1701=struct felix_stream {\n--\ndrivers/net/dsa/ocelot/felix_vsc9959.c-1703-\tunsigned long id;\ndrivers/net/dsa/ocelot/felix_vsc9959.c:1704:\tbool dummy;\ndrivers/net/dsa/ocelot/felix_vsc9959.c-1705-\tint ports;\n--\ndrivers/net/dsa/ocelot/felix_vsc9959.c=1854=static int vsc9959_stream_table_add(struct ocelot *ocelot,\n\n"
} |
| 39/2 | 2026/10/09 18:12 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 66037 output: 143 thoughts: 946 Thoughts: **Analysis of Fuzzing Target: Realtek RTL8365MB Driver** Okay, let's break this down. I'm looking at `dsa_loop.c` as a test driver. The code is not using or touching `rtl8365mb` at all. The goal is to determine if fuzzing the `rtl8365mb` driver, specifically the modified files, is a viable strategy. My understanding is that `rtl8365mb` is a dedicated hardware driver for Realtek Ethernet switch ASICs (RTL8365MB and RTL8367 families). I need to figure out if it's even *possible* to instantiate this driver in a standard virtualized environment like QEMU or GCE. The key requirements for instantiating `rtl8365mb` are: 1. A Device Tree (DT) node that matches "realtek,rtl8365mb", and this node must specify either an MDIO or platform SMI bus. 2. Physical hardware presence. The driver requires regulators, GPIOs, and runs `rtl8365mb_detect(priv)`. 3. `rtl8365mb_detect` calls `rtl8365mb_get_chip_id_and_ver`, which relies on specific register read/write operations over the bus. 4. If communication fails or the chip ID doesn't match a known Realtek chip, the probe fails. 5. Only after successful detection do functions like `rtl8365mb_setup` or any port/vlan/sds operations run. Based on the instructions, structurally unreachable code should not be fuzzed. The instructions say that vendor-specific hardware like PCIe switches and SmartNICs are examples of code that is structurally unreachable, because they require physical ASICs or cards not emulated in standard QEMU. `rtl8365mb` is a vendor-specific hardware Ethernet switch ASIC. The code requires physical hardware connected via SMI (GPIOs) or MDIO, neither of which are emulated or accessible in standard environments. The architecture is amd64. The modified files are all inside the Realtek RTL8365MB DSA switch driver. Therefore, I can conclude that this code is structurally unreachable hardware. `WorthFuzzing` must be false. Now, let's verify what parameters I need for `set-results`: * `WorthFuzzing`: I set to `false`. * `Reasoning`: I'll concisely explain that it's a hardware driver requiring physical hardware connected over SMI or MDIO, with specific DT bindings. Also, the probe requires reading specific hardware chip IDs, and this is not emulated in QEMU/GCE. * `EnableConfigs`: It can be an empty list, or include `NET_DSA_REALTEK_RTL8365MB`. I'll set it to an empty list since `WorthFuzzing` is `false`. * `FocusSymbols`: Set this to an empty list. I've verified the expected parameters. I'm ready to call `set-results`. |
| 40/2 | 2026/10/09 18:14 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patched code is part of the Realtek RTL8365MB switch driver (drivers/net/dsa/realtek/rtl8365mb*), which drives physical Ethernet switch ASICs (RTL8365MB/RTL8367 family) connected via Device Tree over MDIO or SMI GPIO buses. Probe requires hardware communication to match specific chip IDs, and this hardware is not emulated in standard QEMU or GCE virtual environments. Therefore, the modified code is structurally unreachable for fuzzing.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patched code is part of the Realtek RTL8365MB switch driver (drivers/net/dsa/realtek/rtl8365mb*), which drives physical Ethernet switch ASICs (RTL8365MB/RTL8367 family) connected via Device Tree over MDIO or SMI GPIO buses. Probe requires hardware communication to match specific chip IDs, and this hardware is not emulated in standard QEMU or GCE virtual environments. Therefore, the modified code is structurally unreachable for fuzzing.",
"WorthFuzzing": false
} |
| 41/1 | 2026/10/09 18:14 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|