AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/31 21:54 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series adds RTL8380/RTL8390 support and MDIO polling handling to the Realtek Otto MDIO driver (drivers/net/mdio/mdio-realtek-rtl9300.c) along with helper notification callbacks in the core PHY device layer (drivers/net/phy/phy_device.c). The driver is an OF-matched platform driver for Realtek RTL83xx/RTL93xx switch SoCs (MACH_REALTEK_RTL) requiring physical hardware that cannot be reached or emulated in virtualized environments like QEMU or GCE. The core PHY additions are no-op hooks unless the underlying MDIO bus driver implements the callbacks, which only this unreachable driver does.",
  "WorthFuzzing": false
}

1/1 2026/08/31 21:54 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 39ccbafaf0a92e183538589df7fd256aa91f1161\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Aug 31 21:54:58 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/Kconfig b/drivers/net/mdio/Kconfig\nindex a05229838cb45..d44278f26fabe 100644\n--- a/drivers/net/mdio/Kconfig\n+++ b/drivers/net/mdio/Kconfig\n@@ -172,11 +172,11 @@ config MDIO_IPQ8064\n \t  interface units of the IPQ8064 SoC\n \n config MDIO_REALTEK_RTL9300\n-\ttristate \"Realtek RTL9300 MDIO interface support\"\n+\ttristate \"Realtek RTL83xx/RTL93xx MDIO interface support\"\n \tdepends on MACH_REALTEK_RTL || COMPILE_TEST\n \thelp\n \t  This driver supports the MDIO interface found in the Realtek\n-\t  RTL9300 family of Ethernet switches with integrated SoC.\n+\t  RTL83xx/RTL93xx family of Ethernet switches with integrated SoC.\n \n config MDIO_REGMAP\n \ttristate\ndiff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c\nindex afd52a1cd7f83..11d078ef941cd 100644\n--- a/drivers/net/mdio/mdio-realtek-rtl9300.c\n+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c\n@@ -35,11 +35,87 @@\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/device.h\u003e\n #include \u003clinux/find.h\u003e\n #include \u003clinux/mdio.h\u003e\n #include \u003clinux/mfd/syscon.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@@ -126,6 +251,7 @@ struct otto_emdio_priv {\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 +268,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 +304,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 +318,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 +360,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 +397,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 +527,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 +567,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 +579,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@@ -378,27 +621,53 @@ static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)\n \tint ret, port;\n \tu32 value;\n \n+\tif (regnum == MII_MMD_CTRL || regnum == MII_MMD_DATA) {\n+\t\tdev_warn_once(\u0026bus-\u003edev,\n+\t\t\t      \"C45 over C22 read access broken due to polling\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n \tport = otto_emdio_phy_to_port(bus, phy_id);\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 \n-static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)\n+static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum,\n+\t\t\t\tu16 value)\n {\n \tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\n \tint ret, port;\n \n+\tif (regnum == MII_MMD_CTRL || regnum == MII_MMD_DATA) {\n+\t\tdev_warn_once(\u0026bus-\u003edev,\n+\t\t\t      \"C45 over C22 write access broken due to polling\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n \tport = otto_emdio_phy_to_port(bus, phy_id);\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 +740,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 +785,51 @@ 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\t/* All subsequent bus operations will fail */\n+\t\tphydev_err(phydev, \"PHY is not mapped to a valid switch port\\n\");\n+\t\treturn port;\n+\t}\n+\n+\t/* \"sync\" page in case of previously failed attachment */\n+\tscoped_guard(mutex, \u0026priv-\u003elock)\n+\t\tpriv-\u003epage[port] = 0;\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 mii_bus *bus = phydev-\u003emdio.bus;\n+\tstruct otto_emdio_priv *priv;\n+\tint port;\n+\n+\tpriv = otto_emdio_bus_to_priv(phydev-\u003emdio.bus);\n+\tport = otto_emdio_phy_to_port(phydev-\u003emdio.bus, phydev-\u003emdio.addr);\n+\n+\tif (port \u003c 0)\n+\t\treturn;\n+\n+\t/* \"sync\" page for next attachment */\n+\tscoped_guard(mutex, \u0026priv-\u003elock)\n+\t\tpriv-\u003epage[port] = 0;\n+\n+\tif (otto_emdio_set_port_polling(priv, port, false))\n+\t\tdev_err(bus-\u003eparent, \"failed to disable polling for port %d\\n\", port);\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@@ -528,14 +851,14 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv\n \t\treturn -ENOMEM;\n \n \tbus-\u003ename = \"Realtek Switch MDIO Bus\";\n-\tif (priv-\u003esmi_bus_is_c45[mdio_bus]) {\n-\t\tbus-\u003eread_c45 = otto_emdio_read_c45;\n-\t\tbus-\u003ewrite_c45 = otto_emdio_write_c45;\n-\t} else {\n-\t\tbus-\u003eread = otto_emdio_read_c22;\n-\t\tbus-\u003ewrite = otto_emdio_write_c22;\n-\t}\n+\tbus-\u003eread_c45 = otto_emdio_read_c45;\n+\tbus-\u003ewrite_c45 = otto_emdio_write_c45;\n+\tbus-\u003eread = otto_emdio_read_c22;\n+\tbus-\u003ewrite = otto_emdio_write_c22;\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@@ -652,6 +975,19 @@ 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@@ -671,6 +1007,10 @@ static int otto_emdio_probe(struct platform_device *pdev)\n \tif (IS_ERR(priv-\u003eregmap))\n \t\treturn PTR_ERR(priv-\u003eregmap);\n \n+\terr = otto_emdio_init_polling(priv);\n+\tif (err)\n+\t\treturn err;\n+\n \tplatform_set_drvdata(pdev, priv);\n \n \terr = otto_emdio_map_ports(dev);\n@@ -696,6 +1036,51 @@ static int otto_emdio_probe(struct platform_device *pdev)\n \treturn 0;\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 +1096,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 +1122,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 +1131,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{}\n@@ -755,10 +1144,11 @@ static struct platform_driver otto_emdio_driver = {\n \t.driver = {\n \t\t.name = \"mdio-rtl9300\",\n \t\t.of_match_table = otto_emdio_ids,\n+\t\t.suppress_bind_attrs = true,\n \t},\n };\n \n module_platform_driver(otto_emdio_driver);\n \n-MODULE_DESCRIPTION(\"RTL9300 MDIO driver\");\n+MODULE_DESCRIPTION(\"RTL83xx/RTL93xx MDIO driver\");\n MODULE_LICENSE(\"GPL\");\ndiff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c\nindex 94b2e85e00a37..de59d864b0608 100644\n--- a/drivers/net/phy/phy_device.c\n+++ b/drivers/net/phy/phy_device.c\n@@ -1734,6 +1734,104 @@ static bool phy_drv_supports_irq(const struct phy_driver *phydrv)\n \treturn phydrv-\u003econfig_intr \u0026\u0026 phydrv-\u003ehandle_interrupt;\n }\n \n+/**\n+ * phy_detach_internal - detach a PHY device from its network device\n+ * @phydev: target phy_device struct\n+ * @notify_bus: whether to notify the MDIO bus about the PHY detach\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+static void phy_detach_internal(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+\tphy_detach_internal(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 +1974,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 +1994,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/* phy_detach_internal() does all of the cleanup below */\n+\tphy_detach_internal(phydev, false);\n \treturn err;\n \n error_module_put:\n@@ -1906,86 +2010,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 5f8d65868e0ff..3d8afe6b7f1c5 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/08/31 21:55 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series adds RTL8380/RTL8390 support and MDIO polling handling to the Realtek Otto MDIO driver (drivers/net/mdio/mdio-realtek-rtl9300.c) along with helper notification callbacks in the core PHY device layer (drivers/net/phy/phy_device.c). The driver is an OF-matched platform driver for Realtek RTL83xx/RTL93xx switch SoCs (MACH_REALTEK_RTL) requiring physical hardware that cannot be reached or emulated in virtualized environments like QEMU or GCE. The core PHY additions are no-op hooks unless the underlying MDIO bus driver implements the callbacks, which only this unreachable driver does.",
  "WorthFuzzing": false
}

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 39ccbafaf0a92e183538589df7fd256aa91f1161
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 31 21:54:58 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/Kconfig b/drivers/net/mdio/Kconfig
index a05229838cb45..d44278f26fabe 100644
--- a/drivers/net/mdio/Kconfig
+++ b/drivers/net/mdio/Kconfig
@@ -172,11 +172,11 @@ config MDIO_IPQ8064
 	  interface units of the IPQ8064 SoC
 
 config MDIO_REALTEK_RTL9300
