| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/31 01:13 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"PHYLIB",
"USB_NET_SMSC95XX",
"MDIO_REALTEK_RTL9300"
],
"FocusSymbols": [
"phy_attach_direct",
"phy_detach"
],
"Reasoning": "The patch adds notify_phy_attach and notify_phy_detach callbacks to the core PHY subsystem and implements them in the Realtek RTL9300 MDIO driver. The core PHY changes in phy_attach_direct and phy_detach are reachable via virtual/emulated network devices (e.g., USB SMSC95xx) and should be fuzzed to ensure the new error handling and callback logic is robust.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/31 01:13 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 8bb0ff0134436b490bf30e5dacc2f52487707302\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Jul 31 01:13:36 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml b/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml\nindex 271e05bae9c57..67e0b23a8470e 100644\n--- a/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml\n+++ b/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml\n@@ -4,7 +4,7 @@\n $id: http://devicetree.org/schemas/net/realtek,rtl9301-mdio.yaml#\n $schema: http://devicetree.org/meta-schemas/core.yaml#\n \n-title: Realtek RTL9300 MDIO Controller\n+title: Realtek Otto Switches Ethernet MDIO Controller\n \n maintainers:\n - Chris Packham \u003cchris.packham@alliedtelesis.co.nz\u003e\n@@ -12,6 +12,16 @@ maintainers:\n properties:\n compatible:\n oneOf:\n+ - items:\n+ - enum:\n+ - realtek,rtl8381-mdio\n+ - realtek,rtl8382-mdio\n+ - const: realtek,rtl8380-mdio\n+ - items:\n+ - enum:\n+ - realtek,rtl8392-mdio\n+ - realtek,rtl8393-mdio\n+ - const: realtek,rtl8391-mdio\n - items:\n - enum:\n - realtek,rtl9302b-mdio\n@@ -24,6 +34,8 @@ properties:\n - realtek,rtl9313-mdio\n - const: realtek,rtl9311-mdio\n - enum:\n+ - realtek,rtl8380-mdio\n+ - realtek,rtl8391-mdio\n - realtek,rtl9301-mdio\n - realtek,rtl9311-mdio\n \ndiff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c\nindex afd52a1cd7f83..d9d4b704afb3d 100644\n--- a/drivers/net/mdio/mdio-realtek-rtl9300.c\n+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c\n@@ -35,12 +35,88 @@\n *\n * The driver works out the mapping based on the MDIO bus described in device tree and phandles on\n * the ethernet-ports property.\n+ *\n+ * The devices have a hardware polling unit that runs in the background without any CPU load. It\n+ * constantly scans the MDIO bus and the attached PHYs and updates the MAC status registers.\n+ *\n+ * How does the polling work?\n+ *\n+ * Each device has a SMI_POLL_CTRL register. A per-port bitmask decides if the hardware polling of\n+ * the associated bus/address is active or not. The hardware runs a tight loop over this and for\n+ * each set polling bit it issues a status check for the PHY. Attaching a logic analyzer to the\n+ * MDIO bus of an RTL8380 and RTL8393 gives the following commands (in kernel notation):\n+ *\n+ *\tRTL8380\t\t\t\tRTL8393\n+ *\t---------------------------\t---------------------------\n+ *\tphy_write(phy, 31, 0x0);\tphy_read(phy, 0);\n+ *\tphy_write(phy, 13, 0x7);\tphy_read(phy, 1);\n+ *\tphy_write(phy, 14, 0x3c);\tphy_read(phy, 4);\n+ *\tphy_write(phy, 13, 0x8007);\tphy_read(phy, 5);\n+ *\tphy_read(phy, 14);\t\tphy_read(phy, 6);\n+ *\tphy_write(phy, 13, 0x7);\tphy_read(phy, 9);\n+ *\tphy_write(phy, 14, 0x3d);\tphy_read(phy, 10);\n+ *\tphy_write(phy, 13, 0x8007);\tphy_read(phy, 15);\n+ *\tphy_read(phy, 14);\t\tphy_write(phy, 13, 0x7);\n+ *\tphy_read(phy, 9);\t\tphy_write(phy, 14, 0x3c);\n+ *\tphy_read(phy, 10);\t\tphy_write(phy, 13, 0x4007);\n+ *\tphy_read(phy, 15);\t\tphy_read(phy, 14);\n+ *\tphy_read(phy, 0);\t\tphy_write(phy, 13, 0x7);\n+ *\tphy_read(phy, 1);\t\tphy_write(phy, 14, 0x3d);\n+ *\tphy_read(phy, 4);\t\tphy_write(phy, 13, 0x4007);\n+ *\tphy_read(phy, 5);\t\tphy_read(phy, 14);\n+ *\tphy_read(phy, 6);\n+ *\n+ * After one PHY status is read, the polling engine goes over to the next PHY. If one bus is fully\n+ * scanned it switches over to the next bus. Basically the polling system is always busy and the\n+ * MAC state is updated in real-time.\n+ *\n+ * This is a glimpse look at the complexity of the polling and leaves out the C45 case and the MAC\n+ * register update logic afterwards. Important to note:\n+ *\n+ * - 100 MBit downshifts (broken cables) are identified and propagated to the MAC correctly\n+ * - Access to MDIO_AN_EEE_ADV and MDIO_AN_EEE_LPABLE works via C45 over C22.\n+ * - It is unclear if these sequences change for different PHYs.\n+ * - Access to Realtek reserved register 26 (link speed) has not yet been seen.\n+ *\n+ * How does MDIO access from kernel work?\n+ *\n+ * When issuing MDIO accesses via an MMIO based interface the final write to the command register\n+ * sets a \"run command now\" bit. Between two polling sequences for different PHYs the hardware\n+ * checks if a user command needs to run and sends it onto the bus. Afterwards it simply continues\n+ * its polling work. Inspecting the command sequence for a paged write on the logic analyzer gives:\n+ *\n+ *\tRTL8380\t\t\t\tRTL8393\n+ *\t---------------------------\t---------------------------\n+ *\tphy_write(phy, 31, page);\tphy_write(phy, 31, page);\n+ *\tphy_write(phy, reg, value);\tphy_write(phy, reg, value);\n+ *\t\t\t\t\tphy_write(phy, 31, 0);\n+ *\n+ * What does this mean?\n+ *\n+ * There are slight differences in polling and PHY access between the models but the challenge\n+ * stays the same. On the one hand that greatly simplifies the MAC layer, on the other hand it\n+ * has some implications for the kernel PHY subsystem.\n+ *\n+ * - Without the polling and a proper MAC status, some of the link handling features do not work.\n+ * Especially an unpopulated MAC_LINK_STS register cancels operations to other MAC registers.\n+ * - The Realtek page register 31 is magically modified in the background so that polling will\n+ * read the right data. On the RTL838x polling simply resets it to zero. Other devices seem\n+ * to track the page access \"magically\" in the background.\n+ * - A C45 over C22 kernel access sequence is most likely to fail because chances are high that\n+ * the polling engine overwrites registers 13/14 in between.\n+ * - PHY firmware loading can have issues. Especially if a PHY is designed to expect a clean\n+ * sequence of registers and values without deviation.\n+ * - An access to one PHY will need to wait for the next free slot of the polling engine.\n+ *\n+ * Conclusion: The Realtek MDIO bus driver PHY access must know and handle any interference that\n+ * arises from the above described hardware polling.\n */\n \n #include \u003clinux/bitfield.h\u003e\n #include \u003clinux/bitmap.h\u003e\n #include \u003clinux/bits.h\u003e\n #include \u003clinux/find.h\u003e\n+#include \u003clinux/kref.h\u003e\n #include \u003clinux/mdio.h\u003e\n #include \u003clinux/mfd/syscon.h\u003e\n #include \u003clinux/mutex.h\u003e\n@@ -50,6 +126,50 @@\n #include \u003clinux/property.h\u003e\n #include \u003clinux/regmap.h\u003e\n \n+#define RTL8380_NUM_BUSES\t\t\t1\n+#define RTL8380_NUM_PAGES\t\t\t4096\n+#define RTL8380_NUM_PORTS\t\t\t28\n+#define RTL8380_SMI_GLB_CTRL\t\t\t0xa100\n+#define RTL8380_SMI_PHY_PATCH_DONE\t\tBIT(15)\n+#define RTL8380_SMI_ACCESS_PHY_CTRL_0\t\t0xa1b8\n+#define RTL8380_SMI_ACCESS_PHY_CTRL_1\t\t0xa1bc\n+#define RTL8380_PHY_CTRL_REG_ADDR\t\tGENMASK(24, 20)\n+#define RTL8380_PHY_CTRL_PARK_PAGE\t\tGENMASK(19, 15)\n+#define RTL8380_PHY_CTRL_MAIN_PAGE\t\tGENMASK(14, 3)\n+#define RTL8380_PHY_CTRL_WRITE\t\tBIT(2)\n+#define RTL8380_PHY_CTRL_READ\t\t\t0\n+#define RTL8380_PHY_CTRL_TYPE_C45\t\tBIT(1)\n+#define RTL8380_PHY_CTRL_TYPE_C22\t\t0\n+#define RTL8380_PHY_CTRL_FAIL\t\t\t0 /* no fail indicator */\n+#define RTL8380_SMI_ACCESS_PHY_CTRL_2\t\t0xa1c0\n+#define RTL8380_PHY_CTRL_INDATA\t\tGENMASK(31, 16)\n+#define RTL8380_PHY_CTRL_DATA\t\t\tGENMASK(15, 0)\n+#define RTL8380_SMI_ACCESS_PHY_CTRL_3\t\t0xa1c4\n+#define RTL8380_SMI_POLL_CTRL\t\t\t0xa17c\n+#define RTL8380_SMI_PORT0_5_ADDR_CTRL\t\t0xa1c8\n+\n+#define RTL8390_NUM_BUSES\t\t\t2\n+#define RTL8390_NUM_PAGES\t\t\t8192\n+#define RTL8390_NUM_PORTS\t\t\t52\n+#define RTL8390_BCAST_PHYID_CTRL\t\t0x03ec\n+#define RTL8390_PHYREG_ACCESS_CTRL\t\t0x03dc\n+#define RTL8390_PHY_CTRL_REG_ADDR\t\tGENMASK(9, 5)\n+#define RTL8390_PHY_CTRL_MAIN_PAGE\t\tGENMASK(22, 10)\n+#define RTL8390_PHY_CTRL_FAIL\t\t\tBIT(1)\n+#define RTL8390_PHY_CTRL_WRITE\t\tBIT(3)\n+#define RTL8390_PHY_CTRL_READ\t\t\t0\n+#define RTL8390_PHY_CTRL_TYPE_C45\t\tBIT(2)\n+#define RTL8390_PHY_CTRL_TYPE_C22\t\t0\n+#define RTL8390_PHYREG_CTRL\t\t\t0x03e0\n+#define RTL8390_PHY_CTRL_EXT_PAGE\t\tGENMASK(8, 0)\n+#define RTL8390_PHYREG_DATA_CTRL\t\t0x03f0\n+#define RTL8390_PHY_CTRL_INDATA\t\tGENMASK(31, 16)\n+#define RTL8390_PHY_CTRL_DATA\t\t\tGENMASK(15, 0)\n+#define RTL8390_PHYREG_MMD_CTRL\t\t\t0x03f4\n+#define RTL8390_PHYREG_PORT_CTRL_LOW\t\t0x03e4\n+#define RTL8390_PHYREG_PORT_CTRL_HIGH\t\t0x03e8\n+#define RTL8390_SMI_PORT_POLLING_CTRL\t\t0x03fc\n+\n #define RTL9300_NUM_BUSES\t\t\t4\n #define RTL9300_NUM_PAGES\t\t\t4096\n #define RTL9300_NUM_PORTS\t\t\t28\n@@ -70,6 +190,7 @@\n #define RTL9300_PHY_CTRL_INDATA\t\tGENMASK(31, 16)\n #define RTL9300_PHY_CTRL_DATA\t\t\tGENMASK(15, 0)\n #define RTL9300_SMI_ACCESS_PHY_CTRL_3\t\t0xcb7c\n+#define RTL9300_SMI_POLL_CTRL\t\t\t0xca90\n #define RTL9300_SMI_PORT0_5_ADDR_CTRL\t\t0xcb80\n \n #define RTL9310_NUM_BUSES\t\t\t4\n@@ -95,12 +216,16 @@\n #define RTL9310_PHY_CTRL_INDATA\t\tGENMASK(15, 0)\n #define RTL9310_SMI_INDRT_ACCESS_MMD_CTRL\t0x0c18\n #define RTL9310_SMI_PORT_ADDR_CTRL\t\t0x0c74\n+#define RTL9310_SMI_PORT_POLLING_CTRL\t\t0x0ccc\n #define RTL9310_SMI_PORT_POLLING_SEL\t\t0x0c9c\n \n #define PHY_CTRL_CMD\t\t\t\tBIT(0)\n #define PHY_CTRL_MMD_DEVAD\t\t\tGENMASK(20, 16)\n #define PHY_CTRL_MMD_REG\t\t\tGENMASK(15, 0)\n \n+#define RTL_VENDOR_ID\t\t\t\t0x001cc800\n+#define RTL_PAGE_SELECT\t\t\t\t31\n+\n #define MAP_ADDRS_PER_REG\t\t\t6\n #define MAP_BITS_PER_ADDR\t\t\t5\n #define MAP_BITS_PER_BUS\t\t\t2\n@@ -123,9 +248,11 @@ struct otto_emdio_cmd_regs {\n \n struct otto_emdio_priv {\n \tconst struct otto_emdio_info *info;\n+\tstruct kref kref;\n \tstruct regmap *regmap;\n \tstruct mutex lock; /* protect HW access */\n \tDECLARE_BITMAP(valid_ports, MAX_PORTS);\n+\tu16 page[MAX_PORTS];\n \tu8 smi_bus[MAX_PORTS];\n \tu8 smi_addr[MAX_PORTS];\n \tbool smi_bus_is_c45[MAX_SMI_BUSSES];\n@@ -142,6 +269,7 @@ struct otto_emdio_info {\n \tu8 num_buses;\n \tu8 num_ports;\n \tu16 num_pages;\n+\tu32 poll_ctrl;\n \tint (*setup_controller)(struct otto_emdio_priv *priv);\n \tint (*read_c22)(struct mii_bus *bus, int port, int regnum, u32 *value);\n \tint (*read_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u32 *value);\n@@ -177,6 +305,12 @@ static struct otto_emdio_priv *otto_emdio_bus_to_priv(struct mii_bus *bus)\n \treturn chan-\u003epriv;\n }\n \n+static int otto_emdio_set_port_polling(struct otto_emdio_priv *priv, int port, bool active)\n+{\n+\treturn regmap_assign_bits(priv-\u003eregmap, priv-\u003einfo-\u003epoll_ctrl + (port / 32) * 4,\n+\t\t\t\t BIT(port % 32), active);\n+}\n+\n static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd,\n \t\t\t struct otto_emdio_cmd_regs *cmd_data)\n {\n@@ -185,9 +319,9 @@ static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd,\n \tu32 cmdstate;\n \tint ret;\n \n-\t/* Defensive pre check just in case something goes horrible wrong */\n+\t/* Defensive pre check just in case something goes horribly wrong */\n \tret = regmap_read_poll_timeout(priv-\u003eregmap, info-\u003ecmd_regs.c22_data,\n-\t\t\t\t cmdstate, !(cmdstate \u0026 PHY_CTRL_CMD), 10, 1000);\n+\t\t\t\t cmdstate, !(cmdstate \u0026 PHY_CTRL_CMD), 10, 10000);\n \tif (ret)\n \t\treturn ret;\n \n@@ -227,7 +361,7 @@ static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd,\n \t\treturn ret;\n \n \tret = regmap_read_poll_timeout(priv-\u003eregmap, info-\u003ecmd_regs.c22_data,\n-\t\t\t\t cmdstate, !(cmdstate \u0026 PHY_CTRL_CMD), 10, 1000);\n+\t\t\t\t cmdstate, !(cmdstate \u0026 PHY_CTRL_CMD), 10, 10000);\n \tif (ret)\n \t\treturn ret;\n \n@@ -264,13 +398,123 @@ static int otto_emdio_write_cmd(struct mii_bus *bus, u32 cmd,\n \treturn otto_emdio_run_cmd(bus, cmd | priv-\u003einfo-\u003ecmd_write, cmd_data);\n }\n \n+static int otto_emdio_8380_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)\n+{\n+\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\n+\tstruct otto_emdio_cmd_regs cmd_data = {\n+\t\t.c22_data\t= FIELD_PREP(RTL8380_PHY_CTRL_REG_ADDR, regnum) |\n+\t\t\t\t FIELD_PREP(RTL8380_PHY_CTRL_PARK_PAGE, 0x1f) |\n+\t\t\t\t FIELD_PREP(RTL8380_PHY_CTRL_MAIN_PAGE, priv-\u003epage[port]),\n+\t\t.io_data\t= FIELD_PREP(RTL8380_PHY_CTRL_INDATA, port),\n+\t};\n+\n+\treturn otto_emdio_read_cmd(bus, RTL8380_PHY_CTRL_TYPE_C22, \u0026cmd_data,\n+\t\t\t\t RTL8380_PHY_CTRL_DATA, value);\n+}\n+\n+static int otto_emdio_8380_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)\n+{\n+\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\n+\tstruct otto_emdio_cmd_regs cmd_data = {\n+\t\t.c22_data\t= FIELD_PREP(RTL8380_PHY_CTRL_REG_ADDR, regnum) |\n+\t\t\t\t FIELD_PREP(RTL8380_PHY_CTRL_PARK_PAGE, 0x1f) |\n+\t\t\t\t FIELD_PREP(RTL8380_PHY_CTRL_MAIN_PAGE, priv-\u003epage[port]),\n+\t\t.io_data\t= FIELD_PREP(RTL8380_PHY_CTRL_INDATA, value),\n+\t\t.port_mask_low\t= BIT(port),\n+\t};\n+\n+\treturn otto_emdio_write_cmd(bus, RTL8380_PHY_CTRL_TYPE_C22, \u0026cmd_data);\n+}\n+\n+static int otto_emdio_8380_read_c45(struct mii_bus *bus, int port,\n+\t\t\t\t int dev_addr, int regnum, u32 *value)\n+{\n+\tstruct otto_emdio_cmd_regs cmd_data = {\n+\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n+\t\t\t\t FIELD_PREP(PHY_CTRL_MMD_REG, regnum),\n+\t\t.io_data\t= FIELD_PREP(RTL8380_PHY_CTRL_INDATA, port),\n+\t};\n+\n+\treturn otto_emdio_read_cmd(bus, RTL8380_PHY_CTRL_TYPE_C45, \u0026cmd_data,\n+\t\t\t\t RTL8380_PHY_CTRL_DATA, value);\n+}\n+\n+static int otto_emdio_8380_write_c45(struct mii_bus *bus, int port,\n+\t\t\t\t int dev_addr, int regnum, u16 value)\n+{\n+\tstruct otto_emdio_cmd_regs cmd_data = {\n+\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n+\t\t\t\t FIELD_PREP(PHY_CTRL_MMD_REG, regnum),\n+\t\t.io_data\t= FIELD_PREP(RTL8380_PHY_CTRL_INDATA, value),\n+\t\t.port_mask_low\t= BIT(port),\n+\t};\n+\n+\treturn otto_emdio_write_cmd(bus, RTL8380_PHY_CTRL_TYPE_C45, \u0026cmd_data);\n+}\n+\n+static int otto_emdio_8390_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)\n+{\n+\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\n+\tstruct otto_emdio_cmd_regs cmd_data = {\n+\t\t.c22_data\t= FIELD_PREP(RTL8390_PHY_CTRL_REG_ADDR, regnum) |\n+\t\t\t\t FIELD_PREP(RTL8390_PHY_CTRL_MAIN_PAGE, priv-\u003epage[port]),\n+\t\t.ext_page\t= FIELD_PREP(RTL8390_PHY_CTRL_EXT_PAGE, 0x1ff),\n+\t\t.io_data\t= FIELD_PREP(RTL8390_PHY_CTRL_INDATA, port),\n+\t};\n+\n+\treturn otto_emdio_read_cmd(bus, RTL8390_PHY_CTRL_TYPE_C22, \u0026cmd_data,\n+\t\t\t\t RTL8390_PHY_CTRL_DATA, value);\n+}\n+\n+static int otto_emdio_8390_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)\n+{\n+\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\n+\tstruct otto_emdio_cmd_regs cmd_data = {\n+\t\t.c22_data\t= FIELD_PREP(RTL8390_PHY_CTRL_REG_ADDR, regnum) |\n+\t\t\t\t FIELD_PREP(RTL8390_PHY_CTRL_MAIN_PAGE, priv-\u003epage[port]),\n+\t\t.ext_page\t= FIELD_PREP(RTL8390_PHY_CTRL_EXT_PAGE, 0x1ff),\n+\t\t.io_data\t= FIELD_PREP(RTL8390_PHY_CTRL_INDATA, value),\n+\t\t.port_mask_high\t= (u32)(BIT_ULL(port) \u003e\u003e 32),\n+\t\t.port_mask_low\t= (u32)(BIT_ULL(port)),\n+\t};\n+\n+\treturn otto_emdio_write_cmd(bus, RTL8390_PHY_CTRL_TYPE_C22, \u0026cmd_data);\n+}\n+\n+static int otto_emdio_8390_read_c45(struct mii_bus *bus, int port,\n+\t\t\t\t int dev_addr, int regnum, u32 *value)\n+{\n+\tstruct otto_emdio_cmd_regs cmd_data = {\n+\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n+\t\t\t\t FIELD_PREP(PHY_CTRL_MMD_REG, regnum),\n+\t\t.io_data\t= FIELD_PREP(RTL8390_PHY_CTRL_INDATA, port),\n+\t};\n+\n+\treturn otto_emdio_read_cmd(bus, RTL8390_PHY_CTRL_TYPE_C45, \u0026cmd_data,\n+\t\t\t\t RTL8390_PHY_CTRL_DATA, value);\n+}\n+\n+static int otto_emdio_8390_write_c45(struct mii_bus *bus, int port,\n+\t\t\t\t int dev_addr, int regnum, u16 value)\n+{\n+\tstruct otto_emdio_cmd_regs cmd_data = {\n+\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n+\t\t\t\t FIELD_PREP(PHY_CTRL_MMD_REG, regnum),\n+\t\t.io_data\t= FIELD_PREP(RTL8390_PHY_CTRL_INDATA, value),\n+\t\t.port_mask_high\t= (u32)(BIT_ULL(port) \u003e\u003e 32),\n+\t\t.port_mask_low\t= (u32)(BIT_ULL(port)),\n+\t};\n+\n+\treturn otto_emdio_write_cmd(bus, RTL8390_PHY_CTRL_TYPE_C45, \u0026cmd_data);\n+}\n+\n static int otto_emdio_9300_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)\n {\n \tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\n \tstruct otto_emdio_cmd_regs cmd_data = {\n \t\t.c22_data\t= FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) |\n \t\t\t\t FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) |\n-\t\t\t\t FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),\n+\t\t\t\t FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, priv-\u003epage[port]),\n \t\t.io_data\t= FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port),\n \t};\n \n@@ -284,7 +528,7 @@ static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum,\n \tstruct otto_emdio_cmd_regs cmd_data = {\n \t\t.c22_data\t= FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) |\n \t\t\t\t FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) |\n-\t\t\t\t FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),\n+\t\t\t\t FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, priv-\u003epage[port]),\n \t\t.io_data\t= FIELD_PREP(RTL9300_PHY_CTRL_INDATA, value),\n \t\t.port_mask_low\t= BIT(port),\n \t};\n@@ -324,7 +568,7 @@ static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u\n \tstruct otto_emdio_cmd_regs cmd_data = {\n \t\t.broadcast\t= FIELD_PREP(RTL9310_BC_PORT_ID, port),\n \t\t.c22_data\t= FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |\n-\t\t\t\t FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),\n+\t\t\t\t FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, priv-\u003epage[port]),\n \t};\n \n \treturn otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, \u0026cmd_data,\n@@ -336,7 +580,7 @@ static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum,\n \tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\n \tstruct otto_emdio_cmd_regs cmd_data = {\n \t\t.c22_data\t= FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |\n-\t\t\t\t FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),\n+\t\t\t\t FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, priv-\u003epage[port]),\n \t\t.io_data\t= FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value),\n \t\t.port_mask_high\t= (u32)(BIT_ULL(port) \u003e\u003e 32),\n \t\t.port_mask_low\t= (u32)(BIT_ULL(port)),\n@@ -382,8 +626,12 @@ static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)\n \tif (port \u003c 0)\n \t\treturn port;\n \n-\tscoped_guard(mutex, \u0026priv-\u003elock)\n+\tscoped_guard(mutex, \u0026priv-\u003elock) {\n+\t\tif (regnum == RTL_PAGE_SELECT)\n+\t\t\treturn priv-\u003epage[port];\n+\n \t\tret = priv-\u003einfo-\u003eread_c22(bus, port, regnum, \u0026value);\n+\t}\n \n \treturn ret ? ret : value;\n }\n@@ -397,8 +645,17 @@ static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16\n \tif (port \u003c 0)\n \t\treturn port;\n \n-\tscoped_guard(mutex, \u0026priv-\u003elock)\n+\tscoped_guard(mutex, \u0026priv-\u003elock) {\n+\t\tif (regnum == RTL_PAGE_SELECT) {\n+\t\t\tif (value \u003e= RAW_PAGE(priv))\n+\t\t\t\treturn -EINVAL;\n+\n+\t\t\tpriv-\u003epage[port] = value;\n+\t\t\treturn 0;\n+\t\t}\n+\n \t\tret = priv-\u003einfo-\u003ewrite_c22(bus, port, regnum, value);\n+\t}\n \n \treturn ret;\n }\n@@ -471,6 +728,15 @@ static int otto_emdio_setup_topology(struct otto_emdio_priv *priv)\n \treturn 0;\n }\n \n+static int otto_emdio_8380_setup_controller(struct otto_emdio_priv *priv)\n+{\n+\t/*\n+\t * PHY_PATCH_DONE enables PHY control via SoC. This is required for PHY access, including\n+\t * patching and must be set before the PHYs are probed.\n+\t */\n+\treturn regmap_set_bits(priv-\u003eregmap, RTL8380_SMI_GLB_CTRL, RTL8380_SMI_PHY_PATCH_DONE);\n+}\n+\n static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv)\n {\n \tu32 glb_ctrl_mask = 0, glb_ctrl_val = 0;\n@@ -507,6 +773,59 @@ static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)\n \treturn 0;\n }\n \n+static int otto_emdio_notify_phy_attach(struct phy_device *phydev)\n+{\n+\tint port = otto_emdio_phy_to_port(phydev-\u003emdio.bus, phydev-\u003emdio.addr);\n+\tstruct otto_emdio_chan *chan = phydev-\u003emdio.bus-\u003epriv;\n+\tstruct otto_emdio_priv *priv = chan-\u003epriv;\n+\n+\tif (port \u003c 0)\n+\t\treturn port;\n+\n+\tif (!priv-\u003esmi_bus_is_c45[chan-\u003emdio_bus] \u0026\u0026\n+\t !phy_id_compare_vendor(phydev-\u003ephy_id, RTL_VENDOR_ID)) {\n+\t\tphydev_err(phydev, \"Only Realtek PHYs allowed on C22 bus\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\treturn otto_emdio_set_port_polling(priv, port, true);\n+}\n+\n+static void otto_emdio_notify_phy_detach(struct phy_device *phydev)\n+{\n+\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(phydev-\u003emdio.bus);\n+\tint port = otto_emdio_phy_to_port(phydev-\u003emdio.bus, phydev-\u003emdio.addr);\n+\tstruct otto_emdio_chan *chan = phydev-\u003emdio.bus-\u003epriv;\n+\tstruct mii_bus *bus = phydev-\u003emdio.bus;\n+\tint ret;\n+\n+\tif (port \u003c 0)\n+\t\treturn;\n+\n+\t/* avoid wrong cached page for next attachment */\n+\tif (!priv-\u003esmi_bus_is_c45[chan-\u003emdio_bus])\n+\t\tpriv-\u003epage[port] = 0;\n+\n+\tret = otto_emdio_set_port_polling(priv, port, false);\n+\tif (ret)\n+\t\tdev_err(bus-\u003eparent, \"failed to disable polling for port %d\\n\", port);\n+}\n+\n+static void otto_emdio_cleanup_controller(struct kref *kref)\n+{\n+\tstruct otto_emdio_priv *priv = container_of(kref, struct otto_emdio_priv, kref);\n+\n+\tmutex_destroy(\u0026priv-\u003elock);\n+\tkfree(priv);\n+}\n+\n+static void otto_emdio_cleanup_bus(void *data)\n+{\n+\tstruct otto_emdio_priv *priv = data;\n+\n+\tkref_put(\u0026priv-\u003ekref, otto_emdio_cleanup_controller);\n+}\n+\n static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,\n \t\t\t\t struct fwnode_handle *node)\n {\n@@ -536,12 +855,20 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv\n \t\tbus-\u003ewrite = otto_emdio_write_c22;\n \t}\n \tbus-\u003eparent = dev;\n+\tbus-\u003enotify_phy_attach = otto_emdio_notify_phy_attach;\n+\tbus-\u003enotify_phy_detach = otto_emdio_notify_phy_detach;\n+\n \tchan = bus-\u003epriv;\n \tchan-\u003emdio_bus = mdio_bus;\n \tchan-\u003epriv = priv;\n \n \tsnprintf(bus-\u003eid, MII_BUS_ID_SIZE, \"%s-%d\", dev_name(dev), mdio_bus);\n \n+\tkref_get(\u0026priv-\u003ekref);\n+\terr = devm_add_action_or_reset(\u0026bus-\u003edev, otto_emdio_cleanup_bus, priv);\n+\tif (err)\n+\t\treturn dev_err_probe(dev, err, \"cannot register cleanup action\\n\");\n+\n \terr = devm_of_mdiobus_register(dev, bus, to_of_node(node));\n \tif (err)\n \t\treturn dev_err_probe(dev, err, \"cannot register MDIO bus\\n\");\n@@ -652,50 +979,122 @@ static int otto_emdio_map_ports(struct device *dev)\n \treturn err;\n }\n \n+static int otto_emdio_init_polling(struct otto_emdio_priv *priv)\n+{\n+\tint err;\n+\n+\tfor (int port = 0; port \u003c priv-\u003einfo-\u003enum_ports; port++) {\n+\t\terr = otto_emdio_set_port_polling(priv, port, false);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int otto_emdio_probe(struct platform_device *pdev)\n {\n \tstruct device *dev = \u0026pdev-\u003edev;\n \tstruct otto_emdio_priv *priv;\n+\tint buses_registered = 0;\n \tint err;\n \n-\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n+\tpriv = kzalloc_obj(*priv, GFP_KERNEL);\n \tif (!priv)\n \t\treturn -ENOMEM;\n \n-\terr = devm_mutex_init(dev, \u0026priv-\u003elock);\n-\tif (err)\n-\t\treturn err;\n+\tkref_init(\u0026priv-\u003ekref);\n+\tmutex_init(\u0026priv-\u003elock);\n \n \tpriv-\u003einfo = device_get_match_data(dev);\n \tpriv-\u003eregmap = syscon_node_to_regmap(dev-\u003eparent-\u003eof_node);\n-\tif (IS_ERR(priv-\u003eregmap))\n-\t\treturn PTR_ERR(priv-\u003eregmap);\n+\tif (IS_ERR(priv-\u003eregmap)) {\n+\t\terr = PTR_ERR(priv-\u003eregmap);\n+\t\tgoto err_out;\n+\t}\n+\n+\terr = otto_emdio_init_polling(priv);\n+\tif (err)\n+\t\tgoto err_out;\n \n \tplatform_set_drvdata(pdev, priv);\n \n \terr = otto_emdio_map_ports(dev);\n \tif (err)\n-\t\treturn err;\n+\t\tgoto err_out;\n \n \terr = otto_emdio_setup_topology(priv);\n \tif (err)\n-\t\treturn err;\n+\t\tgoto err_out;\n \n \tif (priv-\u003einfo-\u003esetup_controller) {\n \t\terr = priv-\u003einfo-\u003esetup_controller(priv);\n-\t\tif (err)\n-\t\t\treturn dev_err_probe(dev, err, \"failed to setup MDIO bus controller\\n\");\n+\t\tif (err) {\n+\t\t\tdev_err_probe(dev, err, \"failed to setup MDIO bus controller\\n\");\n+\t\t\tgoto err_out;\n+\t\t}\n \t}\n \n \tdevice_for_each_child_node_scoped(dev, child) {\n \t\terr = otto_emdio_probe_one(dev, priv, child);\n \t\tif (err)\n-\t\t\treturn err;\n+\t\t\tgoto err_out;\n+\t\tbuses_registered++;\n \t}\n \n-\treturn 0;\n+\tif (!buses_registered)\n+\t\terr = dev_err_probe(dev, -ENODEV, \"no MDIO buses registered\\n\");\n+err_out:\n+\tkref_put(\u0026priv-\u003ekref, otto_emdio_cleanup_controller);\n+\n+\treturn err;\n }\n \n+static const struct otto_emdio_info otto_emdio_8380_info = {\n+\t.addr_map_base = RTL8380_SMI_PORT0_5_ADDR_CTRL,\n+\t.cmd_fail = RTL8380_PHY_CTRL_FAIL,\n+\t.cmd_read = RTL8380_PHY_CTRL_READ,\n+\t.cmd_write = RTL8380_PHY_CTRL_WRITE,\n+\t.cmd_regs = {\n+\t\t.c22_data = RTL8380_SMI_ACCESS_PHY_CTRL_1,\n+\t\t.c45_data = RTL8380_SMI_ACCESS_PHY_CTRL_3,\n+\t\t.io_data = RTL8380_SMI_ACCESS_PHY_CTRL_2,\n+\t\t.port_mask_low = RTL8380_SMI_ACCESS_PHY_CTRL_0,\n+\t},\n+\t.num_buses = RTL8380_NUM_BUSES,\n+\t.num_pages = RTL8380_NUM_PAGES,\n+\t.num_ports = RTL8380_NUM_PORTS,\n+\t.poll_ctrl = RTL8380_SMI_POLL_CTRL,\n+\t.setup_controller = otto_emdio_8380_setup_controller,\n+\t.read_c22 = otto_emdio_8380_read_c22,\n+\t.read_c45 = otto_emdio_8380_read_c45,\n+\t.write_c22 = otto_emdio_8380_write_c22,\n+\t.write_c45 = otto_emdio_8380_write_c45,\n+};\n+\n+static const struct otto_emdio_info otto_emdio_8390_info = {\n+\t.cmd_fail = RTL8390_PHY_CTRL_FAIL,\n+\t.cmd_read = RTL8390_PHY_CTRL_READ,\n+\t.cmd_write = RTL8390_PHY_CTRL_WRITE,\n+\t.cmd_regs = {\n+\t\t.broadcast = RTL8390_BCAST_PHYID_CTRL,\n+\t\t.c22_data = RTL8390_PHYREG_ACCESS_CTRL,\n+\t\t.c45_data = RTL8390_PHYREG_MMD_CTRL,\n+\t\t.ext_page = RTL8390_PHYREG_CTRL,\n+\t\t.io_data = RTL8390_PHYREG_DATA_CTRL,\n+\t\t.port_mask_low = RTL8390_PHYREG_PORT_CTRL_LOW,\n+\t\t.port_mask_high = RTL8390_PHYREG_PORT_CTRL_HIGH,\n+\t},\n+\t.num_buses = RTL8390_NUM_BUSES,\n+\t.num_pages = RTL8390_NUM_PAGES,\n+\t.num_ports = RTL8390_NUM_PORTS,\n+\t.poll_ctrl = RTL8390_SMI_PORT_POLLING_CTRL,\n+\t.read_c22 = otto_emdio_8390_read_c22,\n+\t.read_c45 = otto_emdio_8390_read_c45,\n+\t.write_c22 = otto_emdio_8390_write_c22,\n+\t.write_c45 = otto_emdio_8390_write_c45,\n+};\n+\n static const struct otto_emdio_info otto_emdio_9300_info = {\n \t.addr_map_base = RTL9300_SMI_PORT0_5_ADDR_CTRL,\n \t.bus_map_base = RTL9300_SMI_PORT0_15_POLLING_SEL,\n@@ -711,6 +1110,7 @@ static const struct otto_emdio_info otto_emdio_9300_info = {\n \t.num_buses = RTL9300_NUM_BUSES,\n \t.num_ports = RTL9300_NUM_PORTS,\n \t.num_pages = RTL9300_NUM_PAGES,\n+\t.poll_ctrl = RTL9300_SMI_POLL_CTRL,\n \t.setup_controller = otto_emdio_9300_setup_controller,\n \t.read_c22 = otto_emdio_9300_read_c22,\n \t.read_c45 = otto_emdio_9300_read_c45,\n@@ -736,6 +1136,7 @@ static const struct otto_emdio_info otto_emdio_9310_info = {\n \t.num_buses = RTL9310_NUM_BUSES,\n \t.num_pages = RTL9310_NUM_PAGES,\n \t.num_ports = RTL9310_NUM_PORTS,\n+\t.poll_ctrl = RTL9310_SMI_PORT_POLLING_CTRL,\n \t.setup_controller = otto_emdio_9310_setup_controller,\n \t.read_c22 = otto_emdio_9310_read_c22,\n \t.read_c45 = otto_emdio_9310_read_c45,\n@@ -744,6 +1145,8 @@ static const struct otto_emdio_info otto_emdio_9310_info = {\n };\n \n static const struct of_device_id otto_emdio_ids[] = {\n+\t{ .compatible = \"realtek,rtl8380-mdio\", .data = \u0026otto_emdio_8380_info },\n+\t{ .compatible = \"realtek,rtl8391-mdio\", .data = \u0026otto_emdio_8390_info },\n \t{ .compatible = \"realtek,rtl9301-mdio\", .data = \u0026otto_emdio_9300_info },\n \t{ .compatible = \"realtek,rtl9311-mdio\", .data = \u0026otto_emdio_9310_info },\n \t{}\ndiff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c\nindex 94b2e85e00a37..233622e9ba61f 100644\n--- a/drivers/net/phy/phy_device.c\n+++ b/drivers/net/phy/phy_device.c\n@@ -1734,6 +1734,96 @@ static bool phy_drv_supports_irq(const struct phy_driver *phydrv)\n \treturn phydrv-\u003econfig_intr \u0026\u0026 phydrv-\u003ehandle_interrupt;\n }\n \n+static void __phy_detach(struct phy_device *phydev, bool notify_bus)\n+{\n+\tstruct net_device *dev = phydev-\u003eattached_dev;\n+\tstruct module *ndev_owner = NULL;\n+\tstruct mii_bus *bus;\n+\n+\tif (phydev-\u003edevlink) {\n+\t\tdevice_link_del(phydev-\u003edevlink);\n+\t\tphydev-\u003edevlink = NULL;\n+\t}\n+\n+\tif (phydev-\u003esysfs_links) {\n+\t\tif (dev)\n+\t\t\tsysfs_remove_link(\u0026dev-\u003edev.kobj, \"phydev\");\n+\t\tsysfs_remove_link(\u0026phydev-\u003emdio.dev.kobj, \"attached_dev\");\n+\t}\n+\n+\tif (!phydev-\u003eattached_dev)\n+\t\tsysfs_remove_file(\u0026phydev-\u003emdio.dev.kobj,\n+\t\t\t\t \u0026dev_attr_phy_standalone.attr);\n+\n+\tphy_suspend(phydev);\n+\n+\tif (notify_bus \u0026\u0026 phydev-\u003emdio.bus-\u003enotify_phy_detach)\n+\t\tphydev-\u003emdio.bus-\u003enotify_phy_detach(phydev);\n+\n+\tif (dev) {\n+\t\tstruct hwtstamp_provider *hwprov;\n+\n+\t\t/* hwprov may technically be protected by ops lock but\n+\t\t * not for devices with a phydev, see phy_link_topo_add_phy()\n+\t\t */\n+\t\thwprov = rtnl_dereference(dev-\u003ehwprov);\n+\t\t/* Disable timestamp if it is the one selected */\n+\t\tif (hwprov \u0026\u0026 hwprov-\u003ephydev == phydev) {\n+\t\t\trcu_assign_pointer(dev-\u003ehwprov, NULL);\n+\t\t\tkfree_rcu(hwprov, rcu_head);\n+\t\t}\n+\n+\t\tphydev-\u003eattached_dev-\u003ephydev = NULL;\n+\t\tphydev-\u003eattached_dev = NULL;\n+\t\tphy_link_topo_del_phy(dev, phydev);\n+\t}\n+\n+\tphydev-\u003ephy_link_change = NULL;\n+\tphydev-\u003ephylink = NULL;\n+\n+\tif (phydev-\u003emdio.dev.driver)\n+\t\tmodule_put(phydev-\u003emdio.dev.driver-\u003eowner);\n+\n+\t/* If the device had no specific driver before (i.e. - it\n+\t * was using the generic driver), we unbind the device\n+\t * from the generic driver so that there's a chance a\n+\t * real driver could be loaded\n+\t */\n+\tif (phydev-\u003eis_genphy_driven) {\n+\t\tdevice_release_driver(\u0026phydev-\u003emdio.dev);\n+\t\tphydev-\u003eis_genphy_driven = 0;\n+\t}\n+\n+\t/* Assert the reset signal */\n+\tphy_device_reset(phydev, 1);\n+\n+\t/*\n+\t * The phydev might go away on the put_device() below, so avoid\n+\t * a use-after-free bug by reading the underlying bus first.\n+\t */\n+\tbus = phydev-\u003emdio.bus;\n+\n+\tput_device(\u0026phydev-\u003emdio.dev);\n+\tif (dev)\n+\t\tndev_owner = dev-\u003edev.parent-\u003edriver-\u003eowner;\n+\tif (ndev_owner != bus-\u003eowner)\n+\t\tmodule_put(bus-\u003eowner);\n+}\n+\n+/**\n+ * phy_detach - detach a PHY device from its network device\n+ * @phydev: target phy_device struct\n+ *\n+ * This detaches the phy device from its network device and the phy\n+ * driver, and drops the reference count taken in phy_attach_direct().\n+ */\n+void phy_detach(struct phy_device *phydev)\n+{\n+\t/* cleanup including bus notification */\n+\t__phy_detach(phydev, true);\n+}\n+EXPORT_SYMBOL(phy_detach);\n+\n /**\n * phy_attach_direct - attach a network device to a given PHY device pointer\n * @dev: network device to attach\n@@ -1876,6 +1966,12 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,\n \tif (err)\n \t\tgoto error;\n \n+\tif (phydev-\u003emdio.bus-\u003enotify_phy_attach) {\n+\t\terr = phydev-\u003emdio.bus-\u003enotify_phy_attach(phydev);\n+\t\tif (err)\n+\t\t\tgoto error;\n+\t}\n+\n \tphy_resume(phydev);\n \n \t/**\n@@ -1890,8 +1986,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,\n \treturn err;\n \n error:\n-\t/* phy_detach() does all of the cleanup below */\n-\tphy_detach(phydev);\n+\t/* cleanup without bus notification */\n+\t__phy_detach(phydev, false);\n \treturn err;\n \n error_module_put:\n@@ -1906,86 +2002,6 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,\n }\n EXPORT_SYMBOL(phy_attach_direct);\n \n-/**\n- * phy_detach - detach a PHY device from its network device\n- * @phydev: target phy_device struct\n- *\n- * This detaches the phy device from its network device and the phy\n- * driver, and drops the reference count taken in phy_attach_direct().\n- */\n-void phy_detach(struct phy_device *phydev)\n-{\n-\tstruct net_device *dev = phydev-\u003eattached_dev;\n-\tstruct module *ndev_owner = NULL;\n-\tstruct mii_bus *bus;\n-\n-\tif (phydev-\u003edevlink) {\n-\t\tdevice_link_del(phydev-\u003edevlink);\n-\t\tphydev-\u003edevlink = NULL;\n-\t}\n-\n-\tif (phydev-\u003esysfs_links) {\n-\t\tif (dev)\n-\t\t\tsysfs_remove_link(\u0026dev-\u003edev.kobj, \"phydev\");\n-\t\tsysfs_remove_link(\u0026phydev-\u003emdio.dev.kobj, \"attached_dev\");\n-\t}\n-\n-\tif (!phydev-\u003eattached_dev)\n-\t\tsysfs_remove_file(\u0026phydev-\u003emdio.dev.kobj,\n-\t\t\t\t \u0026dev_attr_phy_standalone.attr);\n-\n-\tphy_suspend(phydev);\n-\tif (dev) {\n-\t\tstruct hwtstamp_provider *hwprov;\n-\n-\t\t/* hwprov may technically be protected by ops lock but\n-\t\t * not for devices with a phydev, see phy_link_topo_add_phy()\n-\t\t */\n-\t\thwprov = rtnl_dereference(dev-\u003ehwprov);\n-\t\t/* Disable timestamp if it is the one selected */\n-\t\tif (hwprov \u0026\u0026 hwprov-\u003ephydev == phydev) {\n-\t\t\trcu_assign_pointer(dev-\u003ehwprov, NULL);\n-\t\t\tkfree_rcu(hwprov, rcu_head);\n-\t\t}\n-\n-\t\tphydev-\u003eattached_dev-\u003ephydev = NULL;\n-\t\tphydev-\u003eattached_dev = NULL;\n-\t\tphy_link_topo_del_phy(dev, phydev);\n-\t}\n-\n-\tphydev-\u003ephy_link_change = NULL;\n-\tphydev-\u003ephylink = NULL;\n-\n-\tif (phydev-\u003emdio.dev.driver)\n-\t\tmodule_put(phydev-\u003emdio.dev.driver-\u003eowner);\n-\n-\t/* If the device had no specific driver before (i.e. - it\n-\t * was using the generic driver), we unbind the device\n-\t * from the generic driver so that there's a chance a\n-\t * real driver could be loaded\n-\t */\n-\tif (phydev-\u003eis_genphy_driven) {\n-\t\tdevice_release_driver(\u0026phydev-\u003emdio.dev);\n-\t\tphydev-\u003eis_genphy_driven = 0;\n-\t}\n-\n-\t/* Assert the reset signal */\n-\tphy_device_reset(phydev, 1);\n-\n-\t/*\n-\t * The phydev might go away on the put_device() below, so avoid\n-\t * a use-after-free bug by reading the underlying bus first.\n-\t */\n-\tbus = phydev-\u003emdio.bus;\n-\n-\tput_device(\u0026phydev-\u003emdio.dev);\n-\tif (dev)\n-\t\tndev_owner = dev-\u003edev.parent-\u003edriver-\u003eowner;\n-\tif (ndev_owner != bus-\u003eowner)\n-\t\tmodule_put(bus-\u003eowner);\n-}\n-EXPORT_SYMBOL(phy_detach);\n-\n int phy_suspend(struct phy_device *phydev)\n {\n \tstruct net_device *netdev = phydev-\u003eattached_dev;\ndiff --git a/include/linux/phy.h b/include/linux/phy.h\nindex 11092c3175b3d..fef0c6a3e27ff 100644\n--- a/include/linux/phy.h\n+++ b/include/linux/phy.h\n@@ -376,6 +376,24 @@ struct mii_bus {\n \t\t\t int regnum, u16 val);\n \t/** @reset: Perform a reset of the bus */\n \tint (*reset)(struct mii_bus *bus);\n+\t/**\n+\t * @notify_phy_attach: Perform post-attach handling for MDIO bus\n+\t * drivers. Optional and independent of @notify_phy_detach. Called\n+\t * in phy_attach_direct() right before phy_resume(). Runs in process\n+\t * context, may sleep and may be called with RTNL held. Must not\n+\t * acquire or rely on RTNL. Returns 0 on success or negative errno\n+\t * on failure. Must unwind its own state on error as attachment is\n+\t * aborted.\n+\t */\n+\tint (*notify_phy_attach)(struct phy_device *phydev);\n+\t/**\n+\t * @notify_phy_detach: Perform pre-detach handling for MDIO bus\n+\t * drivers. Optional and independent of @notify_phy_attach. Called\n+\t * in phy_detach() right after phy_suspend(). Runs in process context,\n+\t * may sleep and may be called with RTNL held. Must not acquire or\n+\t * rely on RTNL.\n+\t */\n+\tvoid (*notify_phy_detach)(struct phy_device *phydev);\n \n \t/** @stats: Statistic counters per device on the bus */\n \tstruct mdio_bus_stats stats[PHY_MAX_ADDR];\n"
} |
| 2/1 | 2026/07/31 01:13 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"PHYLIB",
"USB_NET_SMSC95XX",
"MDIO_REALTEK_RTL9300"
],
"FocusSymbols": [
"phy_attach_direct",
"phy_detach"
],
"Reasoning": "The patch adds notify_phy_attach and notify_phy_detach callbacks to the core PHY subsystem and implements them in the Realtek RTL9300 MDIO driver. The core PHY changes in phy_attach_direct and phy_detach are reachable via virtual/emulated network devices (e.g., USB SMSC95xx) and should be fuzzed to ensure the new error handling and callback logic is robust.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a '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: For your convenience, here is the diff of the changes:
commit 8bb0ff0134436b490bf30e5dacc2f52487707302
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Jul 31 01:13:36 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml b/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml
index 271e05bae9c57..67e0b23a8470e 100644
--- a/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml
+++ b/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/net/realtek,rtl9301-mdio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Realtek RTL9300 MDIO Controller
+title: Realtek Otto Switches Ethernet MDIO Controller
maintainers:
- Chris Packham <chris.packham@alliedtelesis.co.nz>
@@ -12,6 +12,16 @@ maintainers:
properties:
compatible:
oneOf:
+ - items:
+ - enum:
+ - realtek,rtl8381-mdio
+ - realtek,rtl8382-mdio
+ - const: realtek,rtl8380-mdio
+ - items:
+ - enum:
+ - realtek,rtl8392-mdio
+ - realtek,rtl8393-mdio
+ - const: realtek,rtl8391-mdio
- items:
- enum:
- realtek,rtl9302b-mdio
@@ -24,6 +34,8 @@ properties:
- realtek,rtl9313-mdio
- const: realtek,rtl9311-mdio
- enum:
+ - realtek,rtl8380-mdio
+ - realtek,rtl8391-mdio
- realtek,rtl9301-mdio
- realtek,rtl9311-mdio
diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c
index afd52a1cd7f83..d9d4b704afb3d 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -35,12 +35,88 @@
*
* The driver works out the mapping based on the MDIO bus described in device tree and phandles on
* the ethernet-ports property.
+ *
+ * The devices have a hardware polling unit that runs in the background without any CPU load. It
+ * constantly scans the MDIO bus and the attached PHYs and updates the MAC status registers.
+ *
+ * How does the polling work?
+ *
+ * Each device has a SMI_POLL_CTRL register. A per-port bitmask decides if the hardware polling of
+ * the associated bus/address is active or not. The hardware runs a tight loop over this and for
+ * each set polling bit it issues a status check for the PHY. Attaching a logic analyzer to the
+ * MDIO bus of an RTL8380 and RTL8393 gives the following commands (in kernel notation):
+ *
+ * RTL8380 RTL8393
+ * --------------------------- ---------------------------
+ * phy_write(phy, 31, 0x0); phy_read(phy, 0);
+ * phy_write(phy, 13, 0x7); phy_read(phy, 1);
+ * phy_write(phy, 14, 0x3c); phy_read(phy, 4);
+ * phy_write(phy, 13, 0x8007); phy_read(phy, 5);
+ * phy_read(phy, 14); phy_read(phy, 6);
+ * phy_write(phy, 13, 0x7); phy_read(phy, 9);
+ * phy_write(phy, 14, 0x3d); phy_read(phy, 10);
+ * phy_write(phy, 13, 0x8007); phy_read(phy, 15);
+ * phy_read(phy, 14); phy_write(phy, 13, 0x7);
+ * phy_read(phy, 9); phy_write(phy, 14, 0x3c);
+ * phy_read(phy, 10); phy_write(phy, 13, 0x4007);
+ * phy_read(phy, 15); phy_read(phy, 14);
+ * phy_read(phy, 0); phy_write(phy, 13, 0x7);
+ * phy_read(phy, 1); phy_write(phy, 14, 0x3d);
+ * phy_read(phy, 4); phy_write(phy, 13, 0x4007);
+ * phy_read(phy, 5); phy_read(phy, 14);
+ * phy_read(phy, 6);
+ *
+ * After one PHY status is read, the polling engine goes over to the next PHY. If one bus is fully
+ * scanned it switches over to the next bus. Basically the polling system is always busy and the
+ * MAC state is updated in real-time.
+ *
+ * This is a glimpse look at the complexity of the polling and leaves out the C45 case and the MAC
+ * register update logic afterwards. Important to note:
+ *
+ * - 100 MBit downshifts (broken cables) are identified and propagated to the MAC correctly
+ * - Access to MDIO_AN_EEE_ADV and MDIO_AN_EEE_LPABLE works via C45 over C22.
+ * - It is unclear if these sequences change for different PHYs.
+ * - Access to Realtek reserved register 26 (link speed) has not yet been seen.
+ *
+ * How does MDIO access from kernel work?
+ *
+ * When issuing MDIO accesses via an MMIO based interface the final write to the command register
+ * sets a "run command now" bit. Between two polling sequences for different PHYs the hardware
+ * checks if a user command needs to run and sends it onto the bus. Afterwards it simply continues
+ * its polling work. Inspecting the command sequence for a paged write on the logic analyzer gives:
+ *
+ * RTL8380 RTL8393
+ * --------------------------- ---------------------------
+ * phy_write(phy, 31, page); phy_write(phy, 31, page);
+ * phy_write(phy, reg, value); phy_write(phy, reg, value);
+ * phy_write(phy, 31, 0);
+ *
+ * What does this mean?
+ *
+ * There are slight differences in polling and PHY access between the models but the challenge
+ * stays the same. On the one hand that greatly simplifies the MAC layer, on the other hand it
+ * has some implications for the kernel PHY subsystem.
+ *
+ * - Without the polling and a proper MAC status, some of the link handling features do not work.
+ * Especially an unpopulated MAC_LINK_STS register cancels operations to other MAC registers.
+ * - The Realtek page register 31 is magically modified in the background so that polling will
+ * read the right data. On the RTL838x polling simply resets it to zero. Other devices seem
+ * to track the page access "magically" in the background.
+ * - A C45 over C22 kernel access sequence is most likely to fail because chances are high that
+ * the polling engine overwrites registers 13/14 in between.
+ * - PHY firmware loading can have issues. Especially if a PHY is designed to expect a clean
+ * sequence of registers and values without deviation.
+ * - An access to one PHY will need to wait for the next free slot of the polling engine.
+ *
+ * Conclusion: The Realtek MDIO bus driver PHY access must know and handle any interference that
+ * arises from the above described hardware polling.
*/
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/bits.h>
#include <linux/find.h>
+#include <linux/kref.h>
#include <linux/mdio.h>
#include <linux/mfd/syscon.h>
#include <linux/mutex.h>
@@ -50,6 +126,50 @@
#include <linux/property.h>
#include <linux/regmap.h>
+#define RTL8380_NUM_BUSES 1
+#define RTL8380_NUM_PAGES 4096
+#define RTL8380_NUM_PORTS 28
+#define RTL8380_SMI_GLB_CTRL 0xa100
+#define RTL8380_SMI_PHY_PATCH_DONE BIT(15)
+#define RTL8380_SMI_ACCESS_PHY_CTRL_0 0xa1b8
+#define RTL8380_SMI_ACCESS_PHY_CTRL_1 0xa1bc
+#define RTL8380_PHY_CTRL_REG_ADDR GENMASK(24, 20)
+#define RTL8380_PHY_CTRL_PARK_PAGE GENMASK(19, 15)
+#define RTL8380_PHY_CTRL_MAIN_PAGE GENMASK(14, 3)
+#define RTL8380_PHY_CTRL_WRITE BIT(2)
+#define RTL8380_PHY_CTRL_READ 0
+#define RTL8380_PHY_CTRL_TYPE_C45 BIT(1)
+#define RTL8380_PHY_CTRL_TYPE_C22 0
+#define RTL8380_PHY_CTRL_FAIL 0 /* no fail indicator */
+#define RTL8380_SMI_ACCESS_PHY_CTRL_2 0xa1c0
+#define RTL8380_PHY_CTRL_INDATA GENMASK(31, 16)
+#define RTL8380_PHY_CTRL_DATA GENMASK(15, 0)
+#define RTL8380_SMI_ACCESS_PHY_CTRL_3 0xa1c4
+#define RTL8380_SMI_POLL_CTRL 0xa17c
+#define RTL8380_SMI_PORT0_5_ADDR_CTRL 0xa1c8
+
+#define RTL8390_NUM_BUSES 2
+#define RTL8390_NUM_PAGES 8192
+#define RTL8390_NUM_PORTS 52
+#define RTL8390_BCAST_PHYID_CTRL 0x03ec
+#define RTL8390_PHYREG_ACCESS_CTRL 0x03dc
+#define RTL8390_PHY_CTRL_REG_ADDR GENMASK(9, 5)
+#define RTL8390_PHY_CTRL_MAIN_PAGE GENMASK(22, 10)
+#define RTL8390_PHY_CTRL_FAIL BIT(1)
+#define RTL8390_PHY_CTRL_WRITE BIT(3)
+#define RTL8390_PHY_CTRL_READ 0
+#define RTL8390_PHY_CTRL_TYPE_C45 BIT(2)
+#define RTL8390_PHY_CTRL_TYPE_C22 0
+#define RTL8390_PHYREG_CTRL 0x03e0
+#define RTL8390_PHY_CTRL_EXT_PAGE GENMASK(8, 0)
+#define RTL8390_PHYREG_DATA_CTRL 0x03f0
+#define RTL8390_PHY_CTRL_INDATA GENMASK(31, 16)
+#define RTL8390_PHY_CTRL_DATA GENMASK(15, 0)
+#define RTL8390_PHYREG_MMD_CTRL 0x03f4
+#define RTL8390_PHYREG_PORT_CTRL_LOW 0x03e4
+#define RTL8390_PHYREG_PORT_CTRL_HIGH 0x03e8
+#define RTL8390_SMI_PORT_POLLING_CTRL 0x03fc
+
#define RTL9300_NUM_BUSES 4
#define RTL9300_NUM_PAGES 4096
#define RTL9300_NUM_PORTS 28
@@ -70,6 +190,7 @@
#define RTL9300_PHY_CTRL_INDATA GENMASK(31, 16)
#define RTL9300_PHY_CTRL_DATA GENMASK(15, 0)
#define RTL9300_SMI_ACCESS_PHY_CTRL_3 0xcb7c
+#define RTL9300_SMI_POLL_CTRL 0xca90
#define RTL9300_SMI_PORT0_5_ADDR_CTRL 0xcb80
#define RTL9310_NUM_BUSES 4
@@ -95,12 +216,16 @@
#define RTL9310_PHY_CTRL_INDATA GENMASK(15, 0)
#define RTL9310_SMI_INDRT_ACCESS_MMD_CTRL 0x0c18
#define RTL9310_SMI_PORT_ADDR_CTRL 0x0c74
+#define RTL9310_SMI_PORT_POLLING_CTRL 0x0ccc
#define RTL9310_SMI_PORT_POLLING_SEL 0x0c9c
#define PHY_CTRL_CMD BIT(0)
#define PHY_CTRL_MMD_DEVAD GENMASK(20, 16)
#define PHY_CTRL_MMD_REG GENMASK(15, 0)
+#define RTL_VENDOR_ID 0x001cc800
+#define RTL_PAGE_SELECT 31
+
#define MAP_ADDRS_PER_REG 6
#define MAP_BITS_PER_ADDR 5
#define MAP_BITS_PER_BUS 2
@@ -123,9 +248,11 @@ struct otto_emdio_cmd_regs {
struct otto_emdio_priv {
const struct otto_emdio_info *info;
+ struct kref kref;
struct regmap *regmap;
struct mutex lock; /* protect HW access */
DECLARE_BITMAP(valid_ports, MAX_PORTS);
+ u16 page[MAX_PORTS];
u8 smi_bus[MAX_PORTS];
u8 smi_addr[MAX_PORTS];
bool smi_bus_is_c45[MAX_SMI_BUSSES];
@@ -142,6 +269,7 @@ struct otto_emdio_info {
u8 num_buses;
u8 num_ports;
u16 num_pages;
+ u32 poll_ctrl;
int (*setup_controller)(struct otto_emdio_priv *priv);
int (*read_c22)(struct mii_bus *bus, int port, int regnum, u32 *value);
int (*read_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u32 *value);
@@ -177,6 +305,12 @@ static struct otto_emdio_priv *otto_emdio_bus_to_priv(struct mii_bus *bus)
return chan->priv;
}
+static int otto_emdio_set_port_polling(struct otto_emdio_priv *priv, int port, bool active)
+{
+ return regmap_assign_bits(priv->regmap, priv->info->poll_ctrl + (port / 32) * 4,
+ BIT(port % 32), active);
+}
+
static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd,
struct otto_emdio_cmd_regs *cmd_data)
{
@@ -185,9 +319,9 @@ static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd,
u32 cmdstate;
int ret;
- /* Defensive pre check just in case something goes horrible wrong */
+ /* Defensive pre check just in case something goes horribly wrong */
ret = regmap_read_poll_timeout(priv->regmap, info->cmd_regs.c22_data,
- cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 1000);
+ cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 10000);
if (ret)
return ret;
@@ -227,7 +361,7 @@ static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd,
return ret;
ret = regmap_read_poll_timeout(priv->regmap, info->cmd_regs.c22_data,
- cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 1000);
+ cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 10000);
if (ret)
return ret;
@@ -264,13 +398,123 @@ static int otto_emdio_write_cmd(struct mii_bus *bus, u32 cmd,
return otto_emdio_run_cmd(bus, cmd | priv->info->cmd_write, cmd_data);
}
+static int otto_emdio_8380_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+ struct otto_emdio_cmd_regs cmd_data = {
+ .c22_data = FIELD_PREP(RTL8380_PHY_CTRL_REG_ADDR, regnum) |
+ FIELD_PREP(RTL8380_PHY_CTRL_PARK_PAGE, 0x1f) |
+ FIELD_PREP(RTL8380_PHY_CTRL_MAIN_PAGE, priv->page[port]),
+ .io_data = FIELD_PREP(RTL8380_PHY_CTRL_INDATA, port),
+ };
+
+ return otto_emdio_read_cmd(bus, RTL8380_PHY_CTRL_TYPE_C22, &cmd_data,
+ RTL8380_PHY_CTRL_DATA, value);
+}
+
+static int otto_emdio_8380_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+ struct otto_emdio_cmd_regs cmd_data = {
+ .c22_data = FIELD_PREP(RTL8380_PHY_CTRL_REG_ADDR, regnum) |
+ FIELD_PREP(RTL8380_PHY_CTRL_PARK_PAGE, 0x1f) |
+ FIELD_PREP(RTL8380_PHY_CTRL_MAIN_PAGE, priv->page[port]),
+ .io_data = FIELD_PREP(RTL8380_PHY_CTRL_INDATA, value),
+ .port_mask_low = BIT(port),
+ };
+
+ return otto_emdio_write_cmd(bus, RTL8380_PHY_CTRL_TYPE_C22, &cmd_data);
+}
+
+static int otto_emdio_8380_read_c45(struct mii_bus *bus, int port,
+ int dev_addr, int regnum, u32 *value)
+{
+ struct otto_emdio_cmd_regs cmd_data = {
+ .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+ FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
+ .io_data = FIELD_PREP(RTL8380_PHY_CTRL_INDATA, port),
+ };
+
+ return otto_emdio_read_cmd(bus, RTL8380_PHY_CTRL_TYPE_C45, &cmd_data,
+ RTL8380_PHY_CTRL_DATA, value);
+}
+
+static int otto_emdio_8380_write_c45(struct mii_bus *bus, int port,
+ int dev_addr, int regnum, u16 value)
+{
+ struct otto_emdio_cmd_regs cmd_data = {
+ .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+ FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
+ .io_data = FIELD_PREP(RTL8380_PHY_CTRL_INDATA, value),
+ .port_mask_low = BIT(port),
+ };
+
+ return otto_emdio_write_cmd(bus, RTL8380_PHY_CTRL_TYPE_C45, &cmd_data);
+}
+
+static int otto_emdio_8390_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+ struct otto_emdio_cmd_regs cmd_data = {
+ .c22_data = FIELD_PREP(RTL8390_PHY_CTRL_REG_ADDR, regnum) |
+ FIELD_PREP(RTL8390_PHY_CTRL_MAIN_PAGE, priv->page[port]),
+ .ext_page = FIELD_PREP(RTL8390_PHY_CTRL_EXT_PAGE, 0x1ff),
+ .io_data = FIELD_PREP(RTL8390_PHY_CTRL_INDATA, port),
+ };
+
+ return otto_emdio_read_cmd(bus, RTL8390_PHY_CTRL_TYPE_C22, &cmd_data,
+ RTL8390_PHY_CTRL_DATA, value);
+}
+
+static int otto_emdio_8390_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
+ struct otto_emdio_cmd_regs cmd_data = {
+ .c22_data = FIELD_PREP(RTL8390_PHY_CTRL_REG_ADDR, regnum) |
+ FIELD_PREP(RTL8390_PHY_CTRL_MAIN_PAGE, priv->page[port]),
+ .ext_page = FIELD_PREP(RTL8390_PHY_CTRL_EXT_PAGE, 0x1ff),
+ .io_data = FIELD_PREP(RTL8390_PHY_CTRL_INDATA, value),
+ .port_mask_high = (u32)(BIT_ULL(port) >> 32),
+ .port_mask_low = (u32)(BIT_ULL(port)),
+ };
+
+ return otto_emdio_write_cmd(bus, RTL8390_PHY_CTRL_TYPE_C22, &cmd_data);
+}
+
+static int otto_emdio_8390_read_c45(struct mii_bus *bus, int port,
+ int dev_addr, int regnum, u32 *value)
+{
+ struct otto_emdio_cmd_regs cmd_data = {
+ .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+ FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
+ .io_data = FIELD_PREP(RTL8390_PHY_CTRL_INDATA, port),
+ };
+
+ return otto_emdio_read_cmd(bus, RTL8390_PHY_CTRL_TYPE_C45, &cmd_data,
+ RTL8390_PHY_CTRL_DATA, value);
+}
+
+static int otto_emdio_8390_write_c45(struct mii_bus *bus, int port,
+ int dev_addr, int regnum, u16 value)
+{
+ struct otto_emdio_cmd_regs cmd_data = {
+ .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |
+ FIELD_PREP(PHY_CTRL_MMD_REG, regnum),
+ .io_data = FIELD_PREP(RTL8390_PHY_CTRL_INDATA, value),
+ .port_mask_high = (u32)(BIT_ULL(port) >> 32),
+ .port_mask_low = (u32)(BIT_ULL(port)),
+ };
+
+ return otto_emdio_write_cmd(bus, RTL8390_PHY_CTRL_TYPE_C45, &cmd_data);
+}
+
static int otto_emdio_9300_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)
{
struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
struct otto_emdio_cmd_regs cmd_data = {
.c22_data = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) |
FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) |
- FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
+ FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, priv->page[port]),
.io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port),
};
@@ -284,7 +528,7 @@ static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum,
struct otto_emdio_cmd_regs cmd_data = {
.c22_data = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) |
FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) |
- FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
+ FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, priv->page[port]),
.io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, value),
.port_mask_low = BIT(port),
};
@@ -324,7 +568,7 @@ static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u
struct otto_emdio_cmd_regs cmd_data = {
.broadcast = FIELD_PREP(RTL9310_BC_PORT_ID, port),
.c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |
- FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
+ FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, priv->page[port]),
};
return otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, &cmd_data,
@@ -336,7 +580,7 @@ static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum,
struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
struct otto_emdio_cmd_regs cmd_data = {
.c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |
- FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)),
+ FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, priv->page[port]),
.io_data = FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value),
.port_mask_high = (u32)(BIT_ULL(port) >> 32),
.port_mask_low = (u32)(BIT_ULL(port)),
@@ -382,8 +626,12 @@ static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
if (port < 0)
return port;
- scoped_guard(mutex, &priv->lock)
+ scoped_guard(mutex, &priv->lock) {
+ if (regnum == RTL_PAGE_SELECT)
+ return priv->page[port];
+
ret = priv->info->read_c22(bus, port, regnum, &value);
+ }
return ret ? ret : value;
}
@@ -397,8 +645,17 @@ static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16
if (port < 0)
return port;
- scoped_guard(mutex, &priv->lock)
+ scoped_guard(mutex, &priv->lock) {
+ if (regnum == RTL_PAGE_SELECT) {
+ if (value >= RAW_PAGE(priv))
+ return -EINVAL;
+
+ priv->page[port] = value;
+ return 0;
+ }
+
ret = priv->info->write_c22(bus, port, regnum, value);
+ }
return ret;
}
@@ -471,6 +728,15 @@ static int otto_emdio_setup_topology(struct otto_emdio_priv *priv)
return 0;
}
+static int otto_emdio_8380_setup_controller(struct otto_emdio_priv *priv)
+{
+ /*
+ * PHY_PATCH_DONE enables PHY control via SoC. This is required for PHY access, including
+ * patching and must be set before the PHYs are probed.
+ */
+ return regmap_set_bits(priv->regmap, RTL8380_SMI_GLB_CTRL, RTL8380_SMI_PHY_PATCH_DONE);
+}
+
static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv)
{
u32 glb_ctrl_mask = 0, glb_ctrl_val = 0;
@@ -507,6 +773,59 @@ static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)
return 0;
}
+static int otto_emdio_notify_phy_attach(struct phy_device *phydev)
+{
+ int port = otto_emdio_phy_to_port(phydev->mdio.bus, phydev->mdio.addr);
+ struct otto_emdio_chan *chan = phydev->mdio.bus->priv;
+ struct otto_emdio_priv *priv = chan->priv;
+
+ if (port < 0)
+ return port;
+
+ if (!priv->smi_bus_is_c45[chan->mdio_bus] &&
+ !phy_id_compare_vendor(phydev->phy_id, RTL_VENDOR_ID)) {
+ phydev_err(phydev, "Only Realtek PHYs allowed on C22 bus\n");
+ return -EOPNOTSUPP;
+ }
+
+ return otto_emdio_set_port_polling(priv, port, true);
+}
+
+static void otto_emdio_notify_phy_detach(struct phy_device *phydev)
+{
+ struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(phydev->mdio.bus);
+ int port = otto_emdio_phy_to_port(phydev->mdio.bus, phydev->mdio.addr);
+ struct otto_emdio_chan *chan = phydev->mdio.bus->priv;
+ struct mii_bus *bus = phydev->mdio.bus;
+ int ret;
+
+ if (port < 0)
+ return;
+
+ /* avoid wrong cached page for next attachment */
+ if (!priv->smi_bus_is_c45[chan->mdio_bus])
+ priv->page[port] = 0;
+
+ ret = otto_emdio_set_port_polling(priv, port, false);
+ if (ret)
+ dev_err(bus->parent, "failed to disable polling for port %d\n", port);
+}
+
+static void otto_emdio_cleanup_controller(struct kref *kref)
+{
+ struct otto_emdio_priv *priv = container_of(kref, struct otto_emdio_priv, kref);
+
+ mutex_destroy(&priv->lock);
+ kfree(priv);
+}
+
+static void otto_emdio_cleanup_bus(void *data)
+{
+ struct otto_emdio_priv *priv = data;
+
+ kref_put(&priv->kref, otto_emdio_cleanup_controller);
+}
+
static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,
struct fwnode_handle *node)
{
@@ -536,12 +855,20 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv
bus->write = otto_emdio_write_c22;
}
bus->parent = dev;
+ bus->notify_phy_attach = otto_emdio_notify_phy_attach;
+ bus->notify_phy_detach = otto_emdio_notify_phy_detach;
+
chan = bus->priv;
chan->mdio_bus = mdio_bus;
chan->priv = priv;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", dev_name(dev), mdio_bus);
+ kref_get(&priv->kref);
+ err = devm_add_action_or_reset(&bus->dev, otto_emdio_cleanup_bus, priv);
+ if (err)
+ return dev_err_probe(dev, err, "cannot register cleanup action\n");
+
err = devm_of_mdiobus_register(dev, bus, to_of_node(node));
if (err)
return dev_err_probe(dev, err, "cannot register MDIO bus\n");
@@ -652,50 +979,122 @@ static int otto_emdio_map_ports(struct device *dev)
return err;
}
+static int otto_emdio_init_polling(struct otto_emdio_priv *priv)
+{
+ int err;
+
+ for (int port = 0; port < priv->info->num_ports; port++) {
+ err = otto_emdio_set_port_polling(priv, port, false);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int otto_emdio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct otto_emdio_priv *priv;
+ int buses_registered = 0;
int err;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv, GFP_KERNEL);
if (!priv)
return -ENOMEM;
- err = devm_mutex_init(dev, &priv->lock);
- if (err)
- return err;
+ kref_init(&priv->kref);
+ mutex_init(&priv->lock);
priv->info = device_get_match_data(dev);
priv->regmap = syscon_node_to_regmap(dev->parent->of_node);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
+ if (IS_ERR(priv->regmap)) {
+ err = PTR_ERR(priv->regmap);
+ goto err_out;
+ }
+
+ err = otto_emdio_init_polling(priv);
+ if (err)
+ goto err_out;
platform_set_drvdata(pdev, priv);
err = otto_emdio_map_ports(dev);
if (err)
- return err;
+ goto err_out;
err = otto_emdio_setup_topology(priv);
if (err)
- return err;
+ goto err_out;
if (priv->info->setup_controller) {
err = priv->info->setup_controller(priv);
- if (err)
- return dev_err_probe(dev, err, "failed to setup MDIO bus controller\n");
+ if (err) {
+ dev_err_probe(dev, err, "failed to setup MDIO bus controller\n");
+ goto err_out;
+ }
}
device_for_each_child_node_scoped(dev, child) {
err = otto_emdio_probe_one(dev, priv, child);
if (err)
- return err;
+ goto err_out;
+ buses_registered++;
}
- return 0;
+ if (!buses_registered)
+ err = dev_err_probe(dev, -ENODEV, "no MDIO buses registered\n");
+err_out:
+ kref_put(&priv->kref, otto_emdio_cleanup_controller);
+
+ return err;
}
+static const struct otto_emdio_info otto_emdio_8380_info = {
+ .addr_map_base = RTL8380_SMI_PORT0_5_ADDR_CTRL,
+ .cmd_fail = RTL8380_PHY_CTRL_FAIL,
+ .cmd_read = RTL8380_PHY_CTRL_READ,
+ .cmd_write = RTL8380_PHY_CTRL_WRITE,
+ .cmd_regs = {
+ .c22_data = RTL8380_SMI_ACCESS_PHY_CTRL_1,
+ .c45_data = RTL8380_SMI_ACCESS_PHY_CTRL_3,
+ .io_data = RTL8380_SMI_ACCESS_PHY_CTRL_2,
+ .port_mask_low = RTL8380_SMI_ACCESS_PHY_CTRL_0,
+ },
+ .num_buses = RTL8380_NUM_BUSES,
+ .num_pages = RTL8380_NUM_PAGES,
+ .num_ports = RTL8380_NUM_PORTS,
+ .poll_ctrl = RTL8380_SMI_POLL_CTRL,
+ .setup_controller = otto_emdio_8380_setup_controller,
+ .read_c22 = otto_emdio_8380_read_c22,
+ .read_c45 = otto_emdio_8380_read_c45,
+ .write_c22 = otto_emdio_8380_write_c22,
+ .write_c45 = otto_emdio_8380_write_c45,
+};
+
+static const struct otto_emdio_info otto_emdio_8390_info = {
+ .cmd_fail = RTL8390_PHY_CTRL_FAIL,
+ .cmd_read = RTL8390_PHY_CTRL_READ,
+ .cmd_write = RTL8390_PHY_CTRL_WRITE,
+ .cmd_regs = {
+ .broadcast = RTL8390_BCAST_PHYID_CTRL,
+ .c22_data = RTL8390_PHYREG_ACCESS_CTRL,
+ .c45_data = RTL8390_PHYREG_MMD_CTRL,
+ .ext_page = RTL8390_PHYREG_CTRL,
+ .io_data = RTL8390_PHYREG_DATA_CTRL,
+ .port_mask_low = RTL8390_PHYREG_PORT_CTRL_LOW,
+ .port_mask_high = RTL8390_PHYREG_PORT_CTRL_HIGH,
+ },
+ .num_buses = RTL8390_NUM_BUSES,
+ .num_pages = RTL8390_NUM_PAGES,
+ .num_ports = RTL8390_NUM_PORTS,
+ .poll_ctrl = RTL8390_SMI_PORT_POLLING_CTRL,
+ .read_c22 = otto_emdio_8390_read_c22,
+ .read_c45 = otto_emdio_8390_read_c45,
+ .write_c22 = otto_emdio_8390_write_c22,
+ .write_c45 = otto_emdio_8390_write_c45,
+};
+
static const struct otto_emdio_info otto_emdio_9300_info = {
.addr_map_base = RTL9300_SMI_PORT0_5_ADDR_CTRL,
.bus_map_base = RTL9300_SMI_PORT0_15_POLLING_SEL,
@@ -711,6 +1110,7 @@ static const struct otto_emdio_info otto_emdio_9300_info = {
.num_buses = RTL9300_NUM_BUSES,
.num_ports = RTL9300_NUM_PORTS,
.num_pages = RTL9300_NUM_PAGES,
+ .poll_ctrl = RTL9300_SMI_POLL_CTRL,
.setup_controller = otto_emdio_9300_setup_controller,
.read_c22 = otto_emdio_9300_read_c22,
.read_c45 = otto_emdio_9300_read_c45,
@@ -736,6 +1136,7 @@ static const struct otto_emdio_info otto_emdio_9310_info = {
.num_buses = RTL9310_NUM_BUSES,
.num_pages = RTL9310_NUM_PAGES,
.num_ports = RTL9310_NUM_PORTS,
+ .poll_ctrl = RTL9310_SMI_PORT_POLLING_CTRL,
.setup_controller = otto_emdio_9310_setup_controller,
.read_c22 = otto_emdio_9310_read_c22,
.read_c45 = otto_emdio_9310_read_c45,
@@ -744,6 +1145,8 @@ static const struct otto_emdio_info otto_emdio_9310_info = {
};
static const struct of_device_id otto_emdio_ids[] = {
+ { .compatible = "realtek,rtl8380-mdio", .data = &otto_emdio_8380_info },
+ { .compatible = "realtek,rtl8391-mdio", .data = &otto_emdio_8390_info },
{ .compatible = "realtek,rtl9301-mdio", .data = &otto_emdio_9300_info },
{ .compatible = "realtek,rtl9311-mdio", .data = &otto_emdio_9310_info },
{}
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 94b2e85e00a37..233622e9ba61f 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1734,6 +1734,96 @@ static bool phy_drv_supports_irq(const struct phy_driver *phydrv)
return phydrv->config_intr && phydrv->handle_interrupt;
}
+static void __phy_detach(struct phy_device *phydev, bool notify_bus)
+{
+ struct net_device *dev = phydev->attached_dev;
+ struct module *ndev_owner = NULL;
+ struct mii_bus *bus;
+
+ if (phydev->devlink) {
+ device_link_del(phydev->devlink);
+ phydev->devlink = NULL;
+ }
+
+ if (phydev->sysfs_links) {
+ if (dev)
+ sysfs_remove_link(&dev->dev.kobj, "phydev");
+ sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
+ }
+
+ if (!phydev->attached_dev)
+ sysfs_remove_file(&phydev->mdio.dev.kobj,
+ &dev_attr_phy_standalone.attr);
+
+ phy_suspend(phydev);
+
+ if (notify_bus && phydev->mdio.bus->notify_phy_detach)
+ phydev->mdio.bus->notify_phy_detach(phydev);
+
+ if (dev) {
+ struct hwtstamp_provider *hwprov;
+
+ /* hwprov may technically be protected by ops lock but
+ * not for devices with a phydev, see phy_link_topo_add_phy()
+ */
+ hwprov = rtnl_dereference(dev->hwprov);
+ /* Disable timestamp if it is the one selected */
+ if (hwprov && hwprov->phydev == phydev) {
+ rcu_assign_pointer(dev->hwprov, NULL);
+ kfree_rcu(hwprov, rcu_head);
+ }
+
+ phydev->attached_dev->phydev = NULL;
+ phydev->attached_dev = NULL;
+ phy_link_topo_del_phy(dev, phydev);
+ }
+
+ phydev->phy_link_change = NULL;
+ phydev->phylink = NULL;
+
+ if (phydev->mdio.dev.driver)
+ module_put(phydev->mdio.dev.driver->owner);
+
+ /* If the device had no specific driver before (i.e. - it
+ * was using the generic driver), we unbind the device
+ * from the generic driver so that there's a chance a
+ * real driver could be loaded
+ */
+ if (phydev->is_genphy_driven) {
+ device_release_driver(&phydev->mdio.dev);
+ phydev->is_genphy_driven = 0;
+ }
+
+ /* Assert the reset signal */
+ phy_device_reset(phydev, 1);
+
+ /*
+ * The phydev might go away on the put_device() below, so avoid
+ * a use-after-free bug by reading the underlying bus first.
+ */
+ bus = phydev->mdio.bus;
+
+ put_device(&phydev->mdio.dev);
+ if (dev)
+ ndev_owner = dev->dev.parent->driver->owner;
+ if (ndev_owner != bus->owner)
+ module_put(bus->owner);
+}
+
+/**
+ * phy_detach - detach a PHY device from its network device
+ * @phydev: target phy_device struct
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
+ */
+void phy_detach(struct phy_device *phydev)
+{
+ /* cleanup including bus notification */
+ __phy_detach(phydev, true);
+}
+EXPORT_SYMBOL(phy_detach);
+
/**
* phy_attach_direct - attach a network device to a given PHY device pointer
* @dev: network device to attach
@@ -1876,6 +1966,12 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
if (err)
goto error;
+ if (phydev->mdio.bus->notify_phy_attach) {
+ err = phydev->mdio.bus->notify_phy_attach(phydev);
+ if (err)
+ goto error;
+ }
+
phy_resume(phydev);
/**
@@ -1890,8 +1986,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
return err;
error:
- /* phy_detach() does all of the cleanup below */
- phy_detach(phydev);
+ /* cleanup without bus notification */
+ __phy_detach(phydev, false);
return err;
error_module_put:
@@ -1906,86 +2002,6 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
}
EXPORT_SYMBOL(phy_attach_direct);
-/**
- * phy_detach - detach a PHY device from its network device
- * @phydev: target phy_device struct
- *
- * This detaches the phy device from its network device and the phy
- * driver, and drops the reference count taken in phy_attach_direct().
- */
-void phy_detach(struct phy_device *phydev)
-{
- struct net_device *dev = phydev->attached_dev;
- struct module *ndev_owner = NULL;
- struct mii_bus *bus;
-
- if (phydev->devlink) {
- device_link_del(phydev->devlink);
- phydev->devlink = NULL;
- }
-
- if (phydev->sysfs_links) {
- if (dev)
- sysfs_remove_link(&dev->dev.kobj, "phydev");
- sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
- }
-
- if (!phydev->attached_dev)
- sysfs_remove_file(&phydev->mdio.dev.kobj,
- &dev_attr_phy_standalone.attr);
-
- phy_suspend(phydev);
- if (dev) {
- struct hwtstamp_provider *hwprov;
-
- /* hwprov may technically be protected by ops lock but
- * not for devices with a phydev, see phy_link_topo_add_phy()
- */
- hwprov = rtnl_dereference(dev->hwprov);
- /* Disable timestamp if it is the one selected */
- if (hwprov && hwprov->phydev == phydev) {
- rcu_assign_pointer(dev->hwprov, NULL);
- kfree_rcu(hwprov, rcu_head);
- }
-
- phydev->attached_dev->phydev = NULL;
- phydev->attached_dev = NULL;
- phy_link_topo_del_phy(dev, phydev);
- }
-
- phydev->phy_link_change = NULL;
- phydev->phylink = NULL;
-
- if (phydev->mdio.dev.driver)
- module_put(phydev->mdio.dev.driver->owner);
-
- /* If the device had no specific driver before (i.e. - it
- * was using the generic driver), we unbind the device
- * from the generic driver so that there's a chance a
- * real driver could be loaded
- */
- if (phydev->is_genphy_driven) {
- device_release_driver(&phydev->mdio.dev);
- phydev->is_genphy_driven = 0;
- }
-
- /* Assert the reset signal */
- phy_device_reset(phydev, 1);
-
- /*
- * The phydev might go away on the put_device() below, so avoid
- * a use-after-free bug by reading the underlying bus first.
- */
- bus = phydev->mdio.bus;
-
- put_device(&phydev->mdio.dev);
- if (dev)
- ndev_owner = dev->dev.parent->driver->owner;
- if (ndev_owner != bus->owner)
- module_put(bus->owner);
-}
-EXPORT_SYMBOL(phy_detach);
-
int phy_suspend(struct phy_device *phydev)
{
struct net_device *netdev = phydev->attached_dev;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 11092c3175b3d..fef0c6a3e27ff 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -376,6 +376,24 @@ struct mii_bus {
int regnum, u16 val);
/** @reset: Perform a reset of the bus */
int (*reset)(struct mii_bus *bus);
+ /**
+ * @notify_phy_attach: Perform post-attach handling for MDIO bus
+ * drivers. Optional and independent of @notify_phy_detach. Called
+ * in phy_attach_direct() right before phy_resume(). Runs in process
+ * context, may sleep and may be called with RTNL held. Must not
+ * acquire or rely on RTNL. Returns 0 on success or negative errno
+ * on failure. Must unwind its own state on error as attachment is
+ * aborted.
+ */
+ int (*notify_phy_attach)(struct phy_device *phydev);
+ /**
+ * @notify_phy_detach: Perform pre-detach handling for MDIO bus
+ * drivers. Optional and independent of @notify_phy_attach. Called
+ * in phy_detach() right after phy_suspend(). Runs in process context,
+ * may sleep and may be called with RTNL held. Must not acquire or
+ * rely on RTNL.
+ */
+ void (*notify_phy_detach)(struct phy_device *phydev);
/** @stats: Statistic counters per device on the bus */
struct mdio_bus_stats stats[PHY_MAX_ADDR];
|
| 3/2 | 2026/07/31 01:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16284 output: 19 thoughts: 604 Thoughts: **Fuzzing Feasibility Analysis: Realtek Otto Switch MDIO Controller Patch**
Okay, so I've got this patch adding support for Realtek Otto Switches, specifically the RTL8380 and RTL8390 families, and it's modifying the PHY core to add `notify_phy_attach` and `notify_phy_detach` callbacks to the `mii_bus` struct. The question is, is this something worth throwing fuzzing at?
Let's break this down. The patch touches `drivers/net/mdio/mdio-realtek-rtl9300.c` and `drivers/net/phy/phy_device.c`. `phy_device.c`, that's core networking code, the PHY subsystem itself. The changes in `phy_device.c` add the callbacks to `mii_bus` and call them during `phy_attach_direct` and `phy_detach`. Immediately, those functions seem reachable in a virtualized environment β think dummy net devices, veth interfaces, any emulated NIC that uses PHYs. And then it hits me: `phy_attach_direct` is called when a network device attaches to a PHY.
Could syzkaller reach that? Absolutely. It can create virtual network devices, using netlink, tun/tap, or dummy, and some of those could very well attach PHYs. Or, it can interact with MDIO buses. The key parts are changes made to `phy_attach_direct` and `phy_detach`. Looking at these functions, the core changes that are made, specifically:
```c
+static void __phy_detach(struct phy_device *phydev, bool notify_bus)
...
+ if (notify_bus && phydev->mdio.bus->notify_phy_detach)
+ phydev->mdio.bus->notify_phy_detach(phydev);
...
```
And in `phy_attach_direct`:
```c
+ if (phydev->mdio.bus->notify_phy_attach) {
+ err = phydev->mdio.bus->notify_phy_attach(phydev);
+ if (err)
+ goto error;
+ }
```
This *is* core attachment/detachment logic, so yes, this is absolutely worth fuzzing. It's a fundamental change to the way PHYs are handled.
What about the MDIO driver (`mdio-realtek-rtl9300.c`)? MDIO drivers are usually platform drivers, meaning they might not be easily reachable without the platform device being instantiated. Still, the core PHY changes are reachable regardless.
So, I'm going to focus on those two functions. Time to look for `phy_attach_direct` and `phy_detach` usages to get a better grasp of the entry points and figure out how syzkaller can best interact with this code.
|
| 4/2 | 2026/07/31 01:13 | tool | grepper |
0mArgs:{
"Expression": "phy_attach_direct"
}Results: {
"Output": "Documentation/networking/phy-link-topology.rst=85=These function are already hooked into the phylib subsystem, so all PHYs that\nDocumentation/networking/phy-link-topology.rst:86:are linked to a net_device through :c:func:`phy_attach_direct` will automatically\nDocumentation/networking/phy-link-topology.rst-87-join the netdev's topology.\n--\nDocumentation/networking/sfp-phylink.rst=74=this documentation.\n--\nDocumentation/networking/sfp-phylink.rst-165- For the most part, ``flags`` can be zero; these flags are passed to\nDocumentation/networking/sfp-phylink.rst:166: the phy_attach_direct() inside this function call if a PHY is specified\nDocumentation/networking/sfp-phylink.rst-167- in the DT node ``node``.\n--\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c=931=static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)\n--\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-982-\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c:983:\tret = phy_attach_direct(pdata-\u003enetdev, phydev, phydev-\u003edev_flags,\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-984-\t\t\t\tPHY_INTERFACE_MODE_SGMII);\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-985-\tif (ret) {\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c:986:\t\tnetdev_err(pdata-\u003enetdev, \"phy_attach_direct failed\\n\");\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-987-\t\tphy_device_remove(phydev);\n--\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c=1159=int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h)\n--\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c-1180-\t} else {\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c:1181:\t\tret = phy_attach_direct(ndev, phy_dev, 0, h-\u003ephy_if);\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c-1182-\t}\n--\ndrivers/net/phy/phy_device.c=1225=int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,\n--\ndrivers/net/phy/phy_device.c-1233-\ndrivers/net/phy/phy_device.c:1234:\trc = phy_attach_direct(dev, phydev, phydev-\u003edev_flags, interface);\ndrivers/net/phy/phy_device.c-1235-\tif (rc)\n--\ndrivers/net/phy/phy_device.c=1737=static void __phy_detach(struct phy_device *phydev, bool notify_bus)\n--\ndrivers/net/phy/phy_device.c-1817- * This detaches the phy device from its network device and the phy\ndrivers/net/phy/phy_device.c:1818: * driver, and drops the reference count taken in phy_attach_direct().\ndrivers/net/phy/phy_device.c-1819- */\n--\ndrivers/net/phy/phy_device.c=1825=EXPORT_SYMBOL(phy_detach);\n--\ndrivers/net/phy/phy_device.c-1827-/**\ndrivers/net/phy/phy_device.c:1828: * phy_attach_direct - attach a network device to a given PHY device pointer\ndrivers/net/phy/phy_device.c-1829- * @dev: network device to attach\n--\ndrivers/net/phy/phy_device.c-1841- */\ndrivers/net/phy/phy_device.c:1842:int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,\ndrivers/net/phy/phy_device.c-1843-\t\t u32 flags, phy_interface_t interface)\n--\ndrivers/net/phy/phy_device.c-2002-}\ndrivers/net/phy/phy_device.c:2003:EXPORT_SYMBOL(phy_attach_direct);\ndrivers/net/phy/phy_device.c-2004-\n--\ndrivers/net/phy/phylink.c=2204=static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,\n--\ndrivers/net/phy/phylink.c-2219-\ndrivers/net/phy/phylink.c:2220:\treturn phy_attach_direct(pl-\u003enetdev, phy, flags, interface);\ndrivers/net/phy/phylink.c-2221-}\n--\ndrivers/net/phy/phylink.c-2228- * Connect @phy to the phylink instance specified by @pl by calling\ndrivers/net/phy/phylink.c:2229: * phy_attach_direct(). Configure the @phy according to the MAC driver's\ndrivers/net/phy/phylink.c-2230- * capabilities, start the PHYLIB state machine and enable any interrupts\n--\ndrivers/net/phy/phylink.c=2290=int phylink_fwnode_phy_connect(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-2323-\ndrivers/net/phy/phylink.c:2324:\tret = phy_attach_direct(pl-\u003enetdev, phy_dev, flags,\ndrivers/net/phy/phylink.c-2325-\t\t\t\tpl-\u003elink_interface);\n--\ninclude/linux/phy.h=363=struct mii_bus {\n--\ninclude/linux/phy.h-381-\t * drivers. Optional and independent of @notify_phy_detach. Called\ninclude/linux/phy.h:382:\t * in phy_attach_direct() right before phy_resume(). Runs in process\ninclude/linux/phy.h-383-\t * context, may sleep and may be called with RTNL held. Must not\n--\ninclude/linux/phy.h=2187=struct phy_device *phy_find_next(struct mii_bus *bus, struct phy_device *pos);\ninclude/linux/phy.h:2188:int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,\ninclude/linux/phy.h-2189-\t\t u32 flags, phy_interface_t interface);\n"
} |
| 5/2 | 2026/07/31 01:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19294 output: 17 thoughts: 206 Thoughts: **Investigating the Reachability of `phy_attach_direct`** Okay, so `phy_attach_direct` is a key function, called by things like `phy_connect_direct`, `phylink_attach_phy`, and `phylink_fwnode_phy_connect`. The question that's piquing my interest is whether a virtual device can trigger its use. My instinct is that PHYs are primarily hardware-bound, but I need to be sure. Let's run through some common virtual network devices. Could `macsec`, `veth`, `dummy`, or `tun` devices ever touch this function? My gut says no, given their nature. They're usually layers above the physical interface, abstracting away the PHY interaction. Thinking about it, even though PHYs are typically associated with hardware like in `macb`, `fec`, or `e1000` drivers, are any of these pathways relevant? I don't see it. `tun` and `veth` definitely don't deal with the physical layer. Okay, let's keep going. What about something like `mac80211_hwsim`? No, it's not a direct match. `vkms` is DRM, so no there either. `dummy` is also out. Alright, time to check `netdevsim`. *That's* the one that seems like it might be interesting to look into. Let me see if `netdevsim` has any interaction with PHYs, as that would be the key. |
| 6/2 | 2026/07/31 01:13 | tool | grepper |
0mArgs:{
"Expression": "phy_attach"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 780 lines.\nUse more precise expression if possible.\n\nDocumentation/networking/phy-link-topology.rst=85=These function are already hooked into the phylib subsystem, so all PHYs that\nDocumentation/networking/phy-link-topology.rst:86:are linked to a net_device through :c:func:`phy_attach_direct` will automatically\nDocumentation/networking/phy-link-topology.rst-87-join the netdev's topology.\n--\nDocumentation/networking/phy.rst=363=every second before the state machine reacts to any changes. To do this, you\nDocumentation/networking/phy.rst:364:need to manually call phy_attach() and phy_prepare_link(), and then call\nDocumentation/networking/phy.rst-365-phy_start_machine() with the second argument set to point to your special\n--\nDocumentation/networking/phy.rst=376=the complex interactions between the PHY and your network device. If this is\nDocumentation/networking/phy.rst:377:so, you can simply call phy_attach(), and not call phy_start_machine or\nDocumentation/networking/phy.rst-378-phy_prepare_link(). This will mean that phydev-\u003estate is entirely yours to\n--\nDocumentation/networking/phy.rst=404=Requests the IRQ for the PHY interrupts.\n--\nDocumentation/networking/phy.rst-406-\nDocumentation/networking/phy.rst:407: struct phy_device * phy_attach(struct net_device *dev, const char *phy_id,\nDocumentation/networking/phy.rst-408-\t\t phy_interface_t interface);\n--\nDocumentation/networking/sfp-phylink.rst=74=this documentation.\n--\nDocumentation/networking/sfp-phylink.rst-165- For the most part, ``flags`` can be zero; these flags are passed to\nDocumentation/networking/sfp-phylink.rst:166: the phy_attach_direct() inside this function call if a PHY is specified\nDocumentation/networking/sfp-phylink.rst-167- in the DT node ``node``.\n--\ndrivers/net/ethernet/actions/owl-emac.c=1353=static int owl_emac_phy_init(struct net_device *netdev)\n--\ndrivers/net/ethernet/actions/owl-emac.c-1366-\tif (netif_msg_link(priv))\ndrivers/net/ethernet/actions/owl-emac.c:1367:\t\tphy_attached_info(phy);\ndrivers/net/ethernet/actions/owl-emac.c-1368-\n--\ndrivers/net/ethernet/adi/adin1140.c=650=static int adin1140_phy_init(struct adin1140_priv *priv,\n--\ndrivers/net/ethernet/adi/adin1140.c-676-\ndrivers/net/ethernet/adi/adin1140.c:677:\tphy_attached_info(priv-\u003ephydev);\ndrivers/net/ethernet/adi/adin1140.c-678-\n--\ndrivers/net/ethernet/agere/et131x.c=3273=static int et131x_mii_probe(struct net_device *netdev)\n--\ndrivers/net/ethernet/agere/et131x.c-3298-\ndrivers/net/ethernet/agere/et131x.c:3299:\tphy_attached_info(phydev);\ndrivers/net/ethernet/agere/et131x.c-3300-\n--\ndrivers/net/ethernet/amd/au1000_eth.c=472=static int au1000_mii_probe(struct net_device *dev)\n--\ndrivers/net/ethernet/amd/au1000_eth.c-552-\ndrivers/net/ethernet/amd/au1000_eth.c:553:\tphy_attached_info(phydev);\ndrivers/net/ethernet/amd/au1000_eth.c-554-\n--\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c=931=static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)\n--\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-982-\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c:983:\tret = phy_attach_direct(pdata-\u003enetdev, phydev, phydev-\u003edev_flags,\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-984-\t\t\t\tPHY_INTERFACE_MODE_SGMII);\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-985-\tif (ret) {\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c:986:\t\tnetdev_err(pdata-\u003enetdev, \"phy_attach_direct failed\\n\");\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-987-\t\tphy_device_remove(phydev);\n--\ndrivers/net/ethernet/asix/ax88796c_main.c=966=static int ax88796c_probe(struct spi_device *spi)\n--\ndrivers/net/ethernet/asix/ax88796c_main.c-1094-\t\t dev_name(\u0026spi-\u003edev));\ndrivers/net/ethernet/asix/ax88796c_main.c:1095:\tphy_attached_info(ax_local-\u003ephydev);\ndrivers/net/ethernet/asix/ax88796c_main.c-1096-\n--\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c=1037=static phy_interface_t bcmasp_phy_iface_for_connect(phy_interface_t mode)\n--\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c-1073-\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c:1074:static int bcmasp_phy_attach(struct bcmasp_intf *intf)\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c-1075-{\n--\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c=1121=static int bcmasp_open(struct net_device *dev)\n--\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c-1139-\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c:1140:\tret = bcmasp_phy_attach(intf);\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c-1141-\tif (ret)\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c:1142:\t\tgoto err_phy_attach;\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c-1143-\n--\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c-1155-\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c:1156:err_phy_attach:\ndrivers/net/ethernet/broadcom/asp2/bcmasp_intf.c-1157-\tbcmasp_phy_hw_unprepare(intf);\n--\ndrivers/net/ethernet/broadcom/b44.c=2234=static int b44_register_phy_one(struct b44 *bp)\n--\ndrivers/net/ethernet/broadcom/b44.c-2298-\ndrivers/net/ethernet/broadcom/b44.c:2299:\tphy_attached_info(phydev);\ndrivers/net/ethernet/broadcom/b44.c-2300-\n--\ndrivers/net/ethernet/broadcom/bcm63xx_enet.c=890=static int bcm_enet_open(struct net_device *dev)\n--\ndrivers/net/ethernet/broadcom/bcm63xx_enet.c-923-\ndrivers/net/ethernet/broadcom/bcm63xx_enet.c:924:\t\tphy_attached_info(phydev);\ndrivers/net/ethernet/broadcom/bcm63xx_enet.c-925-\n--\ndrivers/net/ethernet/broadcom/sb1250-mac.c=2324=static int sbmac_mii_probe(struct net_device *dev)\n--\ndrivers/net/ethernet/broadcom/sb1250-mac.c-2345-\ndrivers/net/ethernet/broadcom/sb1250-mac.c:2346:\tphy_attached_info(phy_dev);\ndrivers/net/ethernet/broadcom/sb1250-mac.c-2347-\n--\ndrivers/net/ethernet/broadcom/tg3.c=2084=static int tg3_phy_init(struct tg3 *tp)\n--\ndrivers/net/ethernet/broadcom/tg3.c-2124-\ndrivers/net/ethernet/broadcom/tg3.c:2125:\tphy_attached_info(phydev);\ndrivers/net/ethernet/broadcom/tg3.c-2126-\n--\ndrivers/net/ethernet/cortina/gemini.c=360=static int gmac_setup_phy(struct net_device *netdev)\n--\ndrivers/net/ethernet/cortina/gemini.c-405-\tif (netif_msg_link(port))\ndrivers/net/ethernet/cortina/gemini.c:406:\t\tphy_attached_info(phy);\ndrivers/net/ethernet/cortina/gemini.c-407-\n--\ndrivers/net/ethernet/faraday/ftgmac100.c=1497=static int ftgmac100_mii_probe(struct net_device *netdev)\n--\ndrivers/net/ethernet/faraday/ftgmac100.c-1555-\t/* Display what we found */\ndrivers/net/ethernet/faraday/ftgmac100.c:1556:\tphy_attached_info(phydev);\ndrivers/net/ethernet/faraday/ftgmac100.c-1557-\n--\ndrivers/net/ethernet/faraday/ftgmac100.c=1890=static int ftgmac100_probe_dt(struct net_device *netdev,\n--\ndrivers/net/ethernet/faraday/ftgmac100.c-1921-\t\t/* Display what we found */\ndrivers/net/ethernet/faraday/ftgmac100.c:1922:\t\tphy_attached_info(phy);\ndrivers/net/ethernet/faraday/ftgmac100.c-1923-\t\treturn 0;\n--\ndrivers/net/ethernet/freescale/fec_main.c=2981=static int fec_enet_mii_probe(struct net_device *ndev)\n--\ndrivers/net/ethernet/freescale/fec_main.c-3036-\ndrivers/net/ethernet/freescale/fec_main.c:3037:\tphy_attached_info(phy_dev);\ndrivers/net/ethernet/freescale/fec_main.c-3038-\n--\ndrivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c=201=static int hbg_phy_connect(struct hbg_priv *priv)\n--\ndrivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c-217-\tphy_support_asym_pause(phydev);\ndrivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c:218:\tphy_attached_info(phydev);\ndrivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c-219-\n--\ndrivers/net/ethernet/hisilicon/hisi_femac.c=768=static int hisi_femac_drv_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/hisilicon/hisi_femac.c-839-\ndrivers/net/ethernet/hisilicon/hisi_femac.c:840:\tphy_attached_print(phy, \"phy_id=0x%.8lx, phy_mode=%s\\n\",\ndrivers/net/ethernet/hisilicon/hisi_femac.c-841-\t\t\t (unsigned long)phy-\u003ephy_id,\n--\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c=1159=int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h)\n--\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c-1180-\t} else {\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c:1181:\t\tret = phy_attach_direct(ndev, phy_dev, 0, h-\u003ephy_if);\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c-1182-\t}\n--\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c-1185-\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c:1186:\tphy_attached_info(phy_dev);\ndrivers/net/ethernet/hisilicon/hns/hns_enet.c-1187-\n--\ndrivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c=202=int hclge_mac_connect_phy(struct hnae3_handle *handle)\n--\ndrivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c-236-\ndrivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c:237:\tphy_attached_info(phydev);\ndrivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c-238-\n--\ndrivers/net/ethernet/lantiq_etop.c=357=ltq_etop_mdio_probe(struct net_device *dev)\n--\ndrivers/net/ethernet/lantiq_etop.c-378-\ndrivers/net/ethernet/lantiq_etop.c:379:\tphy_attached_info(phydev);\ndrivers/net/ethernet/lantiq_etop.c-380-\n--\ndrivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c=385=static int mlxbf_gige_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c-487-\t/* Display information about attached PHY device */\ndrivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c:488:\tphy_attached_info(phydev);\ndrivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c-489-\n--\ndrivers/net/ethernet/nxp/lpc_eth.c=751=static int lpc_mii_probe(struct net_device *ndev)\n--\ndrivers/net/ethernet/nxp/lpc_eth.c-784-\ndrivers/net/ethernet/nxp/lpc_eth.c:785:\tphy_attached_info(phydev);\ndrivers/net/ethernet/nxp/lpc_eth.c-786-\n--\ndrivers/net/ethernet/oa_tc6.c=578=static int oa_tc6_phy_init(struct oa_tc6 *tc6)\n--\ndrivers/net/ethernet/oa_tc6.c-613-\ndrivers/net/ethernet/oa_tc6.c:614:\tphy_attached_info(tc6-\u003enetdev-\u003ephydev);\ndrivers/net/ethernet/oa_tc6.c-615-\n--\ndrivers/net/ethernet/qualcomm/emac/emac-mac.c=931=int emac_mac_up(struct emac_adapter *adpt)\n--\ndrivers/net/ethernet/qualcomm/emac/emac-mac.c-947-\ndrivers/net/ethernet/qualcomm/emac/emac-mac.c:948:\tphy_attached_print(adpt-\u003ephydev, NULL);\ndrivers/net/ethernet/qualcomm/emac/emac-mac.c-949-\n--\ndrivers/net/ethernet/rdc/r6040.c=998=static int r6040_mii_probe(struct net_device *dev)\n--\ndrivers/net/ethernet/rdc/r6040.c-1021-\ndrivers/net/ethernet/rdc/r6040.c:1022:\tphy_attached_info(phydev);\ndrivers/net/ethernet/rdc/r6040.c-1023-\n--\ndrivers/net/ethernet/realtek/r8169_main.c=4982=static int r8169_phy_connect(struct rtl8169_private *tp)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-4998-\ndrivers/net/ethernet/realtek/r8169_main.c:4999:\tphy_attached_info(phydev);\ndrivers/net/ethernet/realtek/r8169_main.c-5000-\n--\ndrivers/net/ethernet/renesas/ravb_main.c=1492=static int ravb_phy_init(struct net_device *ndev)\n--\ndrivers/net/ethernet/renesas/ravb_main.c-1541-\ndrivers/net/ethernet/renesas/ravb_main.c:1542:\tphy_attached_info(phydev);\ndrivers/net/ethernet/renesas/ravb_main.c-1543-\n--\ndrivers/net/ethernet/renesas/rswitch_main.c=1460=static int rswitch_phy_device_init(struct rswitch_device *rdev)\n--\ndrivers/net/ethernet/renesas/rswitch_main.c-1493-\ndrivers/net/ethernet/renesas/rswitch_main.c:1494:\tphy_attached_info(phydev);\ndrivers/net/ethernet/renesas/rswitch_main.c-1495-\n--\ndrivers/net/ethernet/renesas/rtsn.c=895=static int rtsn_phy_init(struct rtsn_private *priv)\n--\ndrivers/net/ethernet/renesas/rtsn.c-917-\ndrivers/net/ethernet/renesas/rtsn.c:918:\tphy_attached_info(phydev);\ndrivers/net/ethernet/renesas/rtsn.c-919-\n--\ndrivers/net/ethernet/renesas/sh_eth.c=1987=static int sh_eth_phy_init(struct net_device *ndev)\n--\ndrivers/net/ethernet/renesas/sh_eth.c-2027-\ndrivers/net/ethernet/renesas/sh_eth.c:2028:\tphy_attached_info(phydev);\ndrivers/net/ethernet/renesas/sh_eth.c-2029-\n--\ndrivers/net/ethernet/smsc/smsc911x.c=1015=static int smsc911x_mii_probe(struct net_device *dev)\n--\ndrivers/net/ethernet/smsc/smsc911x.c-1038-\ndrivers/net/ethernet/smsc/smsc911x.c:1039:\tphy_attached_info(phydev);\ndrivers/net/ethernet/smsc/smsc911x.c-1040-\n--\ndrivers/net/ethernet/smsc/smsc9420.c=1102=static int smsc9420_mii_probe(struct net_device *dev)\n--\ndrivers/net/ethernet/smsc/smsc9420.c-1128-\ndrivers/net/ethernet/smsc/smsc9420.c:1129:\tphy_attached_info(phydev);\ndrivers/net/ethernet/smsc/smsc9420.c-1130-\n--\ndrivers/net/ethernet/socionext/sni_ave.c=1163=static int ave_init(struct net_device *ndev)\n--\ndrivers/net/ethernet/socionext/sni_ave.c-1232-\ndrivers/net/ethernet/socionext/sni_ave.c:1233:\tphy_attached_info(phydev);\ndrivers/net/ethernet/socionext/sni_ave.c-1234-\n--\ndrivers/net/ethernet/spacemit/k1_emac.c=1597=static int emac_phy_connect(struct net_device *ndev)\n--\ndrivers/net/ethernet/spacemit/k1_emac.c-1648-\ndrivers/net/ethernet/spacemit/k1_emac.c:1649:\tphy_attached_info(phydev);\ndrivers/net/ethernet/spacemit/k1_emac.c-1650-\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c=595=int stmmac_mdio_register(struct net_device *ndev)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c-704-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c:705:\tphy_attached_info(phydev);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c-706-\n--\ndrivers/net/ethernet/sunplus/spl2sw_phy.c=56=int spl2sw_phy_connect(struct spl2sw_common *comm)\n--\ndrivers/net/ethernet/sunplus/spl2sw_phy.c-72-\t\t\tphy_support_asym_pause(phydev);\ndrivers/net/ethernet/sunplus/spl2sw_phy.c:73:\t\t\tphy_attached_info(phydev);\ndrivers/net/ethernet/sunplus/spl2sw_phy.c-74-\t\t}\n--\ndrivers/net/ethernet/ti/cpsw.c=585=static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)\n--\ndrivers/net/ethernet/ti/cpsw.c-662-\ndrivers/net/ethernet/ti/cpsw.c:663:\tphy_attached_info(slave-\u003ephy);\ndrivers/net/ethernet/ti/cpsw.c-664-\n--\ndrivers/net/ethernet/ti/cpsw_new.c=740=static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)\n--\ndrivers/net/ethernet/ti/cpsw_new.c-804-\ndrivers/net/ethernet/ti/cpsw_new.c:805:\tphy_attached_info(slave-\u003ephy);\ndrivers/net/ethernet/ti/cpsw_new.c-806-\n--\ndrivers/net/ethernet/ti/davinci_emac.c=1402=static int emac_dev_open(struct net_device *ndev)\n--\ndrivers/net/ethernet/ti/davinci_emac.c-1528-\ndrivers/net/ethernet/ti/davinci_emac.c:1529:\t\tphy_attached_info(phydev);\ndrivers/net/ethernet/ti/davinci_emac.c-1530-\t}\n--\ndrivers/net/ethernet/ti/icssg/icssg_prueth.c=2178=static int prueth_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/ti/icssg/icssg_prueth.c-2416-\t\t}\ndrivers/net/ethernet/ti/icssg/icssg_prueth.c:2417:\t\tphy_attached_info(prueth-\u003eemac[PRUETH_MAC0]-\u003endev-\u003ephydev);\ndrivers/net/ethernet/ti/icssg/icssg_prueth.c-2418-\t}\n--\ndrivers/net/ethernet/ti/icssg/icssg_prueth.c-2433-\t\t}\ndrivers/net/ethernet/ti/icssg/icssg_prueth.c:2434:\t\tphy_attached_info(prueth-\u003eemac[PRUETH_MAC1]-\u003endev-\u003ephydev);\ndrivers/net/ethernet/ti/icssg/icssg_prueth.c-2435-\t}\n--\ndrivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c=889=static int prueth_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c-1090-\t\temac_phy_connect(prueth-\u003eemac[PRUETH_MAC0]);\ndrivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c:1091:\t\tphy_attached_info(prueth-\u003eemac[PRUETH_MAC0]-\u003endev-\u003ephydev);\ndrivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c-1092-\t}\n--\ndrivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c-1102-\t\temac_phy_connect(prueth-\u003eemac[PRUETH_MAC1]);\ndrivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c:1103:\t\tphy_attached_info(prueth-\u003eemac[PRUETH_MAC1]-\u003endev-\u003ephydev);\ndrivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c-1104-\t}\n--\ndrivers/net/ethernet/toshiba/tc35815.c=607=static int tc_mii_probe(struct net_device *dev)\n--\ndrivers/net/ethernet/toshiba/tc35815.c-627-\ndrivers/net/ethernet/toshiba/tc35815.c:628:\tphy_attached_info(phydev);\ndrivers/net/ethernet/toshiba/tc35815.c-629-\n--\ndrivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c=159=int ngbe_mdio_init(struct wx *wx)\n--\ndrivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c-189-\ndrivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c:190:\tphy_attached_info(wx-\u003ephydev);\ndrivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c-191-\n--\ndrivers/net/ethernet/wangxun/txgbe/txgbe_phy.c=524=static int txgbe_ext_phy_init(struct txgbe *txgbe)\n--\ndrivers/net/ethernet/wangxun/txgbe/txgbe_phy.c-558-\ndrivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:559:\tphy_attached_info(phydev);\ndrivers/net/ethernet/wangxun/txgbe/txgbe_phy.c-560-\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c=760=static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-775-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:776:static int otto_emdio_notify_phy_attach(struct phy_device *phydev)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-777-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c=829=static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-857-\tbus-\u003eparent = dev;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:858:\tbus-\u003enotify_phy_attach = otto_emdio_notify_phy_attach;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-859-\tbus-\u003enotify_phy_detach = otto_emdio_notify_phy_detach;\n--\ndrivers/net/phy/phy_device.c=1225=int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,\n--\ndrivers/net/phy/phy_device.c-1233-\ndrivers/net/phy/phy_device.c:1234:\trc = phy_attach_direct(dev, phydev, phydev-\u003edev_flags, interface);\ndrivers/net/phy/phy_device.c-1235-\tif (rc)\n--\ndrivers/net/phy/phy_device.c=1388=EXPORT_SYMBOL(phy_init_hw);\ndrivers/net/phy/phy_device.c-1389-\ndrivers/net/phy/phy_device.c:1390:void phy_attached_info(struct phy_device *phydev)\ndrivers/net/phy/phy_device.c-1391-{\ndrivers/net/phy/phy_device.c:1392:\tphy_attached_print(phydev, NULL);\ndrivers/net/phy/phy_device.c-1393-}\ndrivers/net/phy/phy_device.c:1394:EXPORT_SYMBOL(phy_attached_info);\ndrivers/net/phy/phy_device.c-1395-\ndrivers/net/phy/phy_device.c-1396-#define ATTACHED_FMT \"attached PHY driver %s(mii_bus:phy_addr=%s, irq=%s)\"\ndrivers/net/phy/phy_device.c:1397:char *phy_attached_info_irq(struct phy_device *phydev)\ndrivers/net/phy/phy_device.c-1398-{\n--\ndrivers/net/phy/phy_device.c-1416-}\ndrivers/net/phy/phy_device.c:1417:EXPORT_SYMBOL(phy_attached_info_irq);\ndrivers/net/phy/phy_device.c-1418-\ndrivers/net/phy/phy_device.c:1419:void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)\ndrivers/net/phy/phy_device.c-1420-{\ndrivers/net/phy/phy_device.c-1421-\tconst char *unbound = phydev-\u003edrv ? \"\" : \"[unbound] \";\ndrivers/net/phy/phy_device.c:1422:\tchar *irq_str = phy_attached_info_irq(phydev);\ndrivers/net/phy/phy_device.c-1423-\n--\ndrivers/net/phy/phy_device.c-1438-}\ndrivers/net/phy/phy_device.c:1439:EXPORT_SYMBOL(phy_attached_print);\ndrivers/net/phy/phy_device.c-1440-\n--\ndrivers/net/phy/phy_device.c=1737=static void __phy_detach(struct phy_device *phydev, bool notify_bus)\n--\ndrivers/net/phy/phy_device.c-1817- * This detaches the phy device from its network device and the phy\ndrivers/net/phy/phy_device.c:1818: * driver, and drops the reference count taken in phy_attach_direct().\ndrivers/net/phy/phy_device.c-1819- */\n--\ndrivers/net/phy/phy_device.c=1825=EXPORT_SYMBOL(phy_detach);\n--\ndrivers/net/phy/phy_device.c-1827-/**\ndrivers/net/phy/phy_device.c:1828: * phy_attach_direct - attach a network device to a given PHY device pointer\ndrivers/net/phy/phy_device.c-1829- * @dev: network device to attach\n--\ndrivers/net/phy/phy_device.c-1841- */\ndrivers/net/phy/phy_device.c:1842:int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,\ndrivers/net/phy/phy_device.c-1843-\t\t u32 flags, phy_interface_t interface)\n--\ndrivers/net/phy/phy_device.c-1968-\ndrivers/net/phy/phy_device.c:1969:\tif (phydev-\u003emdio.bus-\u003enotify_phy_attach) {\ndrivers/net/phy/phy_device.c:1970:\t\terr = phydev-\u003emdio.bus-\u003enotify_phy_attach(phydev);\ndrivers/net/phy/phy_device.c-1971-\t\tif (err)\n--\ndrivers/net/phy/phy_device.c-2002-}\ndrivers/net/phy/phy_device.c:2003:EXPORT_SYMBOL(phy_attach_direct);\ndrivers/net/phy/phy_device.c-2004-\n--\ndrivers/net/phy/phylink.c=2087=static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,\n--\ndrivers/net/phy/phylink.c-2121-\ndrivers/net/phy/phylink.c:2122:\tirq_str = phy_attached_info_irq(phy);\ndrivers/net/phy/phylink.c-2123-\tphylink_info(pl,\n--\ndrivers/net/phy/phylink.c=2204=static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,\n--\ndrivers/net/phy/phylink.c-2219-\ndrivers/net/phy/phylink.c:2220:\treturn phy_attach_direct(pl-\u003enetdev, phy, flags, interface);\ndrivers/net/phy/phylink.c-2221-}\n--\ndrivers/net/phy/phylink.c-2228- * Connect @phy to the phylink instance specified by @pl by calling\ndrivers/net/phy/phylink.c:2229: * phy_attach_direct(). Configure the @phy according to the MAC driver's\ndrivers/net/phy/phylink.c-2230- * capabilities, start the PHYLIB state machine and enable any interrupts\n--\ndrivers/net/phy/phylink.c=2290=int phylink_fwnode_phy_connect(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-2323-\ndrivers/net/phy/phylink.c:2324:\tret = phy_attach_direct(pl-\u003enetdev, phy_dev, flags,\ndrivers/net/phy/phylink.c-2325-\t\t\t\tpl-\u003elink_interface);\n--\ndrivers/net/usb/asix_devices.c=703=static int ax88772_init_phy(struct usbnet *dev)\n--\ndrivers/net/usb/asix_devices.c-722-\ndrivers/net/usb/asix_devices.c:723:\tphy_attached_info(priv-\u003ephydev);\ndrivers/net/usb/asix_devices.c-724-\n--\ndrivers/net/usb/smsc95xx.c=1147=static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)\n--\ndrivers/net/usb/smsc95xx.c-1292-\ndrivers/net/usb/smsc95xx.c:1293:\tphy_attached_info(dev-\u003enet-\u003ephydev);\ndrivers/net/usb/smsc95xx.c-1294-\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c=4345=static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core,\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c-4538-\t\twlc_hw-\u003eband-\u003epi =\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c:4539:\t\t\twlc_phy_attach(wlc_hw-\u003ephy_sh, core,\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c-4540-\t\t\t\t wlc_hw-\u003eband-\u003ebandtype,\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c=396=struct brcms_phy_pub *\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c:397:wlc_phy_attach(struct shared_phy *sh, struct bcma_device *d11core,\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c-398-\t int bandtype, struct wiphy *wiphy)\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c-532-\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c:533:\t\twlc_phy_attach_nphy(pi);\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c-534-\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c-535-\t} else if (ISLCNPHY(pi)) {\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c:536:\t\tif (!wlc_phy_attach_lcnphy(pi))\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c-537-\t\t\tgoto err;\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h=166=struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp);\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h:167:struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh,\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h-168-\t\t\t\t struct bcma_device *d11core, int bandtype,\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h=939=void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi);\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h-940-\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h:941:void wlc_phy_attach_nphy(struct brcms_phy *pi);\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h:942:bool wlc_phy_attach_lcnphy(struct brcms_phy *pi);\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h-943-\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c=4953=void wlc_phy_detach_lcnphy(struct brcms_phy *pi)\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c-4957-\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c:4958:bool wlc_phy_attach_lcnphy(struct brcms_phy *pi)\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c-4959-{\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c=14549=static void wlc_phy_txpwr_srom_read_nphy(struct brcms_phy *pi)\n--\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c-14599-\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c:14600:void wlc_phy_attach_nphy(struct brcms_phy *pi)\ndrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c-14601-{\n--\ndrivers/scsi/bfa/bfa_core.c=154=static void\ndrivers/scsi/bfa/bfa_core.c:155:bfa_com_phy_attach(struct bfa_s *bfa, bfa_boolean_t mincfg)\ndrivers/scsi/bfa/bfa_core.c-156-{\n--\ndrivers/scsi/bfa/bfa_core.c-159-\ndrivers/scsi/bfa/bfa_core.c:160:\tbfa_phy_attach(phy, \u0026bfa-\u003eioc, bfa, bfa-\u003etrcmod, mincfg);\ndrivers/scsi/bfa/bfa_core.c-161-\tbfa_phy_memclaim(phy, phy_dma-\u003ekva_curp, phy_dma-\u003edma_curp, mincfg);\n--\ndrivers/scsi/bfa/bfa_core.c=1827=bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,\n--\ndrivers/scsi/bfa/bfa_core.c-1872-\tbfa_com_diag_attach(bfa);\ndrivers/scsi/bfa/bfa_core.c:1873:\tbfa_com_phy_attach(bfa, cfg-\u003edrvcfg.min_cfg);\ndrivers/scsi/bfa/bfa_core.c-1874-\tbfa_com_fru_attach(bfa, cfg-\u003edrvcfg.min_cfg);\n--\ndrivers/scsi/bfa/bfa_ioc.c=5436=void\ndrivers/scsi/bfa/bfa_ioc.c:5437:bfa_phy_attach(struct bfa_phy_s *phy, struct bfa_ioc_s *ioc, void *dev,\ndrivers/scsi/bfa/bfa_ioc.c-5438-\t\tstruct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg)\n--\ndrivers/scsi/bfa/bfa_ioc.h=729=u32\tbfa_phy_meminfo(bfa_boolean_t mincfg);\ndrivers/scsi/bfa/bfa_ioc.h:730:void bfa_phy_attach(struct bfa_phy_s *phy, struct bfa_ioc_s *ioc,\ndrivers/scsi/bfa/bfa_ioc.h-731-\t\tvoid *dev, struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg);\n--\ndrivers/scsi/hisi_sas/hisi_sas.h=181=struct hisi_sas_phy {\n--\ndrivers/scsi/hisi_sas/hisi_sas.h-192-\tu8\t\tframe_rcvd[FRAME_RCVD_BUF];\ndrivers/scsi/hisi_sas/hisi_sas.h:193:\tu8\t\tphy_attached;\ndrivers/scsi/hisi_sas/hisi_sas.h-194-\tu8\t\tin_reset;\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c=649=static void hisi_sas_bytes_dmaed(struct hisi_hba *hisi_hba, int phy_no,\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-654-\ndrivers/scsi/hisi_sas/hisi_sas_main.c:655:\tif (!phy-\u003ephy_attached)\ndrivers/scsi/hisi_sas/hisi_sas_main.c-656-\t\treturn;\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c=1030=void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no)\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1037-\tspin_lock_irqsave(\u0026phy-\u003elock, flags);\ndrivers/scsi/hisi_sas/hisi_sas_main.c:1038:\tif (phy-\u003ephy_attached) {\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1039-\t\tspin_unlock_irqrestore(\u0026phy-\u003elock, flags);\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c=1280=static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1289-\tint phy_no = sas_phy-\u003eid;\ndrivers/scsi/hisi_sas/hisi_sas_main.c:1290:\tu8 sts = phy-\u003ephy_attached;\n\n"
} |
| 7/2 | 2026/07/31 01:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 32092 output: 30 thoughts: 384 Thoughts: **My Investigation into `phy_attach_direct` Reachability** Okay, so I'm trying to figure out if syzkaller can trigger the `phy_attach_direct` function. This function, I know, is called by `phy_connect_direct`, which in turn is called by `phy_connect`. The connection chain is important, because `phy_connect` is called by a lot of network drivers. So, the question is, can syzkaller somehow get there? My initial thought is to look for a way syzkaller could create a virtual device that interacts with `phylib`. I considered the possibility of using `netlink` to accomplish this. Then, I had a brief, incorrect thought about `macsec` using PHYs, but quickly dismissed itβ`macsec` is purely a software device. I started scanning potential virtual devices in the kernel. Let's see... I checked `drivers/net/dummy.c`, `drivers/net/veth.c`, `drivers/net/tun.c`, and `drivers/net/macvlan.c`. None of those use PHYs. I thought about MDIO buses, so I glanced at `drivers/net/phy/mdio_bus.c`. I know syzkaller can interact with those through ioctls like `SIOCSMIIREG` and `SIOCGMIIPHY`. However, this doesn't directly trigger `phy_attach_direct`; this function is called when a `net_device` attaches to a PHY. That's when it hit me: `phy_attach_direct` is called when a `net_device` is opened, say, via the `ndo_open` method, and only when the device actually has a PHY associated with it. So, if the kernel is compiled with things like `CONFIG_DUMMY` or `CONFIG_VETH`, I can't trigger it, because those devices don't have PHYs. So, I need to find a network driver, reachable by syzkaller, which *does* use PHYs. That's the key. Aha! Syzkaller can use `netdevsim`. I'll go check whether or not `netdevsim` uses PHYs. |
| 8/2 | 2026/07/31 01:14 | tool | grepper |
0mError:no matches Args: {
"Expression": "phy_attach",
"PathPrefix": "drivers/net/netdevsim"
}Results: {
"error": "no matches"
} |
| 9/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 33000 output: 28 thoughts: 47 Thoughts: **USB Network Drivers and PHYs: A Syzkaller Investigation** Okay, so I'm thinking about how to broaden the coverage of my fuzzing efforts, and `drivers/net/usb` comes to mind. Syzkaller's ability to emulate USB devices using `dummy_hcd` and `syz_usb_connect` is definitely something I want to leverage. The question is, what vulnerabilities are likely to be lurking there? My initial thought is to investigate whether any USB network drivers actually rely on PHYs. This could open up a new avenue for testing and exploit discovery. So, I need to start digging into the code and see if there's any link between the USB network drivers and any Physical Layer implementations. This is the next logical step to understand the potential attack surface. |
| 10/2 | 2026/07/31 01:14 | tool | grepper |
0mArgs:{
"Expression": "phy_attach",
"PathPrefix": "drivers/net/usb"
}Results: {
"Output": "drivers/net/usb/asix_devices.c=703=static int ax88772_init_phy(struct usbnet *dev)\n--\ndrivers/net/usb/asix_devices.c-722-\ndrivers/net/usb/asix_devices.c:723:\tphy_attached_info(priv-\u003ephydev);\ndrivers/net/usb/asix_devices.c-724-\n--\ndrivers/net/usb/smsc95xx.c=1147=static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)\n--\ndrivers/net/usb/smsc95xx.c-1292-\ndrivers/net/usb/smsc95xx.c:1293:\tphy_attached_info(dev-\u003enet-\u003ephydev);\ndrivers/net/usb/smsc95xx.c-1294-\n"
} |
| 11/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 33481 output: 28 thoughts: 31 Thoughts: Okay, here's my take on those thoughts, framed as a first-person expert summary: **Network Driver Investigation: `phy_connect` vs. `phy_attach_direct`** Right, so I'm trying to quickly ascertain how the `smsc95xx` and `asix` drivers handle their PHY connections. The key thing I'm looking for is whether they're employing the legacy `phy_connect` function, or if they've migrated to the newer `phy_attach_direct` approach. Knowing this impacts so many things, from the overall driver architecture to potential quirks in handling specific PHY characteristics. It will help me diagnose certain issues, compare their methods for future reference, and understand if they have taken advantage of the modern approach. It's really the starting point for understanding their PHY interaction model and where to look for potential problems or areas for optimization. Time to get digging through the code... let's see which path these drivers are taking. |
| 12/2 | 2026/07/31 01:14 | tool | grepper |
0mArgs:{
"Expression": "phy_connect",
"PathPrefix": "drivers/net/usb"
}Results: {
"Output": "drivers/net/usb/ax88172a.c=261=static int ax88172a_reset(struct usbnet *dev)\n--\ndrivers/net/usb/ax88172a.c-313-\ndrivers/net/usb/ax88172a.c:314:\tpriv-\u003ephydev = phy_connect(dev-\u003enet, priv-\u003ephy_name,\ndrivers/net/usb/ax88172a.c-315-\t\t\t\t \u0026ax88172a_adjust_link,\n--\ndrivers/net/usb/smsc95xx.c=1147=static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)\n--\ndrivers/net/usb/smsc95xx.c-1284-\ndrivers/net/usb/smsc95xx.c:1285:\tret = phy_connect_direct(dev-\u003enet, pdata-\u003ephydev,\ndrivers/net/usb/smsc95xx.c-1286-\t\t\t\t \u0026smsc95xx_handle_link_change,\n"
} |
| 13/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 34038 output: 43 thoughts: 321 Thoughts: **Analyzing the Potential Impact of the New MDIO Bus Callbacks** Okay, this is interesting. I'm following the potential call chain here. It looks like both `smsc95xx` and `ax88172a` use `phy_connect_direct` and `phy_connect`, which, in turn, call `phy_attach_direct`. Given Syzkaller's USB emulation capabilities, it could trigger either `smsc95xx_bind` or `ax88172a_reset`. That would be the entry point to get into the functions, `phy_connect_direct` and `phy_connect`, eventually leading to `phy_attach_direct`. And then, crucially, `phy_attach_direct` calls `phydev->mdio.bus->notify_phy_attach(phydev)`. That's where it connects. Aha! The patch *adds* `notify_phy_attach` and `notify_phy_detach` to the `struct mii_bus`, which is key. And then it actually implements these callbacks within `drivers/net/mdio/mdio-realtek-rtl9300.c`. But here's the snag: `smsc95xx` and `ax88172a` use *their own* MDIO buses, not the `mdio-realtek-rtl9300.c` implementation. This means the `notify_phy_attach` callback in `mdio-realtek-rtl9300.c` will *only* be called if the PHY is attached to the Realtek RTL9300 MDIO bus. The critical question now is whether Syzkaller can instantiate the Realtek RTL9300 MDIO bus. It's a platform driver, which suggests it might require specific platform initialization. I need to investigate how to trigger that platform driver, to see if I can force it to use this driver. That would be the path to making this call chain actually fire. |
| 14/2 | 2026/07/31 01:14 | tool | grepper |
0mArgs:{
"Expression": "platform_driver",
"PathPrefix": "drivers/net/mdio/mdio-realtek-rtl9300.c"
}Results: {
"Output": "drivers/net/mdio/mdio-realtek-rtl9300.c=1154=MODULE_DEVICE_TABLE(of, otto_emdio_ids);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1155-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1156:static struct platform_driver otto_emdio_driver = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1157-\t.probe = otto_emdio_probe,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1163-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1164:module_platform_driver(otto_emdio_driver);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1165-\n"
} |
| 15/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35093 output: 22 thoughts: 183 Thoughts: **My Current Line of Reasoning** Okay, so I'm thinking through how a platform driver gets loaded. Typically, it's the Device Tree that kicks things off, right? That's how the kernel knows about the hardware and what drivers to load, especially in a virtualized setup like QEMU or Google Compute Engine. I need to make sure the device tree has that all-important "compatible" string, the one that tells the kernel "hey, use this driver" β things like `"realtek,rtl8380-mdio"`. If it's missing, `otto_emdio_probe` isn't going to get called, and that's a problem. Now, my mind wanders to syzkaller... can I force a platform device into existence through `syz_devmem` or one of those pseudo-syscalls? No, wait, that's not quite right. Syzkaller has `syz_platform_device_add`, or it can use `netlink` to create devices, but generally it's a bit tricky to spin up platform devices unless there's some special syzkaller description for that specific device. But then, that sparks a new thought. Does syzkaller have a more general mechanism to instantiate *arbitrary* platform devices? Is there a broader way to define and trigger the creation of a platform device without relying on the Device Tree or a specific device-related call? I need to dig into the syzkaller documentation and code to see if there's a generalized "platform device" description somewhere. Time to check if there's a syzkaller description for platform devices! |
| 16/2 | 2026/07/31 01:14 | tool | grepper |
0mError:no matches Args: {
"Expression": "syz_platform_device_add"
}Results: {
"error": "no matches"
} |
| 17/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35661 output: 38 thoughts: 1394 Thoughts: **Worth Fuzzing Decision: YES** Okay, first things first, I need to ditch the idea of using `git grep` for searching syzkaller pseudo-syscalls; `read-file` or my existing knowledge is the way to go. This patch, it's got me thinking. It touches `drivers/net/phy/phy_device.c`, which is core PHY code. The changes there are simple: adding a notification mechanism on PHY attach/detach β checking for a function pointer and calling it. Very basic. The bulk of the changes are in a Realtek MDIO controller driver. That's platform-specific and for very specific Realtek hardware. Can I hit that in QEMU? Probably not without specific platform instantiation, which syzkaller doesn't usually do unless it's explicitly set up. So, that Realtek stuff is probably a dead end for me. But those core PHY changes? They're executed on *every* PHY attachment and detachment. Even if the `notify_phy_attach` is NULL, that code still runs. So if I fuzz `phy_attach_direct` and `phy_detach`, I'm fuzzing the core, regardless of the target. Is this worth fuzzing? The prompt says if it modifies reachable core kernel logic, I should verify execution and return `WorthFuzzing=true`. I'm thinking: is there some other way to reach the Realtek changes? Nope, that's specific hardware. BUT those core changes are indeed trivial, just a null check and a function call. It's a very simple branch. The real logic is in that vendor-specific driver, so that specific controller is not reachable. So it should be `WorthFuzzing=false`. Wait! The prompt also says: "If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true." I can reach this, using a USB networking gadget, such as `smsc95xx`. So it seems like fuzzing `phy_attach_direct` and `phy_detach` is actually a good plan. Those are definitely reachable through common USB networking gadgets. So, my focus should be those functions. Okay, so I should be returning `WorthFuzzing=true` and focusing on the core functions. Those will execute because of the USB networking gadget. Also, is this core path a hot-path? Not really, it's called during device open/initialization/teardown. This will be an edge case. One last check. `__phy_detach` is a static function, so it *might* be inlined. But it's called by `phy_detach` and `phy_attach_direct` (on error). Looks like that will need to be checked. Let me confirm, yes, `__phy_detach` is static. Alright, the core functions are what I should focus on. `phy_attach_direct` and `phy_detach` it is. And yes, I double-checked; `phy_attach_direct` is reachable through the `smsc95xx` device, which can be emulated. So I should return `WorthFuzzing=true` |
| 18/2 | 2026/07/31 01:14 | tool | grepper |
0mArgs:{
"Expression": "phy_connect_direct",
"PathPrefix": "drivers/net/usb/smsc95xx.c"
}Results: {
"Output": "drivers/net/usb/smsc95xx.c=1147=static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)\n--\ndrivers/net/usb/smsc95xx.c-1284-\ndrivers/net/usb/smsc95xx.c:1285:\tret = phy_connect_direct(dev-\u003enet, pdata-\u003ephydev,\ndrivers/net/usb/smsc95xx.c-1286-\t\t\t\t \u0026smsc95xx_handle_link_change,\n"
} |
| 19/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 37943 output: 32 thoughts: 78 Thoughts: **Reaching Phy Attach Direct** Okay, so looking at this code flow, `smsc95xx_bind` directly calls `phy_connect_direct`, and that function, in turn, calls `phy_attach_direct`. That confirms that `phy_attach_direct` *is* definitely reachable from that binding function. Good. Now, what about the build environment and the configurations? To make sure that `phy_device.c` is compiled, we're going to need `CONFIG_PHYLIB` enabled. I should probably check the `drivers/net/phy/Makefile` to confirm that. |
| 20/2 | 2026/07/31 01:14 | tool | grepper |
0mArgs:{
"Expression": "phy_device\\.o",
"PathPrefix": "drivers/net/phy/Makefile"
}Results: {
"Output": "drivers/net/phy/Makefile-3-\ndrivers/net/phy/Makefile:4:libphy-y\t\t\t:= phy.o phy-c45.o phy-core.o phy_device.o \\\ndrivers/net/phy/Makefile-5-\t\t\t\t linkmode.o phy_link_topology.o \\\n"
} |
| 21/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38269 output: 29 thoughts: 18 Thoughts: Here's a summary of my understanding: **PHY Library Dependencies** Okay, so I've just processed that `libphy-y` gets constructed only when the `CONFIG_PHYLIB` configuration option is enabled. That's straightforward enough. It implies that the PHY library, or some core part of it, is conditionally compiled into the kernel build process. This tells me that if `CONFIG_PHYLIB` isn't enabled, then the PHY functionality is either absent entirely from the kernel, implemented differently, or handled by a separate module. This is fundamental to my knowledge of kernel configuration; enabling it essentially includes the core PHY support. I need to now consider how the PHY library integrates with the rest of the network stack, and any potential side effects. |
| 22/2 | 2026/07/31 01:14 | tool | grepper |
0mArgs:{
"Expression": "libphy",
"PathPrefix": "drivers/net/phy/Makefile"
}Results: {
"Output": "drivers/net/phy/Makefile-3-\ndrivers/net/phy/Makefile:4:libphy-y\t\t\t:= phy.o phy-c45.o phy-core.o phy_device.o \\\ndrivers/net/phy/Makefile-5-\t\t\t\t linkmode.o phy_link_topology.o \\\n--\ndrivers/net/phy/Makefile=12=endif\ndrivers/net/phy/Makefile-13-\ndrivers/net/phy/Makefile:14:libphy-$(CONFIG_SWPHY)\t\t+= swphy.o\ndrivers/net/phy/Makefile:15:libphy-$(CONFIG_LED_TRIGGER_PHY)\t+= phy_led_triggers.o\ndrivers/net/phy/Makefile:16:libphy-$(CONFIG_OPEN_ALLIANCE_HELPERS) += open_alliance_helpers.o\ndrivers/net/phy/Makefile-17-\n--\ndrivers/net/phy/Makefile=19=obj-$(CONFIG_PHYLINK)\t\t+= phylink.o\ndrivers/net/phy/Makefile:20:obj-$(CONFIG_PHYLIB)\t\t+= libphy.o\ndrivers/net/phy/Makefile-21-obj-$(CONFIG_PHYLIB)\t\t+= mdio_devres.o\n"
} |
| 23/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38778 output: 38 thoughts: 48 Thoughts: **Examining the USB Network Driver Configuration** Okay, so I'm configuring this network driver, and I need to make sure I have the necessary dependencies sorted out. First, I'm confirming that `CONFIG_PHYLIB` is enabled; that's a key requirement. Then, specifically for the `smsc95xx` driver, I also need to ensure that `CONFIG_USB_NET_SMSC95XX` is enabled. To double-check those specific configurations, it looks like `drivers/net/usb/Kconfig` is the right place to find the relevant configurations. |
| 24/2 | 2026/07/31 01:14 | tool | grepper |
0mArgs:{
"Expression": "config USB_NET_SMSC95XX",
"PathPrefix": "drivers/net/usb/Kconfig"
}Results: {
"Output": "drivers/net/usb/Kconfig=341=config USB_NET_SMSC75XX\n--\ndrivers/net/usb/Kconfig-350-\ndrivers/net/usb/Kconfig:351:config USB_NET_SMSC95XX\ndrivers/net/usb/Kconfig-352-\ttristate \"SMSC LAN95XX based USB 2.0 10/100 ethernet devices\"\n"
} |
| 25/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39107 output: 43 thoughts: 62 Thoughts: Okay, here's my thought process on these kernel configurations, framed as if I'm working through it myself: **Network Driver Configuration Decisions** Alright, let's see, based on the network requirements, `PHYLIB` and `USB_NET_SMSC95XX` are definitely good candidates to enable. Those cover a broad range of PHY and USB network adapter scenarios, respectively. I think that's a solid start. Hmm, but wait a second... isn't the Realtek driver using `CONFIG_REALTEK_MDIO`? I'd better double-check to make sure. I don't want to miss anything crucial. Let me go pull up the `drivers/net/mdio/Kconfig` file and take a closer look at its dependencies and configuration options. Just to be sure I'm not overlooking any relevant Realtek-specific settings, or any dependencies I need to take care of as well. That Realtek driver might bring in other configurations. Better to be safe than sorry. |
| 26/2 | 2026/07/31 01:14 | tool | grepper |
0mArgs:{
"Expression": "config MDIO_REALTEK_RTL9300",
"PathPrefix": "drivers/net/mdio/Kconfig"
}Results: {
"Output": "drivers/net/mdio/Kconfig=166=config MDIO_IPQ8064\n--\ndrivers/net/mdio/Kconfig-173-\ndrivers/net/mdio/Kconfig:174:config MDIO_REALTEK_RTL9300\ndrivers/net/mdio/Kconfig-175-\ttristate \"Realtek RTL9300 MDIO interface support\"\n"
} |
| 27/2 | 2026/07/31 01:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39544 output: 165 thoughts: 993 Thoughts: **Fuzzing Strategy for PHY Core and RTL9300 MDIO Controller** Okay, let's break this down. The driver in question is `MDIO_REALTEK_RTL9300`. I immediately recognize this as a platform driver, specifically for an Realtek SoC, so it won't be directly instantiated in a standard QEMU setup without a specific device tree. The question then becomes, is including `MDIO_REALTEK_RTL9300` in `EnableConfigs` worthwhile? Yes, absolutely. It's the primary target of the patch, and while it might not be directly fuzzed, it doesn't hurt to include it. The prompt specified that vendor-specific PCIe switch, SmartNIC, or GPU drivers are not likely to be emulated. But this `MDIO_REALTEK_RTL9300` is not a PCIe device; it's an MMIO device, a syscon, an SoC block. So even though it's not a PCIe device, the prompt is still applicable if the device is not instantiated. My immediate concern is if syzkaller can reach it. It's unlikely syzkaller can easily fuzz it unless it's instantiated. However, the core PHY changes are definitely reachable, so that's the angle. Thus, I'll set `WorthFuzzing=true`. The core focus will be on the key functions: `phy_attach_direct` and `phy_detach`. I need to confirm if these symbols are exported. Yes, `EXPORT_SYMBOL(phy_attach_direct);` is used, and similarly for `phy_detach`. The patch also modifies `drivers/net/mdio/mdio-realtek-rtl9300.c`. If the Realtek device *were* instantiated somehow, there are functions here that might be interesting: `otto_emdio_probe`, `otto_emdio_notify_phy_attach`, and `otto_emdio_notify_phy_detach`. But the SoC block instantiation is very unlikely, so I will focus on the core PHY functions. I reviewed the changes to `phy_device.c`. The patch refactors `phy_detach` to `__phy_detach` and adds a `notify_bus` parameter. It also calls `notify_phy_attach` within `phy_attach_direct`. If `notify_phy_attach` fails, it calls `__phy_detach(phydev, false)` to rollback. This is a small but functional change in the core PHY subsystem. How often is `phy_attach_direct` called? Not on a hot path. It's called when a network interface is brought up (`ip link set eth0 up`). Safe to focus on. Let's quickly check the config options. `CONFIG_PHYLIB` is tristate. `CONFIG_USB_NET_SMSC95XX` is also tristate. `CONFIG_MDIO_REALTEK_RTL9300` is also tristate. Therefore, here is the strategy: * `WorthFuzzing: true` * `FocusSymbols: ["phy_attach_direct", "phy_detach"]` * `EnableConfigs: ["PHYLIB", "USB_NET_SMSC95XX", "MDIO_REALTEK_RTL9300"]` * `Reasoning: "The patch adds notify_phy_attach and notify_phy_detach callbacks to the core PHY subsystem and implements them in the Realtek RTL9300 MDIO driver. The core PHY changes in phy_attach_direct and phy_detach are reachable via virtual/emulated network devices (e.g., USB SMSC95xx) and should be fuzzed to ensure the new error handling and callback logic is robust."` |
| 28/2 | 2026/07/31 01:15 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"PHYLIB",
"USB_NET_SMSC95XX",
"MDIO_REALTEK_RTL9300"
],
"FocusSymbols": [
"phy_attach_direct",
"phy_detach"
],
"Reasoning": "The patch adds notify_phy_attach and notify_phy_detach callbacks to the core PHY subsystem and implements them in the Realtek RTL9300 MDIO driver. The core PHY changes in phy_attach_direct and phy_detach are reachable via virtual/emulated network devices (e.g., USB SMSC95xx) and should be fuzzed to ensure the new error handling and callback logic is robust.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"PHYLIB",
"USB_NET_SMSC95XX",
"MDIO_REALTEK_RTL9300"
],
"FocusSymbols": [
"phy_attach_direct",
"phy_detach"
],
"Reasoning": "The patch adds notify_phy_attach and notify_phy_detach callbacks to the core PHY subsystem and implements them in the Realtek RTL9300 MDIO driver. The core PHY changes in phy_attach_direct and phy_detach are reachable via virtual/emulated network devices (e.g., USB SMSC95xx) and should be fuzzed to ensure the new error handling and callback logic is robust.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|