-	tristate "Realtek RTL9300 MDIO interface support"
+	tristate "Realtek RTL83xx/RTL93xx MDIO interface support"
 	depends on MACH_REALTEK_RTL || COMPILE_TEST
 	help
 	  This driver supports the MDIO interface found in the Realtek
-	  RTL9300 family of Ethernet switches with integrated SoC.
+	  RTL83xx/RTL93xx family of Ethernet switches with integrated SoC.
 
 config MDIO_REGMAP
 	tristate
diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c
index afd52a1cd7f83..11d078ef941cd 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -35,11 +35,87 @@
  *
  * 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/device.h>
 #include <linux/find.h>
 #include <linux/mdio.h>
 #include <linux/mfd/syscon.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
@@ -126,6 +251,7 @@ struct otto_emdio_priv {
 	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 +268,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 +304,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 +318,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 +360,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 +397,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 +527,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 +567,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 +579,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)),
@@ -378,27 +621,53 @@ static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
 	int ret, port;
 	u32 value;
 
+	if (regnum == MII_MMD_CTRL || regnum == MII_MMD_DATA) {
+		dev_warn_once(&bus->dev,
+			      "C45 over C22 read access broken due to polling\n");
+		return -EOPNOTSUPP;
+	}
+
 	port = otto_emdio_phy_to_port(bus, phy_id);
 	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;
 }
 
-static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value)
+static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum,
+				u16 value)
 {
 	struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);
 	int ret, port;
 
+	if (regnum == MII_MMD_CTRL || regnum == MII_MMD_DATA) {
+		dev_warn_once(&bus->dev,
+			      "C45 over C22 write access broken due to polling\n");
+		return -EOPNOTSUPP;
+	}
+
 	port = otto_emdio_phy_to_port(bus, phy_id);
 	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 +740,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 +785,51 @@ 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) {
+		/* All subsequent bus operations will fail */
+		phydev_err(phydev, "PHY is not mapped to a valid switch port\n");
+		return port;
+	}
+
+	/* "sync" page in case of previously failed attachment */
+	scoped_guard(mutex, &priv->lock)
+		priv->page[port] = 0;
+
+	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 mii_bus *bus = phydev->mdio.bus;
+	struct otto_emdio_priv *priv;
+	int port;
+
+	priv = otto_emdio_bus_to_priv(phydev->mdio.bus);
+	port = otto_emdio_phy_to_port(phydev->mdio.bus, phydev->mdio.addr);
+
+	if (port < 0)
+		return;
+
+	/* "sync" page for next attachment */
+	scoped_guard(mutex, &priv->lock)
+		priv->page[port] = 0;
+
+	if (otto_emdio_set_port_polling(priv, port, false))
+		dev_err(bus->parent, "failed to disable polling for port %d\n", port);
+}
+
 static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,
 				 struct fwnode_handle *node)
 {
@@ -528,14 +851,14 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv
 		return -ENOMEM;
 
 	bus->name = "Realtek Switch MDIO Bus";
-	if (priv->smi_bus_is_c45[mdio_bus]) {
-		bus->read_c45 = otto_emdio_read_c45;
-		bus->write_c45 = otto_emdio_write_c45;
-	} else {
-		bus->read = otto_emdio_read_c22;
-		bus->write = otto_emdio_write_c22;
-	}
+	bus->read_c45 = otto_emdio_read_c45;
+	bus->write_c45 = otto_emdio_write_c45;
+	bus->read = otto_emdio_read_c22;
+	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;
@@ -652,6 +975,19 @@ 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;
@@ -671,6 +1007,10 @@ static int otto_emdio_probe(struct platform_device *pdev)
 	if (IS_ERR(priv->regmap))
 		return PTR_ERR(priv->regmap);
 
+	err = otto_emdio_init_polling(priv);
+	if (err)
+		return err;
+
 	platform_set_drvdata(pdev, priv);
 
 	err = otto_emdio_map_ports(dev);
@@ -696,6 +1036,51 @@ static int otto_emdio_probe(struct platform_device *pdev)
 	return 0;
 }
 
+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 +1096,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 +1122,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 +1131,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 },
 	{}
@@ -755,10 +1144,11 @@ static struct platform_driver otto_emdio_driver = {
 	.driver = {
 		.name = "mdio-rtl9300",
 		.of_match_table = otto_emdio_ids,
+		.suppress_bind_attrs = true,
 	},
 };
 
 module_platform_driver(otto_emdio_driver);
 
-MODULE_DESCRIPTION("RTL9300 MDIO driver");
+MODULE_DESCRIPTION("RTL83xx/RTL93xx MDIO driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 94b2e85e00a37..de59d864b0608 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1734,6 +1734,104 @@ static bool phy_drv_supports_irq(const struct phy_driver *phydrv)
 	return phydrv->config_intr && phydrv->handle_interrupt;
 }
 
+/**
+ * phy_detach_internal - detach a PHY device from its network device
+ * @phydev: target phy_device struct
+ * @notify_bus: whether to notify the MDIO bus about the PHY detach
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
+ */
+static void phy_detach_internal(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_internal(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 +1974,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 +1994,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);
+	/* phy_detach_internal() does all of the cleanup below */
+	phy_detach_internal(phydev, false);
 	return err;
 
 error_module_put:
@@ -1906,86 +2010,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 5f8d65868e0ff..3d8afe6b7f1c5 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/08/31 21:55 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 16537
							output: 22
							thoughts: 607
						

Thoughts:
**Analyzing the PHY Subsystem Patch**

Okay, let's break down this patch series. The files impacted are: `Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml`, `drivers/net/mdio/Kconfig`, `drivers/net/mdio/mdio-realtek-rtl9300.c`, `drivers/net/phy/phy_device.c`, and `include/linux/phy.h`. My initial focus is on the core PHY code changes, specifically in `phy_device.c` and `include/linux/phy.h`.

It looks like we're introducing callbacks, `notify_phy_attach` and `notify_phy_detach`, to the `struct mii_bus`. This is interesting; `phy_attach_direct()` now calls `bus->notify_phy_attach(phydev)`, and `phy_detach()` calls `bus->notify_phy_detach(phydev)` via the newly refactored `phy_detach_internal()`. This is a clear signal that there's a new interaction layer for PHY attach/detach events.

Now, peeking into `drivers/net/mdio/mdio-realtek-rtl9300.c`, I see support for RTL8380 and RTL8390 MDIO controllers, along with implementations for `otto_emdio_notify_phy_attach` and `otto_emdio_notify_phy_detach`. They also added polling control logic for Realtek switches. This looks specific to these Realtek SoCs.

The critical question is, does this new callback mechanism exist only for `mdio-realtek-rtl9300.c`? Is this a driver that's only going to work on Realtek based systems, or can it be used elsewhere?

Diving deeper, I see that `phy_attach_direct` and `phy_detach` are central PHY subsystem functions. These functions are used across networking. So, this refactoring *must* be core, and the callbacks are probably added to enable broader support.

I need to confirm if other drivers will use these new callbacks. I need to figure out if other drivers set these callbacks. If no other driver sets `bus->notify_phy_attach` besides the Realtek driver, then this entire change is only useful for Realtek systems.

Let's check if `mdio-realtek-rtl9300.c` is even possible to test outside a specific SoC. I'll search for `CONFIG_MDIO_REALTEK_RTL9300` or `MACH_REALTEK_RTL` to verify if I can run this with QEMU or GCE.



4/2 2026/08/31 21:55 tool grepper
0m Args:
{
  "Expression": "MACH_REALTEK_RTL"
}

Results:
{
  "Output": "arch/mips/Kconfig=681=config RALINK\n--\narch/mips/Kconfig-698-\narch/mips/Kconfig:699:config MACH_REALTEK_RTL\narch/mips/Kconfig-700-\tbool \"Realtek RTL838x/RTL839x based machines\"\n--\narch/mips/boot/dts/realtek/Makefile-1-# SPDX-License-Identifier: GPL-2.0\narch/mips/boot/dts/realtek/Makefile:2:dtb-$(CONFIG_MACH_REALTEK_RTL)\t+= cisco_sg220-26.dtb\narch/mips/boot/dts/realtek/Makefile:3:dtb-$(CONFIG_MACH_REALTEK_RTL)\t+= cameo-rtl9302c-2x-rtl8224-2xge.dtb\n--\narch/mips/generic/Makefile=15=obj-$(CONFIG_VIRT_BOARD_RANCHU)\t\t+= board-ranchu.o\narch/mips/generic/Makefile:16:obj-$(CONFIG_MACH_REALTEK_RTL)\t\t+= board-realtek.o\n--\ndrivers/gpio/Kconfig=625=config GPIO_REALTEK_OTTO\ndrivers/gpio/Kconfig-626-\ttristate \"Realtek Otto GPIO support\"\ndrivers/gpio/Kconfig:627:\tdepends on MACH_REALTEK_RTL || COMPILE_TEST\ndrivers/gpio/Kconfig:628:\tdefault MACH_REALTEK_RTL\ndrivers/gpio/Kconfig-629-\tselect GPIO_GENERIC\n--\ndrivers/i2c/busses/Kconfig=1103=config I2C_RTL9300\ndrivers/i2c/busses/Kconfig-1104-\ttristate \"Realtek RTL9300 I2C controller\"\ndrivers/i2c/busses/Kconfig:1105:\tdepends on MACH_REALTEK_RTL || COMPILE_TEST\ndrivers/i2c/busses/Kconfig-1106-\thelp\n--\ndrivers/irqchip/Makefile=130=obj-$(CONFIG_SL28CPLD_INTC)\t\t+= irq-sl28cpld.o\ndrivers/irqchip/Makefile:131:obj-$(CONFIG_MACH_REALTEK_RTL)\t\t+= irq-realtek-rtl.o\ndrivers/irqchip/Makefile-132-obj-$(CONFIG_WPCM450_AIC)\t\t+= irq-wpcm450-aic.o\n--\ndrivers/mtd/nand/Kconfig=64=config MTD_NAND_ECC_REALTEK\n--\ndrivers/mtd/nand/Kconfig-66-        depends on HAS_IOMEM \u0026\u0026 HAS_DMA\ndrivers/mtd/nand/Kconfig:67:        depends on MACH_REALTEK_RTL || COMPILE_TEST\ndrivers/mtd/nand/Kconfig-68-        select MTD_NAND_ECC\n--\ndrivers/net/mdio/Kconfig=174=config MDIO_REALTEK_RTL9300\ndrivers/net/mdio/Kconfig-175-\ttristate \"Realtek RTL83xx/RTL93xx MDIO interface support\"\ndrivers/net/mdio/Kconfig:176:\tdepends on MACH_REALTEK_RTL || COMPILE_TEST\ndrivers/net/mdio/Kconfig-177-\thelp\n--\ndrivers/spi/Kconfig=916=config SPI_REALTEK_SNAND\ndrivers/spi/Kconfig-917-\ttristate \"Realtek SPI-NAND Flash Controller\"\ndrivers/spi/Kconfig:918:\tdepends on MACH_REALTEK_RTL || COMPILE_TEST\ndrivers/spi/Kconfig-919-\tselect REGMAP\n--\ndrivers/spi/Makefile=131=obj-$(CONFIG_SPI_RB4XX)\t\t\t+= spi-rb4xx.o\ndrivers/spi/Makefile:132:obj-$(CONFIG_MACH_REALTEK_RTL)\t\t+= spi-realtek-rtl.o\ndrivers/spi/Makefile-133-obj-$(CONFIG_SPI_REALTEK_SNAND)\t\t+= spi-realtek-rtl-snand.o\n--\ndrivers/watchdog/Kconfig=1113=config REALTEK_OTTO_WDT\ndrivers/watchdog/Kconfig-1114-\ttristate \"Realtek Otto MIPS watchdog support\"\ndrivers/watchdog/Kconfig:1115:\tdepends on MACH_REALTEK_RTL || COMPILE_TEST\ndrivers/watchdog/Kconfig-1116-\tdepends on COMMON_CLK\n--\ndrivers/watchdog/Kconfig-1118-\tselect REGMAP_MMIO\ndrivers/watchdog/Kconfig:1119:\tdefault MACH_REALTEK_RTL\ndrivers/watchdog/Kconfig-1120-\thelp\n"
}

5/2 2026/08/31 21:55 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 19015
							output: 18
							thoughts: 511
						

Thoughts:
**Analyzing PHY Detachment and Attachment in the Realtek RTL9300 context**

Okay, so `MDIO_REALTEK_RTL9300` relates to the MIPS Realtek RTL838x/RTL839x/RTL930x SoCs, specifically for the embedded `MACH_REALTEK_RTL` boards. Got it. But hold on, the core PHY changes… That's the key. Let's delve into what's altered within `drivers/net/phy/phy_device.c`.

Right, `phy_detach_internal` is the critical function. It handles the actual detachment of the PHY device from its network device and driver. The added `notify_bus` parameter is interesting; it decides whether to tell the MDIO bus about the detachment. And hey, there's `phy_detach`, a wrapper that always notifies the bus. Okay, `notify_phy_detach` it is.

The code shows that `phy_detach_internal` will call a bus specific `notify_phy_detach` callback if `notify_bus` is set to true and the bus has implemented a specific callback. The same is true for `phy_attach_internal` which calls `notify_phy_attach` if the bus has a callback defined. Makes sense.

But here's the burning question: Is either `notify_phy_attach` or `notify_phy_detach` actually *used* anywhere else? My gut tells me this could be a new, or underutilized, interface. Time to run a `grep` search across the entire codebase. This should give me a quick answer as to whether any other drivers leverage these new callback functions, or if this is isolated to the Realtek implementation.



6/2 2026/08/31 21:55 tool grepper
0m Args:
{
  "Expression": "notify_phy_"
}

Results:
{
  "Output": "Documentation/scsi/libsas.rst=189=The event interface::\n--\nDocumentation/scsi/libsas.rst-192-\tvoid sas_notify_port_event(struct sas_phy *, enum port_event, gfp_t);\nDocumentation/scsi/libsas.rst:193:\tvoid sas_notify_phy_event(struct sas_phy *, enum phy_event, gfp_t);\nDocumentation/scsi/libsas.rst-194-\n--\nDocumentation/translations/zh_CN/scsi/libsas.rst=170=num_phys\n--\nDocumentation/translations/zh_CN/scsi/libsas.rst-179-\tvoid sas_notify_port_event(struct sas_phy *, enum port_event, gfp_t);\nDocumentation/translations/zh_CN/scsi/libsas.rst:180:\tvoid sas_notify_phy_event(struct sas_phy *, enum phy_event, gfp_t);\nDocumentation/translations/zh_CN/scsi/libsas.rst-181-\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c=772=static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-787-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:788:static int otto_emdio_notify_phy_attach(struct phy_device *phydev)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-789-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-812-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:813:static void otto_emdio_notify_phy_detach(struct phy_device *phydev)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-814-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c=833=static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-858-\tbus-\u003eparent = dev;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:859:\tbus-\u003enotify_phy_attach = otto_emdio_notify_phy_attach;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:860:\tbus-\u003enotify_phy_detach = otto_emdio_notify_phy_detach;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-861-\n--\ndrivers/net/phy/phy_device.c=1745=static void phy_detach_internal(struct phy_device *phydev, bool notify_bus)\n--\ndrivers/net/phy/phy_device.c-1767-\ndrivers/net/phy/phy_device.c:1768:\tif (notify_bus \u0026\u0026 phydev-\u003emdio.bus-\u003enotify_phy_detach)\ndrivers/net/phy/phy_device.c:1769:\t\tphydev-\u003emdio.bus-\u003enotify_phy_detach(phydev);\ndrivers/net/phy/phy_device.c-1770-\n--\ndrivers/net/phy/phy_device.c=1850=int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,\n--\ndrivers/net/phy/phy_device.c-1976-\ndrivers/net/phy/phy_device.c:1977:\tif (phydev-\u003emdio.bus-\u003enotify_phy_attach) {\ndrivers/net/phy/phy_device.c:1978:\t\terr = phydev-\u003emdio.bus-\u003enotify_phy_attach(phydev);\ndrivers/net/phy/phy_device.c-1979-\t\tif (err)\n--\ndrivers/scsi/aic94xx/aic94xx_scb.c=67=static void asd_phy_event_tasklet(struct asd_ascb *ascb,\n--\ndrivers/scsi/aic94xx/aic94xx_scb.c-82-\t\tsas_phy_disconnected(\u0026phy-\u003esas_phy);\ndrivers/scsi/aic94xx/aic94xx_scb.c:83:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_LOSS_OF_SIGNAL,\ndrivers/scsi/aic94xx/aic94xx_scb.c-84-\t\t\t\t     GFP_ATOMIC);\n--\ndrivers/scsi/aic94xx/aic94xx_scb.c-91-\t\t\t    phy_id, phy-\u003esas_phy.linkrate, phy-\u003esas_phy.iproto);\ndrivers/scsi/aic94xx/aic94xx_scb.c:92:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_OOB_DONE, GFP_ATOMIC);\ndrivers/scsi/aic94xx/aic94xx_scb.c-93-\t\tbreak;\n--\ndrivers/scsi/aic94xx/aic94xx_scb.c-96-\t\tasd_turn_led(asd_ha, phy_id, 1);\ndrivers/scsi/aic94xx/aic94xx_scb.c:97:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_SPINUP_HOLD,\ndrivers/scsi/aic94xx/aic94xx_scb.c-98-\t\t\t\t     GFP_ATOMIC);\n--\ndrivers/scsi/aic94xx/aic94xx_scb.c-105-\t\tsas_phy_disconnected(\u0026phy-\u003esas_phy);\ndrivers/scsi/aic94xx/aic94xx_scb.c:106:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_OOB_ERROR, GFP_ATOMIC);\ndrivers/scsi/aic94xx/aic94xx_scb.c-107-\t\tbreak;\n--\ndrivers/scsi/hisi_sas/hisi_sas.h=683=extern void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no);\ndrivers/scsi/hisi_sas/hisi_sas.h:684:extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy,\ndrivers/scsi/hisi_sas/hisi_sas.h-685-\t\t\t\tenum hisi_sas_phy_event event);\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-657-\ndrivers/scsi/hisi_sas/hisi_sas_main.c:658:\tsas_notify_phy_event(sas_phy, PHYE_OOB_DONE, gfp_flags);\ndrivers/scsi/hisi_sas/hisi_sas_main.c-659-\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c=939=static void hisi_sas_phyup_work_common(struct work_struct *work,\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-962-\t\t\tset_bit(SAS_DEV_GONE, \u0026port_dev-\u003estate);\ndrivers/scsi/hisi_sas/hisi_sas_main.c:963:\t\t\thisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);\ndrivers/scsi/hisi_sas/hisi_sas_main.c-964-\t\t\treturn;\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c=999=static const work_func_t hisi_sas_phye_fns[HISI_PHYES_NUM] = {\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1004-\ndrivers/scsi/hisi_sas/hisi_sas_main.c:1005:bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy,\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1006-\t\t\t\tenum hisi_sas_phy_event event)\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1014-}\ndrivers/scsi/hisi_sas/hisi_sas_main.c:1015:EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event);\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1016-\ndrivers/scsi/hisi_sas/hisi_sas_main.c=1017=static void hisi_sas_wait_phyup_timedout(struct timer_list *t)\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1024-\tdev_warn(dev, \"phy%d wait phyup timeout, issuing link reset\\n\", phy_no);\ndrivers/scsi/hisi_sas/hisi_sas_main.c:1025:\thisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1026-}\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c=1437=static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 state)\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1473-\t\t\tif (state \u0026 BIT(phy_no))\ndrivers/scsi/hisi_sas/hisi_sas_main.c:1474:\t\t\t\thisi_sas_notify_phy_event(phy,\ndrivers/scsi/hisi_sas/hisi_sas_main.c-1475-\t\t\t\t\t\t\t  HISI_PHYE_LINK_RESET);\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c=2102=void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy,\n--\ndrivers/scsi/hisi_sas/hisi_sas_main.c-2121-\t\t/* Phy down and not ready */\ndrivers/scsi/hisi_sas/hisi_sas_main.c:2122:\t\tsas_notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL, gfp_flags);\ndrivers/scsi/hisi_sas/hisi_sas_main.c-2123-\t\tsas_phy_disconnected(sas_phy);\n--\ndrivers/scsi/hisi_sas/hisi_sas_v1_hw.c=1315=static irqreturn_t int_phyup_v1_hw(int irq_no, void *p)\n--\ndrivers/scsi/hisi_sas/hisi_sas_v1_hw.c-1376-\t\t\tSAS_PROTOCOL_SMP;\ndrivers/scsi/hisi_sas/hisi_sas_v1_hw.c:1377:\thisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);\ndrivers/scsi/hisi_sas/hisi_sas_v1_hw.c-1378-end:\n--\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c=2646=static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)\n--\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c-2707-\t}\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c:2708:\thisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c-2709-end:\n--\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c=2864=static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p)\n--\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c-2911-\t\t\t\t\t phy_no);\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c:2912:\t\t\t\thisi_sas_notify_phy_event(phy,\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c-2913-\t\t\t\t\t\tHISI_PHYE_LINK_RESET);\n--\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c=3198=static irqreturn_t sata_int_v2_hw(int irq_no, void *p)\n--\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c-3235-\t\t\t fis-\u003estatus);\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c:3236:\t\thisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c-3237-\t\tres = IRQ_NONE;\n--\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c-3281-\tphy-\u003eidentify.target_port_protocols = SAS_PROTOCOL_SATA;\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c:3282:\thisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);\ndrivers/scsi/hisi_sas/hisi_sas_v2_hw.c-3283-\n--\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c=1582=static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)\n--\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1621-\t\t\t\t phy_no, fis-\u003estatus);\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c:1622:\t\t\thisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1623-\t\t\tres = IRQ_NONE;\n--\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1678-\tpm_runtime_get_noresume(dev);\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c:1679:\tres = hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP_PM);\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1680-\tif (!res)\n--\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c=1887=static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no)\n--\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1906-\t\tdev_warn(dev, \"phy%d identify timeout\\n\", phy_no);\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c:1907:\t\thisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1908-\t}\n--\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1916-\t\tif (reg_value \u0026 BIT(LINK_RESET_TIMEOUT_OFF))\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c:1917:\t\t\thisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1918-\t}\n--\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1948-\t\tif (rc)\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c:1949:\t\t\thisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);\ndrivers/scsi/hisi_sas/hisi_sas_v3_hw.c-1950-\t}\n--\ndrivers/scsi/isci/port.c=240=static void isci_port_link_down(struct isci_host *isci_host,\n--\ndrivers/scsi/isci/port.c-273-\tsas_phy_disconnected(\u0026isci_phy-\u003esas_phy);\ndrivers/scsi/isci/port.c:274:\tsas_notify_phy_event(\u0026isci_phy-\u003esas_phy,\ndrivers/scsi/isci/port.c-275-\t\t\t     PHYE_LOSS_OF_SIGNAL, GFP_ATOMIC);\n--\ndrivers/scsi/libsas/sas_event.c=189=EXPORT_SYMBOL_GPL(sas_notify_port_event);\ndrivers/scsi/libsas/sas_event.c-190-\ndrivers/scsi/libsas/sas_event.c:191:void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,\ndrivers/scsi/libsas/sas_event.c-192-\t\t\t  gfp_t gfp_flags)\n--\ndrivers/scsi/libsas/sas_event.c-215-}\ndrivers/scsi/libsas/sas_event.c:216:EXPORT_SYMBOL_GPL(sas_notify_phy_event);\n--\ndrivers/scsi/libsas/sas_init.c=413=void sas_resume_ha(struct sas_ha_struct *ha)\n--\ndrivers/scsi/libsas/sas_init.c-450-\t\t\tdev_warn(\u0026phy-\u003ephy-\u003edev, \"resume timeout\\n\");\ndrivers/scsi/libsas/sas_init.c:451:\t\t\tsas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT,\ndrivers/scsi/libsas/sas_init.c-452-\t\t\t\t\t     GFP_KERNEL);\n--\ndrivers/scsi/libsas/sas_init.c=638=struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy,\n--\ndrivers/scsi/libsas/sas_init.c-656-\t\t\t\t\t  phy-\u003eid);\ndrivers/scsi/libsas/sas_init.c:657:\t\t\t\tsas_notify_phy_event(phy, PHYE_SHUTDOWN,\ndrivers/scsi/libsas/sas_init.c-658-\t\t\t\t\t\t     gfp_flags);\n--\ndrivers/scsi/mvsas/mv_sas.c=210=static void mvs_bytes_dmaed(struct mvs_info *mvi, int i, gfp_t gfp_flags)\n--\ndrivers/scsi/mvsas/mv_sas.c-222-\ndrivers/scsi/mvsas/mv_sas.c:223:\tsas_notify_phy_event(sas_phy, PHYE_OOB_DONE, gfp_flags);\ndrivers/scsi/mvsas/mv_sas.c-224-\n--\ndrivers/scsi/mvsas/mv_sas.c=1694=static void mvs_work_queue(struct work_struct *work)\n--\ndrivers/scsi/mvsas/mv_sas.c-1714-\t\t\t\tmvs_phy_disconnected(phy);\ndrivers/scsi/mvsas/mv_sas.c:1715:\t\t\t\tsas_notify_phy_event(sas_phy,\ndrivers/scsi/mvsas/mv_sas.c-1716-\t\t\t\t\tPHYE_LOSS_OF_SIGNAL, GFP_ATOMIC);\n--\ndrivers/scsi/pm8001/pm8001_hwi.c=3105=hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)\n--\ndrivers/scsi/pm8001/pm8001_hwi.c-3163-\tphy-\u003esas_phy.oob_mode = SAS_OOB_MODE;\ndrivers/scsi/pm8001/pm8001_hwi.c:3164:\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_OOB_DONE, GFP_ATOMIC);\ndrivers/scsi/pm8001/pm8001_hwi.c-3165-\tspin_lock_irqsave(\u0026phy-\u003esas_phy.frame_rcvd_lock, flags);\n--\ndrivers/scsi/pm8001/pm8001_hwi.c=3182=hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)\n--\ndrivers/scsi/pm8001/pm8001_hwi.c-3208-\tphy-\u003esas_phy.oob_mode = SATA_OOB_MODE;\ndrivers/scsi/pm8001/pm8001_hwi.c:3209:\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_OOB_DONE, GFP_ATOMIC);\ndrivers/scsi/pm8001/pm8001_hwi.c-3210-\tspin_lock_irqsave(\u0026phy-\u003esas_phy.frame_rcvd_lock, flags);\n--\ndrivers/scsi/pm8001/pm8001_hwi.c=3501=static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)\n--\ndrivers/scsi/pm8001/pm8001_hwi.c-3549-\t\tpm8001_dbg(pm8001_ha, MSG, \"HW_EVENT_SATA_SPINUP_HOLD\\n\");\ndrivers/scsi/pm8001/pm8001_hwi.c:3550:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_SPINUP_HOLD,\ndrivers/scsi/pm8001/pm8001_hwi.c-3551-\t\t\tGFP_ATOMIC);\n--\ndrivers/scsi/pm8001/pm8001_hwi.c-3554-\t\tpm8001_dbg(pm8001_ha, MSG, \"HW_EVENT_PHY_DOWN\\n\");\ndrivers/scsi/pm8001/pm8001_hwi.c:3555:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_LOSS_OF_SIGNAL,\ndrivers/scsi/pm8001/pm8001_hwi.c-3556-\t\t\tGFP_ATOMIC);\n--\ndrivers/scsi/pm8001/pm8001_hwi.c-3583-\t\tphy-\u003ephy_attached = 0;\ndrivers/scsi/pm8001/pm8001_hwi.c:3584:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_OOB_ERROR, GFP_ATOMIC);\ndrivers/scsi/pm8001/pm8001_hwi.c-3585-\t\tbreak;\n--\ndrivers/scsi/pm8001/pm8001_sas.c=225=int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,\n--\ndrivers/scsi/pm8001/pm8001_sas.c-292-\t\t\t\tsas_phy_disconnected(\u0026phy-\u003esas_phy);\ndrivers/scsi/pm8001/pm8001_sas.c:293:\t\t\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy,\ndrivers/scsi/pm8001/pm8001_sas.c-294-\t\t\t\t\tPHYE_LOSS_OF_SIGNAL, GFP_KERNEL);\n--\ndrivers/scsi/pm8001/pm8001_sas.c-300-\t\t\t\tsas_phy_disconnected(\u0026phy-\u003esas_phy);\ndrivers/scsi/pm8001/pm8001_sas.c:301:\t\t\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy,\ndrivers/scsi/pm8001/pm8001_sas.c-302-\t\t\t\t\tPHYE_LOSS_OF_SIGNAL, GFP_KERNEL);\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c=3186=hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c-3247-\tphy-\u003esas_phy.oob_mode = SAS_OOB_MODE;\ndrivers/scsi/pm8001/pm80xx_hwi.c:3248:\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_OOB_DONE, GFP_ATOMIC);\ndrivers/scsi/pm8001/pm80xx_hwi.c-3249-\tspin_lock_irqsave(\u0026phy-\u003esas_phy.frame_rcvd_lock, flags);\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c=3266=hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c-3296-\tphy-\u003esas_phy.oob_mode = SATA_OOB_MODE;\ndrivers/scsi/pm8001/pm80xx_hwi.c:3297:\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_OOB_DONE, GFP_ATOMIC);\ndrivers/scsi/pm8001/pm80xx_hwi.c-3298-\tspin_lock_irqsave(\u0026phy-\u003esas_phy.frame_rcvd_lock, flags);\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c=3315=hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c-3386-\tif (port_sata \u0026\u0026 (portstate != PORT_IN_RESET))\ndrivers/scsi/pm8001/pm80xx_hwi.c:3387:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_LOSS_OF_SIGNAL,\ndrivers/scsi/pm8001/pm80xx_hwi.c-3388-\t\t\t\tGFP_ATOMIC);\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c=3454=static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c-3492-\t\t\t   phy_id, port_id);\ndrivers/scsi/pm8001/pm80xx_hwi.c:3493:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_SPINUP_HOLD,\ndrivers/scsi/pm8001/pm80xx_hwi.c-3494-\t\t\tGFP_ATOMIC);\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c-3526-\t\tphy-\u003ephy_attached = 0;\ndrivers/scsi/pm8001/pm80xx_hwi.c:3527:\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy, PHYE_OOB_ERROR, GFP_ATOMIC);\ndrivers/scsi/pm8001/pm80xx_hwi.c-3528-\t\tbreak;\n--\ndrivers/scsi/pm8001/pm80xx_hwi.c-3642-\t\t\t\tphy = \u0026pm8001_ha-\u003ephy[i];\ndrivers/scsi/pm8001/pm80xx_hwi.c:3643:\t\t\t\tsas_notify_phy_event(\u0026phy-\u003esas_phy,\ndrivers/scsi/pm8001/pm80xx_hwi.c-3644-\t\t\t\t\tPHYE_LOSS_OF_SIGNAL, GFP_ATOMIC);\n--\ninclude/linux/phy.h=363=struct mii_bus {\n--\ninclude/linux/phy.h-379-\t/**\ninclude/linux/phy.h:380:\t * @notify_phy_attach: Perform post-attach handling for MDIO bus\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\n--\ninclude/linux/phy.h-387-\t */\ninclude/linux/phy.h:388:\tint (*notify_phy_attach)(struct phy_device *phydev);\ninclude/linux/phy.h-389-\t/**\ninclude/linux/phy.h:390:\t * @notify_phy_detach: Perform pre-detach handling for MDIO bus\ninclude/linux/phy.h:391:\t * drivers. Optional and independent of @notify_phy_attach. Called\ninclude/linux/phy.h-392-\t * in phy_detach() right after phy_suspend(). Runs in process context,\n--\ninclude/linux/phy.h-395-\t */\ninclude/linux/phy.h:396:\tvoid (*notify_phy_detach)(struct phy_device *phydev);\ninclude/linux/phy.h-397-\n--\ninclude/scsi/libsas.h=729=void sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,\ninclude/scsi/libsas.h-730-\t\t\t   gfp_t gfp_flags);\ninclude/scsi/libsas.h:731:void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,\ninclude/scsi/libsas.h-732-\t\t\t   gfp_t gfp_flags);\n"
}

7/2 2026/08/31 21:55 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 27997
							output: 28
							thoughts: 164
						

Thoughts:
**Investigating PHY Attachment/Detachment Notifications**

Okay, so I'm looking at these `notify_phy_attach` and `notify_phy_detach` functions. I know they're defined in `include/linux/phy.h` and used in `drivers/net/phy/phy_device.c`. That's the core PHY framework, good. Now, I need to understand who's actually *using* them.

The trace leads me to `drivers/net/mdio/mdio-realtek-rtl9300.c`. Ah, right, the Realtek MDIO driver. But... is this specific to just the Realtek MIPS hardware? That's what I need to figure out. I can't just assume it's broadly applicable.

I've got to determine if this driver is tightly coupled to a particular SoC, or if it's more generic. Is it bound to something specific, or is there a broader context I'm missing? I need to dig into the `mdio-realtek-rtl9300.c` code itself and see what it's doing, how it's initialized, and what hardware it's expecting to see. Time to dive in.



8/2 2026/08/31 21:55 tool grepper
0m Args:
{
  "Expression": "otto_emdio",
  "PathPrefix": "drivers/net/"
}

Results:
{
  "Output": "drivers/net/mdio/mdio-realtek-rtl9300.c-237-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:238:struct otto_emdio_cmd_regs {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-239-\tu32 c22_data;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-248-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:249:struct otto_emdio_priv {\ndrivers/net/mdio/mdio-realtek-rtl9300.c:250:\tconst struct otto_emdio_info *info;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-251-\tstruct regmap *regmap;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-260-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:261:struct otto_emdio_info {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-262-\tu32 addr_map_base;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-266-\tu32 cmd_write;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:267:\tstruct otto_emdio_cmd_regs cmd_regs;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-268-\tu8 num_buses;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-271-\tu32 poll_ctrl;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:272:\tint (*setup_controller)(struct otto_emdio_priv *priv);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-273-\tint (*read_c22)(struct mii_bus *bus, int port, int regnum, u32 *value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-278-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:279:struct otto_emdio_chan {\ndrivers/net/mdio/mdio-realtek-rtl9300.c:280:\tstruct otto_emdio_priv *priv;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-281-\tu8 mdio_bus;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-283-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:284:static int otto_emdio_phy_to_port(struct mii_bus *bus, int phy_id)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-285-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:286:\tstruct otto_emdio_chan *chan = bus-\u003epriv;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:287:\tstruct otto_emdio_priv *priv;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-288-\tint i;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-299-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:300:static struct otto_emdio_priv *otto_emdio_bus_to_priv(struct mii_bus *bus)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-301-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:302:\tstruct otto_emdio_chan *chan = bus-\u003epriv;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-303-\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-306-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:307:static int otto_emdio_set_port_polling(struct otto_emdio_priv *priv, int port, bool active)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-308-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-312-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:313:static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:314:\t\t\t      struct otto_emdio_cmd_regs *cmd_data)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-315-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:316:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:317:\tconst struct otto_emdio_info *info = priv-\u003einfo;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-318-\tu32 cmdstate;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-369-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:370:static int otto_emdio_read_cmd(struct mii_bus *bus, u32 cmd,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:371:\t\t\t       struct otto_emdio_cmd_regs *cmd_data, u32 mask, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-372-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:373:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-374-\tint ret;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-376-\tlockdep_assert_held(\u0026priv-\u003elock);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:377:\tret = otto_emdio_run_cmd(bus, cmd | priv-\u003einfo-\u003ecmd_read, cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-378-\tif (ret)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-389-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:390:static int otto_emdio_write_cmd(struct mii_bus *bus, u32 cmd,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:391:\t\t\t\tstruct otto_emdio_cmd_regs *cmd_data)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-392-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:393:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-394-\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-396-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:397:\treturn otto_emdio_run_cmd(bus, cmd | priv-\u003einfo-\u003ecmd_write, cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-398-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-399-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:400:static int otto_emdio_8380_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-401-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:402:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:403:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-404-\t\t.c22_data\t= FIELD_PREP(RTL8380_PHY_CTRL_REG_ADDR, regnum) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-409-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:410:\treturn otto_emdio_read_cmd(bus, RTL8380_PHY_CTRL_TYPE_C22, \u0026cmd_data,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-411-\t\t\t\t   RTL8380_PHY_CTRL_DATA, value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-413-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:414:static int otto_emdio_8380_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-415-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:416:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:417:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-418-\t\t.c22_data\t= FIELD_PREP(RTL8380_PHY_CTRL_REG_ADDR, regnum) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-424-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:425:\treturn otto_emdio_write_cmd(bus, RTL8380_PHY_CTRL_TYPE_C22, \u0026cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-426-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-427-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:428:static int otto_emdio_8380_read_c45(struct mii_bus *bus, int port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-429-\t\t\t\t    int dev_addr, int regnum, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-430-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:431:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-432-\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-436-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:437:\treturn otto_emdio_read_cmd(bus, RTL8380_PHY_CTRL_TYPE_C45, \u0026cmd_data,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-438-\t\t\t\t   RTL8380_PHY_CTRL_DATA, value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-440-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:441:static int otto_emdio_8380_write_c45(struct mii_bus *bus, int port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-442-\t\t\t\t     int dev_addr, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-443-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:444:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-445-\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-450-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:451:\treturn otto_emdio_write_cmd(bus, RTL8380_PHY_CTRL_TYPE_C45, \u0026cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-452-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-453-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:454:static int otto_emdio_8390_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-455-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:456:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:457:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-458-\t\t.c22_data\t= FIELD_PREP(RTL8390_PHY_CTRL_REG_ADDR, regnum) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-463-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:464:\treturn otto_emdio_read_cmd(bus, RTL8390_PHY_CTRL_TYPE_C22, \u0026cmd_data,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-465-\t\t\t\t   RTL8390_PHY_CTRL_DATA, value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-467-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:468:static int otto_emdio_8390_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-469-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:470:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:471:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-472-\t\t.c22_data\t= FIELD_PREP(RTL8390_PHY_CTRL_REG_ADDR, regnum) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-479-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:480:\treturn otto_emdio_write_cmd(bus, RTL8390_PHY_CTRL_TYPE_C22, \u0026cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-481-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-482-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:483:static int otto_emdio_8390_read_c45(struct mii_bus *bus, int port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-484-\t\t\t\t    int dev_addr, int regnum, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-485-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:486:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-487-\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-491-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:492:\treturn otto_emdio_read_cmd(bus, RTL8390_PHY_CTRL_TYPE_C45, \u0026cmd_data,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-493-\t\t\t\t   RTL8390_PHY_CTRL_DATA, value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-495-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:496:static int otto_emdio_8390_write_c45(struct mii_bus *bus, int port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-497-\t\t\t\t     int dev_addr, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-498-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:499:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-500-\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-506-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:507:\treturn otto_emdio_write_cmd(bus, RTL8390_PHY_CTRL_TYPE_C45, \u0026cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-508-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-509-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:510:static int otto_emdio_9300_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-511-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:512:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:513:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-514-\t\t.c22_data\t= FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-519-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:520:\treturn otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C22, \u0026cmd_data,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-521-\t\t\t\t   RTL9300_PHY_CTRL_DATA, value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-523-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:524:static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-525-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:526:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:527:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-528-\t\t.c22_data\t= FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-534-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:535:\treturn otto_emdio_write_cmd(bus, RTL9300_PHY_CTRL_TYPE_C22, \u0026cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-536-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-537-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:538:static int otto_emdio_9300_read_c45(struct mii_bus *bus, int port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-539-\t\t\t\t    int dev_addr, int regnum, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-540-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:541:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-542-\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-546-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:547:\treturn otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, \u0026cmd_data,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-548-\t\t\t\t   RTL9300_PHY_CTRL_DATA, value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-550-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:551:static int otto_emdio_9300_write_c45(struct mii_bus *bus, int port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-552-\t\t\t\t     int dev_addr, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-553-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:554:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-555-\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-560-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:561:\treturn otto_emdio_write_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, \u0026cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-562-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-563-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:564:static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-565-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:566:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:567:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-568-\t\t.broadcast\t= FIELD_PREP(RTL9310_BC_PORT_ID, port),\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-572-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:573:\treturn otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, \u0026cmd_data,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-574-\t\t\t\t   RTL9310_PHY_CTRL_DATA, value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-576-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:577:static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-578-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:579:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:580:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-581-\t\t.c22_data\t= FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-587-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:588:\treturn otto_emdio_write_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, \u0026cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-589-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-590-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:591:static int otto_emdio_9310_read_c45(struct mii_bus *bus, int port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-592-\t\t\t\t    int dev_addr, int regnum, u32 *value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-593-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:594:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-595-\t\t.broadcast\t= FIELD_PREP(RTL9310_BC_PORT_ID, port),\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-599-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:600:\treturn otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C45, \u0026cmd_data,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-601-\t\t\t\t   RTL9310_PHY_CTRL_DATA, value);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-603-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:604:static int otto_emdio_9310_write_c45(struct mii_bus *bus, int port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-605-\t\t\t\t     int dev_addr, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-606-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:607:\tstruct otto_emdio_cmd_regs cmd_data = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-608-\t\t.c45_data\t= FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) |\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-614-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:615:\treturn otto_emdio_write_cmd(bus, RTL9310_PHY_CTRL_TYPE_C45, \u0026cmd_data);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-616-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-617-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:618:static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-619-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:620:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-621-\tint ret, port;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-629-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:630:\tport = otto_emdio_phy_to_port(bus, phy_id);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-631-\tif (port \u003c 0)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-643-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:644:static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-645-\t\t\t\tu16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-646-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:647:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-648-\tint ret, port;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-655-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:656:\tport = otto_emdio_phy_to_port(bus, phy_id);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-657-\tif (port \u003c 0)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-674-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:675:static int otto_emdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-676-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:677:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-678-\tint ret, port;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-680-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:681:\tport = otto_emdio_phy_to_port(bus, phy_id);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-682-\tif (port \u003c 0)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-690-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:691:static int otto_emdio_write_c45(struct mii_bus *bus, int phy_id,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-692-\t\t\t\tint dev_addr, int regnum, u16 value)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-693-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:694:\tstruct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-695-\tint ret, port;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-696-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:697:\tport = otto_emdio_phy_to_port(bus, phy_id);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-698-\tif (port \u003c 0)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-706-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:707:static int otto_emdio_write_mapping(struct otto_emdio_priv *priv, u32 base, u32 port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-708-\t\t\t\t    u32 vals_per_reg, u32 bits_per_val, u32 value)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-716-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:717:static int otto_emdio_setup_topology(struct otto_emdio_priv *priv)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-718-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:719:\tconst struct otto_emdio_info *info = priv-\u003einfo;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-720-\tu32 port;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-724-\t\tif (info-\u003ebus_map_base) {\ndrivers/net/mdio/mdio-realtek-rtl9300.c:725:\t\t\tret = otto_emdio_write_mapping(priv, info-\u003ebus_map_base, port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-726-\t\t\t\t\t\t       MAP_BUSES_PER_REG, MAP_BITS_PER_BUS,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-731-\t\tif (info-\u003eaddr_map_base) {\ndrivers/net/mdio/mdio-realtek-rtl9300.c:732:\t\t\tret = otto_emdio_write_mapping(priv, info-\u003eaddr_map_base, port,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-733-\t\t\t\t\t\t       MAP_ADDRS_PER_REG, MAP_BITS_PER_ADDR,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-742-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:743:static int otto_emdio_8380_setup_controller(struct otto_emdio_priv *priv)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-744-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-751-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:752:static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-753-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-771-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:772:static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-773-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-787-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:788:static int otto_emdio_notify_phy_attach(struct phy_device *phydev)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-789-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:790:\tint port = otto_emdio_phy_to_port(phydev-\u003emdio.bus, phydev-\u003emdio.addr);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:791:\tstruct otto_emdio_chan *chan = phydev-\u003emdio.bus-\u003epriv;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:792:\tstruct otto_emdio_priv *priv = chan-\u003epriv;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-793-\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-809-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:810:\treturn otto_emdio_set_port_polling(priv, port, true);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-811-}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-812-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:813:static void otto_emdio_notify_phy_detach(struct phy_device *phydev)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-814-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c-815-\tstruct mii_bus *bus = phydev-\u003emdio.bus;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:816:\tstruct otto_emdio_priv *priv;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-817-\tint port;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-818-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:819:\tpriv = otto_emdio_bus_to_priv(phydev-\u003emdio.bus);\ndrivers/net/mdio/mdio-realtek-rtl9300.c:820:\tport = otto_emdio_phy_to_port(phydev-\u003emdio.bus, phydev-\u003emdio.addr);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-821-\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-828-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:829:\tif (otto_emdio_set_port_polling(priv, port, false))\ndrivers/net/mdio/mdio-realtek-rtl9300.c-830-\t\tdev_err(bus-\u003eparent, \"failed to disable polling for port %d\\n\", port);\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-832-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:833:static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-834-\t\t\t\t struct fwnode_handle *node)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-835-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c:836:\tstruct otto_emdio_chan *chan;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-837-\tstruct mii_bus *bus;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-853-\tbus-\u003ename = \"Realtek Switch MDIO Bus\";\ndrivers/net/mdio/mdio-realtek-rtl9300.c:854:\tbus-\u003eread_c45 = otto_emdio_read_c45;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:855:\tbus-\u003ewrite_c45 = otto_emdio_write_c45;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:856:\tbus-\u003eread = otto_emdio_read_c22;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:857:\tbus-\u003ewrite = otto_emdio_write_c22;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-858-\tbus-\u003eparent = dev;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:859:\tbus-\u003enotify_phy_attach = otto_emdio_notify_phy_attach;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:860:\tbus-\u003enotify_phy_detach = otto_emdio_notify_phy_detach;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-861-\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-874-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:875:static struct device_node *otto_emdio_get_bus_node(struct device_node *dn)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-876-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-893- */\ndrivers/net/mdio/mdio-realtek-rtl9300.c:894:static int otto_emdio_map_ports(struct device *dev)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-895-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c-896-\tstruct device_node *ports_dn, *phy_dn, *bus_dn, *ctrl_dn;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:897:\tstruct otto_emdio_priv *priv = dev_get_drvdata(dev);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-898-\tstruct device *parent = dev-\u003eparent;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-914-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:915:\t\tbus_dn = otto_emdio_get_bus_node(phy_dn);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-916-\t\tif (!bus_dn)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-977-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:978:static int otto_emdio_init_polling(struct otto_emdio_priv *priv)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-979-{\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-982-\tfor (int port = 0; port \u003c priv-\u003einfo-\u003enum_ports; port++) {\ndrivers/net/mdio/mdio-realtek-rtl9300.c:983:\t\terr = otto_emdio_set_port_polling(priv, port, false);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-984-\t\tif (err)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-990-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:991:static int otto_emdio_probe(struct platform_device *pdev)\ndrivers/net/mdio/mdio-realtek-rtl9300.c-992-{\ndrivers/net/mdio/mdio-realtek-rtl9300.c-993-\tstruct device *dev = \u0026pdev-\u003edev;\ndrivers/net/mdio/mdio-realtek-rtl9300.c:994:\tstruct otto_emdio_priv *priv;\ndrivers/net/mdio/mdio-realtek-rtl9300.c-995-\tint err;\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1009-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1010:\terr = otto_emdio_init_polling(priv);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1011-\tif (err)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1015-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1016:\terr = otto_emdio_map_ports(dev);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1017-\tif (err)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1019-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1020:\terr = otto_emdio_setup_topology(priv);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1021-\tif (err)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1030-\tdevice_for_each_child_node_scoped(dev, child) {\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1031:\t\terr = otto_emdio_probe_one(dev, priv, child);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1032-\t\tif (err)\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1038-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1039:static const struct otto_emdio_info otto_emdio_8380_info = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1040-\t.addr_map_base = RTL8380_SMI_PORT0_5_ADDR_CTRL,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1053-\t.poll_ctrl = RTL8380_SMI_POLL_CTRL,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1054:\t.setup_controller = otto_emdio_8380_setup_controller,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1055:\t.read_c22 = otto_emdio_8380_read_c22,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1056:\t.read_c45 = otto_emdio_8380_read_c45,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1057:\t.write_c22 = otto_emdio_8380_write_c22,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1058:\t.write_c45 = otto_emdio_8380_write_c45,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1059-};\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1060-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1061:static const struct otto_emdio_info otto_emdio_8390_info = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1062-\t.cmd_fail = RTL8390_PHY_CTRL_FAIL,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1077-\t.poll_ctrl = RTL8390_SMI_PORT_POLLING_CTRL,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1078:\t.read_c22 = otto_emdio_8390_read_c22,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1079:\t.read_c45 = otto_emdio_8390_read_c45,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1080:\t.write_c22 = otto_emdio_8390_write_c22,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1081:\t.write_c45 = otto_emdio_8390_write_c45,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1082-};\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1083-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1084:static const struct otto_emdio_info otto_emdio_9300_info = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1085-\t.addr_map_base = RTL9300_SMI_PORT0_5_ADDR_CTRL,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1099-\t.poll_ctrl = RTL9300_SMI_POLL_CTRL,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1100:\t.setup_controller = otto_emdio_9300_setup_controller,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1101:\t.read_c22 = otto_emdio_9300_read_c22,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1102:\t.read_c45 = otto_emdio_9300_read_c45,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1103:\t.write_c22 = otto_emdio_9300_write_c22,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1104:\t.write_c45 = otto_emdio_9300_write_c45,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1105-};\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1106-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1107:static const struct otto_emdio_info otto_emdio_9310_info = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1108-\t.addr_map_base = RTL9310_SMI_PORT_ADDR_CTRL,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1125-\t.poll_ctrl = RTL9310_SMI_PORT_POLLING_CTRL,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1126:\t.setup_controller = otto_emdio_9310_setup_controller,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1127:\t.read_c22 = otto_emdio_9310_read_c22,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1128:\t.read_c45 = otto_emdio_9310_read_c45,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1129:\t.write_c22 = otto_emdio_9310_write_c22,\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1130:\t.write_c45 = otto_emdio_9310_write_c45,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1131-};\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1132-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1133:static const struct of_device_id otto_emdio_ids[] = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1134:\t{ .compatible = \"realtek,rtl8380-mdio\", .data = \u0026otto_emdio_8380_info },\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1135:\t{ .compatible = \"realtek,rtl8391-mdio\", .data = \u0026otto_emdio_8390_info },\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1136:\t{ .compatible = \"realtek,rtl9301-mdio\", .data = \u0026otto_emdio_9300_info },\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1137:\t{ .compatible = \"realtek,rtl9311-mdio\", .data = \u0026otto_emdio_9310_info },\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1138-\t{}\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1139-};\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1140:MODULE_DEVICE_TABLE(of, otto_emdio_ids);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1141-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1142:static struct platform_driver otto_emdio_driver = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1143:\t.probe = otto_emdio_probe,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1144-\t.driver = {\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1145-\t\t.name = \"mdio-rtl9300\",\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1146:\t\t.of_match_table = otto_emdio_ids,\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1147-\t\t.suppress_bind_attrs = true,\n--\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1150-\ndrivers/net/mdio/mdio-realtek-rtl9300.c:1151:module_platform_driver(otto_emdio_driver);\ndrivers/net/mdio/mdio-realtek-rtl9300.c-1152-\n"
}

9/2 2026/08/31 21:55 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 44149
							output: 191
							thoughts: 2716
						

Thoughts:
**Analysis of PHY Driver Changes for Realtek Otto SoC Support**

Okay, so I've been dissecting this patch series and it seems pretty straightforward in its implications. The primary focus here is the addition of support for Realtek's Otto SoC switches, specifically the RTL838x/RTL839x/RTL930x series. The code changes revolve around integrating a new MDIO bus driver (`mdio-realtek-rtl9300.c`) that's OF-only, meaning it relies on device tree bindings and platform-specific configurations (`otto_emdio_driver`). Importantly, `otto_emdio_driver.driver.suppress_bind_attrs = true` means the usual sysfs binding won't even work.

The core of the change lies in the PHY device core (`phy_device.c`) where new `notify_phy_attach` and `notify_phy_detach` callbacks have been added to the MDIO bus interface.  However, and this is crucial, in *every* standard network driver or virtualized environment (like QEMU or GCE), these callbacks are never implemented. The checks using those callbacks in `phy_attach_direct` and `phy_detach_internal` effectively become no-ops. For any standard MDIO bus, those pointers are NULL and the conditions `if (phydev->mdio.bus->notify_phy_attach)` and `if (notify_bus && phydev->mdio.bus->notify_phy_detach)` are simply bypassed. The error path tweaks in `phy_attach_direct` redirect to `phy_detach_internal` with `notify_bus` set to false, which further ensures these callbacks are skipped, since the `bus->notify_phy_detach` is NULL, which means `notify_bus` is always false. The only driver that actually *uses* these new callbacks is the `mdio-realtek-rtl9300.c` driver.

Given that this Realtek MDIO bus driver is specifically for Realtek RTL838x/RTL930x switch SoCs, and these are specialized MIPS-based hardware components, they aren't emulated in common virtualization environments like QEMU or GCE.  Moreover, the driver's suppression of sysfs binding confirms that it's designed to be used with specific Realtek hardware configurations, which, again, excludes any standard virtualized environment.

Therefore, since the changes are isolated to a platform-specific driver (Realtek Otto switch) and introduce no-op behaviors in general, there's no reason to consider this patch series fuzzable in any standard setting.  The modifications in the PHY core are passive unless the Realtek MDIO driver is present, which is a very specific condition.



10/2 2026/08/31 21:55 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch series adds RTL8380/RTL8390 support and MDIO polling handling to the Realtek Otto MDIO driver (drivers/net/mdio/mdio-realtek-rtl9300.c) along with helper notification callbacks in the core PHY device layer (drivers/net/phy/phy_device.c). The driver is an OF-matched platform driver for Realtek RTL83xx/RTL93xx switch SoCs (MACH_REALTEK_RTL) requiring physical hardware that cannot be reached or emulated in virtualized environments like QEMU or GCE. The core PHY additions are no-op hooks unless the underlying MDIO bus driver implements the callbacks, which only this unreachable driver does.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series adds RTL8380/RTL8390 support and MDIO polling handling to the Realtek Otto MDIO driver (drivers/net/mdio/mdio-realtek-rtl9300.c) along with helper notification callbacks in the core PHY device layer (drivers/net/phy/phy_device.c). The driver is an OF-matched platform driver for Realtek RTL83xx/RTL93xx switch SoCs (MACH_REALTEK_RTL) requiring physical hardware that cannot be reached or emulated in virtualized environments like QEMU or GCE. The core PHY additions are no-op hooks unless the underlying MDIO bus driver implements the callbacks, which only this unreachable driver does.",
  "WorthFuzzing": false
}

11/1 2026/08/31 21:55 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)