| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/15 05:42 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"STMMAC_ETH",
"DWMAC_INTEL",
"PCS_XPCS",
"PCS_XPCS_ROCKCHIP",
"DWMAC_ROCKCHIP",
"PHY_ROCKCHIP_NANENG_COMBO_PHY"
],
"FocusSymbols": [
"xpcs_config_aneg_c37_sgmii",
"xpcs_config_aneg_c37_1000basex",
"xpcs_get_state_c37_sgmii",
"intel_mgbe_pcs_init",
"stmmac_pcs_setup",
"rk_pcs_init",
"xpcs_rk_probe",
"xpcs_rk_create"
],
"Reasoning": "The patch introduces support for the Rockchip RK3568 XPCS, which includes a new platform driver and modifications to the generic pcs-xpcs driver and stmmac driver. While the Rockchip-specific platform driver might be difficult to reach in a generic virtualized environment without specific device tree configurations, the patch also refactors how the PCS is initialized in the generic stmmac driver and modifies the Intel mGBE PCI driver (dwmac-intel.c) to use the new initialization flow. Furthermore, it modifies the generic pcs-xpcs.c driver's auto-negotiation and state polling logic for SGMII and 1000BASE-X interfaces. These generic and PCI-based changes are reachable via PCI fuzzing and netlink interface manipulation, making the patch worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/15 05:42 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 7193ed0e0aacf4d35974c523bd45360f98c52e8d\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 15 05:42:26 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/devicetree/bindings/net/pcs/rockchip-dwxpcs.yaml b/Documentation/devicetree/bindings/net/pcs/rockchip-dwxpcs.yaml\nnew file mode 100644\nindex 00000000000000..14fadf67c793a8\n--- /dev/null\n+++ b/Documentation/devicetree/bindings/net/pcs/rockchip-dwxpcs.yaml\n@@ -0,0 +1,126 @@\n+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n+%YAML 1.2\n+---\n+$id: http://devicetree.org/schemas/net/pcs/rockchip-dwxpcs.yaml#\n+$schema: http://devicetree.org/meta-schemas/core.yaml#\n+\n+title: Rockchip RK3568 Synopsys DesignWare Ethernet PCS\n+\n+maintainers:\n+ - Coia Prant \u003ccoiaprant@gmail.com\u003e\n+\n+description: |\n+ Rockchip RK3568 SoC integrates a Synopsys DesignWare Ethernet Physical\n+ Coding Sublayer (XPCS).\n+ The PCS provides an interface between the Media Access Control (MAC)\n+ and the Physical Medium Attachment (PMA) sublayer through a Media\n+ Independent Interface (GMII).\n+\n+ The XPCS is accessed over the APB3 bus and internally connected to a\n+ Naneng Combo SerDes PHY.\n+ It supports 1000BASE-X, SGMII and QSGMII modes.\n+\n+ The block contains four MII ports (pcs-mii@0..3) that can be\n+ individually enabled and routed to one of the Ethernet GMAC controllers\n+ via the pcs-handle property in the MAC device tree node.\n+\n+properties:\n+ compatible:\n+ const: rockchip,rk3568-xpcs\n+\n+ '#address-cells':\n+ const: 1\n+\n+ '#size-cells':\n+ const: 0\n+\n+ reg:\n+ description: |\n+ Base address and size of the XPCS register space mapped over the\n+ APB3 bus.\n+ maxItems: 1\n+\n+ clocks:\n+ description: |\n+ Clock sources for the XPCS:\n+ - csr: APB3 bus interface clock (clk_csr_i), required for register\n+ access.\n+ - eee: EEE clock (clk_eee_i), required for Energy Efficient\n+ Ethernet (EEE) operation.\n+ minItems: 2\n+ maxItems: 2\n+\n+ clock-names:\n+ items:\n+ - const: csr\n+ - const: eee\n+\n+ phys:\n+ description: |\n+ The phandle of SerDes PHY (Naneng Combo PHY) that provides\n+ the serial lanes for 1000BASE-X / SGMII / QSGMII.\n+ The SerDes must be powered on and initialised before any XPCS\n+ register access.\n+ maxItems: 1\n+\n+ phy-names:\n+ const: serdes\n+\n+ power-domains:\n+ description: |\n+ Power domain for the XPCS.\n+ On RK3568 this is typically the PD_PIPE power domain, which also\n+ supplies the SerDes PHY.\n+ maxItems: 1\n+\n+patternProperties:\n+ \"^pcs-mii@[0-3]$\":\n+ type: object\n+ description: |\n+ One of the four MII ports of the XPCS.\n+ The port number is specified by the reg property (0..3).\n+ The port is linked to an Ethernet MAC controller via the\n+ pcs-handle property in the MAC's device tree node.\n+\n+ properties:\n+ reg:\n+ minimum: 0\n+ maximum: 3\n+ description: |\n+ MII port number of PCS.\n+\n+ status: true\n+\n+ required:\n+ - reg\n+\n+ additionalProperties: false\n+\n+required:\n+ - compatible\n+ - reg\n+ - clocks\n+ - clock-names\n+\n+additionalProperties: false\n+\n+examples:\n+ - |\n+ #include \u003cdt-bindings/clock/rk3568-cru.h\u003e\n+ #include \u003cdt-bindings/power/rk3568-power.h\u003e\n+\n+ pcs@fda00000 {\n+ compatible = \"rockchip,rk3568-xpcs\";\n+ #address-cells = \u003c1\u003e;\n+ #size-cells = \u003c0\u003e;\n+ reg = \u003c0x0 0xfda00000 0x0 0x200000\u003e;\n+ clocks = \u003c\u0026cru PCLK_XPCS\u003e, \u003c\u0026cru CLK_XPCS_EEE\u003e;\n+ clock-names = \"csr\", \"eee\";\n+ phys = \u003c\u0026combphy2 PHY_TYPE_SGMII\u003e;\n+ phy-names = \"serdes\";\n+ power-domains = \u003c\u0026power RK3568_PD_PIPE\u003e;\n+\n+ pcs-mii@0 {\n+ reg = \u003c0\u003e;\n+ };\n+ };\ndiff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml\nindex 379b08bd9e97a6..6173192e31ab2f 100644\n--- a/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml\n+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml\n@@ -80,6 +80,13 @@ properties:\n description:\n Some additional pipe settings are accessed through GRF regs.\n \n+ rockchip,sgmii-mac-sel:\n+ $ref: /schemas/types.yaml#/definitions/uint32\n+ enum: [0, 1]\n+ default: 0\n+ description:\n+ Select gmac0 or gmac1 to be used as SGMII controller.\n+\n \"#phy-cells\":\n const: 1\n \ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 806bd2d80d1535..8cba6a0a2dd034 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -23463,6 +23463,15 @@ S:\tMaintained\n F:\tDocumentation/devicetree/bindings/sound/rockchip,rk3576-sai.yaml\n F:\tsound/soc/rockchip/rockchip_sai.*\n \n+ROCKCHIP XPCS DRIVER\n+M:\tCoia Prant \u003ccoiaprant@gmail.com\u003e\n+L:\tnetdev@vger.kernel.org\n+L:\tlinux-rockchip@lists.infradead.org\n+S:\tMaintained\n+F:\tDocumentation/devicetree/bindings/net/pcs/rockchip-dwxpcs.yaml\n+F:\tdrivers/net/pcs/pcs-xpcs-rk.c\n+F:\tinclude/linux/pcs/pcs-xpcs-rk.h\n+\n ROCKER DRIVER\n M:\tJiri Pirko \u003cjiri@resnulli.us\u003e\n L:\tnetdev@vger.kernel.org\ndiff --git a/arch/arm64/boot/dts/rockchip/rk3568-photonicat.dts b/arch/arm64/boot/dts/rockchip/rk3568-photonicat.dts\nindex 58c1052ba8ef35..91c17b624fd17d 100644\n--- a/arch/arm64/boot/dts/rockchip/rk3568-photonicat.dts\n+++ b/arch/arm64/boot/dts/rockchip/rk3568-photonicat.dts\n@@ -3,6 +3,7 @@\n /dts-v1/;\n \n #include \u003cdt-bindings/gpio/gpio.h\u003e\n+#include \u003cdt-bindings/leds/common.h\u003e\n #include \u003cdt-bindings/pinctrl/rockchip.h\u003e\n #include \u003cdt-bindings/soc/rockchip,vop2.h\u003e\n #include \"rk3568.dtsi\"\n@@ -242,6 +243,7 @@ \u0026combphy1 {\n \n \u0026combphy2 {\n \tstatus = \"okay\";\n+\trockchip,sgmii-mac-sel = \u003c0\u003e;\n };\n \n \u0026cpu0 {\n@@ -260,9 +262,18 @@ \u0026cpu3 {\n \tcpu-supply = \u003c\u0026vdd_cpu\u003e;\n };\n \n-/* Motorcomm YT8521SC LAN port (require SGMII) */\n+/* Motorcomm YT8521SC LAN port */\n \u0026gmac0 {\n-\tstatus = \"disabled\";\n+\tassigned-clocks = \u003c\u0026cru SCLK_GMAC0_RX_TX\u003e;\n+\tassigned-clock-parents = \u003c\u0026xpcs_gmac0_clk\u003e;\n+\tpcs-handle = \u003c\u0026xpcs_mii0\u003e;\n+\tmanaged = \"in-band-status\";\n+\tphy-handle = \u003c\u0026sgmii_phy\u003e;\n+\tphy-mode = \"sgmii\";\n+\tphy-supply = \u003c\u0026vcc_3v3\u003e;\n+\tpinctrl-names = \"default\";\n+\tpinctrl-0 = \u003c\u0026gmac0_miim\u003e;\n+\tstatus = \"okay\";\n };\n \n /* Motorcomm YT8521SC WAN port */\n@@ -341,6 +352,39 @@ \u0026i2s0_8ch {\n \tstatus = \"okay\";\n };\n \n+\u0026mdio0 {\n+\tsgmii_phy: ethernet-phy@3 {\n+\t\tcompatible = \"ethernet-phy-id0000.011a\";\n+\t\treg = \u003c0x3\u003e;\n+\t\tmax-speed = \u003c1000\u003e;\n+\t\teee-broken-10gt;\n+\t\teee-broken-10gkx4;\n+\t\teee-broken-10gkr;\n+\t\treset-assert-us = \u003c20000\u003e;\n+\t\treset-deassert-us = \u003c100000\u003e;\n+\t\treset-gpios = \u003c\u0026gpio3 RK_PC6 GPIO_ACTIVE_LOW\u003e;\n+\n+\t\tleds {\n+\t\t\t#address-cells = \u003c1\u003e;\n+\t\t\t#size-cells = \u003c0\u003e;\n+\n+\t\t\tled@1 {\n+\t\t\t\treg = \u003c1\u003e;\n+\t\t\t\tcolor = \u003cLED_COLOR_ID_AMBER\u003e;\n+\t\t\t\tfunction = LED_FUNCTION_LAN;\n+\t\t\t\tdefault-state = \"keep\";\n+\t\t\t};\n+\n+\t\t\tled@2 {\n+\t\t\t\treg = \u003c2\u003e;\n+\t\t\t\tcolor = \u003cLED_COLOR_ID_GREEN\u003e;\n+\t\t\t\tfunction = LED_FUNCTION_LAN;\n+\t\t\t\tdefault-state = \"keep\";\n+\t\t\t};\n+\t\t};\n+\t};\n+};\n+\n \u0026mdio1 {\n \trgmii_phy: ethernet-phy@3 {\n \t\tcompatible = \"ethernet-phy-ieee802.3-c22\";\n@@ -350,6 +394,25 @@ rgmii_phy: ethernet-phy@3 {\n \t\treset-gpios = \u003c\u0026gpio4 RK_PC0 GPIO_ACTIVE_LOW\u003e;\n \t\trx-internal-delay-ps = \u003c1500\u003e;\n \t\ttx-internal-delay-ps = \u003c1500\u003e;\n+\n+\t\tleds {\n+\t\t\t#address-cells = \u003c1\u003e;\n+\t\t\t#size-cells = \u003c0\u003e;\n+\n+\t\t\tled@1 {\n+\t\t\t\treg = \u003c1\u003e;\n+\t\t\t\tcolor = \u003cLED_COLOR_ID_AMBER\u003e;\n+\t\t\t\tfunction = LED_FUNCTION_WAN;\n+\t\t\t\tdefault-state = \"keep\";\n+\t\t\t};\n+\n+\t\t\tled@2 {\n+\t\t\t\treg = \u003c2\u003e;\n+\t\t\t\tcolor = \u003cLED_COLOR_ID_GREEN\u003e;\n+\t\t\t\tfunction = LED_FUNCTION_WAN;\n+\t\t\t\tdefault-state = \"keep\";\n+\t\t\t};\n+\t\t};\n \t};\n };\n \n@@ -586,3 +649,13 @@ \u0026xin32k {\n \tpinctrl-names = \"default\";\n \tpinctrl-0 = \u003c\u0026clk32k_out1\u003e;\n };\n+\n+\u0026xpcs {\n+\tstatus = \"okay\";\n+\tphys = \u003c\u0026combphy2 PHY_TYPE_SGMII\u003e;\n+\tphy-names = \"serdes\";\n+};\n+\n+\u0026xpcs_mii0 {\n+\tstatus = \"okay\";\n+};\ndiff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi\nindex 3bc653f027f1fe..989e164c0eb398 100644\n--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi\n+++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi\n@@ -110,6 +110,51 @@ sata0: sata@fc000000 {\n \t\tstatus = \"disabled\";\n \t};\n \n+\txpcs: pcs@fda00000 {\n+\t\tcompatible = \"rockchip,rk3568-xpcs\";\n+\t\t#address-cells = \u003c1\u003e;\n+\t\t#size-cells = \u003c0\u003e;\n+\t\treg = \u003c0x0 0xfda00000 0x0 0x200000\u003e;\n+\t\tclocks = \u003c\u0026cru PCLK_XPCS\u003e, \u003c\u0026cru CLK_XPCS_EEE\u003e;\n+\t\tclock-names = \"csr\", \"eee\";\n+\t\tpower-domains = \u003c\u0026power RK3568_PD_PIPE\u003e;\n+\t\tstatus = \"disabled\";\n+\n+\t\txpcs_mii0: pcs-mii@0 {\n+\t\t\treg = \u003c0\u003e;\n+\t\t\tstatus = \"disabled\";\n+\t\t};\n+\n+\t\txpcs_mii1: pcs-mii@1 {\n+\t\t\treg = \u003c1\u003e;\n+\t\t\tstatus = \"disabled\";\n+\t\t};\n+\n+\t\txpcs_mii2: pcs-mii@2 {\n+\t\t\treg = \u003c2\u003e;\n+\t\t\tstatus = \"disabled\";\n+\t\t};\n+\n+\t\txpcs_mii3: pcs-mii@3 {\n+\t\t\treg = \u003c3\u003e;\n+\t\t\tstatus = \"disabled\";\n+\t\t};\n+\t};\n+\n+\txpcs_gmac0_clk: xpcs-gmac0-clock {\n+\t\tcompatible = \"fixed-clock\";\n+\t\tclock-frequency = \u003c125000000\u003e;\n+\t\tclock-output-names = \"clk_gmac0_xpcs_mii\";\n+\t\t#clock-cells = \u003c0\u003e;\n+\t};\n+\n+\txpcs_gmac1_clk: xpcs-gmac1-clock {\n+\t\tcompatible = \"fixed-clock\";\n+\t\tclock-frequency = \u003c125000000\u003e;\n+\t\tclock-output-names = \"clk_gmac1_xpcs_mii\";\n+\t\t#clock-cells = \u003c0\u003e;\n+\t};\n+\n \tpipe_phy_grf0: syscon@fdc70000 {\n \t\tcompatible = \"rockchip,rk3568-pipe-phy-grf\", \"syscon\";\n \t\treg = \u003c0x0 0xfdc70000 0x0 0x1000\u003e;\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig\nindex e3dd5adda5acaf..5088acc06982e0 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig\n+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig\n@@ -170,6 +170,7 @@ config DWMAC_ROCKCHIP\n \tdefault ARCH_ROCKCHIP\n \tdepends on OF \u0026\u0026 (ARCH_ROCKCHIP || COMPILE_TEST)\n \tselect MFD_SYSCON\n+\tselect PCS_XPCS_ROCKCHIP\n \thelp\n \t Support for Ethernet controller on Rockchip RK3288 SoC.\n \ndiff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c\nindex b8d467ba6d72d3..081323c32bcc11 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c\n@@ -572,13 +572,47 @@ static void common_default_data(struct plat_stmmacenet_data *plat)\n \tplat-\u003emdio_bus_data-\u003eneeds_reset = true;\n }\n \n+static int intel_mgbe_pcs_init(struct stmmac_priv *priv)\n+{\n+\tstruct fwnode_handle *devnode, *pcsnode;\n+\tstruct dw_xpcs *xpcs = NULL;\n+\tint addr;\n+\n+\tdevnode = dev_fwnode(priv-\u003edevice);\n+\n+\tif (fwnode_property_present(devnode, \"pcs-handle\")) {\n+\t\tpcsnode = fwnode_find_reference(devnode, \"pcs-handle\", 0);\n+\t\txpcs = xpcs_create_fwnode(pcsnode);\n+\t\tfwnode_handle_put(pcsnode);\n+\t} else {\n+\t\taddr = ffs(priv-\u003eplat-\u003emdio_bus_data-\u003epcs_mask) - 1;\n+\t\txpcs = xpcs_create_mdiodev(priv-\u003emii, addr);\n+\t}\n+\n+\tif (IS_ERR(xpcs))\n+\t\treturn PTR_ERR(xpcs);\n+\n+\txpcs_config_eee_mult_fact(xpcs, priv-\u003eplat-\u003emult_fact_100ns);\n+\n+\tpriv-\u003ehw-\u003expcs = xpcs;\n+\treturn 0;\n+}\n+\n+static void intel_mgbe_pcs_exit(struct stmmac_priv *priv)\n+{\n+\tif (!priv-\u003ehw-\u003expcs)\n+\t\treturn;\n+\n+\txpcs_destroy(priv-\u003ehw-\u003expcs);\n+\tpriv-\u003ehw-\u003expcs = NULL;\n+}\n+\n static struct phylink_pcs *intel_mgbe_select_pcs(struct stmmac_priv *priv,\n \t\t\t\t\t\t phy_interface_t interface)\n {\n-\t/* plat-\u003emdio_bus_data-\u003ehas_xpcs has been set true, so there\n-\t * should always be an XPCS. The original code would always\n-\t * return this if present.\n-\t */\n+\tif (!priv-\u003ehw-\u003expcs)\n+\t\treturn NULL;\n+\n \treturn xpcs_to_phylink_pcs(priv-\u003ehw-\u003expcs);\n }\n \n@@ -702,6 +736,8 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,\n \t plat-\u003ephy_interface == PHY_INTERFACE_MODE_1000BASEX) {\n \t\tplat-\u003emdio_bus_data-\u003epcs_mask = BIT_U32(INTEL_MGBE_XPCS_ADDR);\n \t\tplat-\u003edefault_an_inband = true;\n+\t\tplat-\u003epcs_init = intel_mgbe_pcs_init;\n+\t\tplat-\u003epcs_exit = intel_mgbe_pcs_exit;\n \t\tplat-\u003eselect_pcs = intel_mgbe_select_pcs;\n \t}\n \ndiff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c\nindex 19f34e18bfef2c..9af32c26f9c146 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c\n@@ -81,8 +81,11 @@ static int renesas_gmac_pcs_init(struct stmmac_priv *priv)\n \n static void renesas_gmac_pcs_exit(struct stmmac_priv *priv)\n {\n-\tif (priv-\u003ehw-\u003ephylink_pcs)\n-\t\tmiic_destroy(priv-\u003ehw-\u003ephylink_pcs);\n+\tif (!priv-\u003ehw-\u003ephylink_pcs)\n+\t\treturn;\n+\n+\tmiic_destroy(priv-\u003ehw-\u003ephylink_pcs);\n+\tpriv-\u003ehw-\u003ephylink_pcs = NULL;\n }\n \n static struct phylink_pcs *renesas_gmac_select_pcs(struct stmmac_priv *priv,\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c\nindex 8d7042e6892619..eca482b4b6bfc0 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c\n@@ -20,6 +20,7 @@\n #include \u003clinux/delay.h\u003e\n #include \u003clinux/mfd/syscon.h\u003e\n #include \u003clinux/regmap.h\u003e\n+#include \u003clinux/pcs/pcs-xpcs-rk.h\u003e\n #include \u003clinux/pm_runtime.h\u003e\n \n #include \"stmmac_platform.h\"\n@@ -47,6 +48,7 @@ struct rk_gmac_ops {\n \tvoid (*set_to_rgmii)(struct rk_priv_data *bsp_priv,\n \t\t\t int tx_delay, int rx_delay);\n \tvoid (*set_to_rmii)(struct rk_priv_data *bsp_priv);\n+\tvoid (*set_to_sgmii)(struct rk_priv_data *bsp_priv);\n \tint (*set_speed)(struct rk_priv_data *bsp_priv,\n \t\t\t phy_interface_t interface, int speed);\n \tvoid (*integrated_phy_powerup)(struct rk_priv_data *bsp_priv);\n@@ -63,6 +65,7 @@ struct rk_gmac_ops {\n \tbool clock_grf_reg_in_php;\n \tbool supports_rgmii;\n \tbool supports_rmii;\n+\tbool supports_sgmii;\n \tbool php_grf_required;\n \tbool regs_valid;\n \tu32 regs[];\n@@ -98,6 +101,7 @@ struct rk_priv_data {\n \tbool integrated_phy;\n \tbool supports_rgmii;\n \tbool supports_rmii;\n+\tbool supports_sgmii;\n \n \tstruct clk_bulk_data *clks;\n \tint num_clks;\n@@ -809,6 +813,8 @@ static const struct rk_gmac_ops rk3528_ops = {\n #define RK3568_GRF_GMAC1_CON1\t\t0x038c\n \n /* RK3568_GRF_GMAC0_CON1 \u0026\u0026 RK3568_GRF_GMAC1_CON1 */\n+#define RK3568_GMAC_MODE_RMII_RGMII\t\tGRF_CLR_BIT(7)\n+#define RK3568_GMAC_MODE_SGMII_QSGMII\t\tGRF_BIT(7)\n #define RK3568_GMAC_FLOW_CTRL\t\t\tGRF_BIT(3)\n #define RK3568_GMAC_FLOW_CTRL_CLR\t\tGRF_CLR_BIT(3)\n #define RK3568_GMAC_RXCLK_DLY_ENABLE\t\tGRF_BIT(1)\n@@ -851,18 +857,32 @@ static void rk3568_set_to_rgmii(struct rk_priv_data *bsp_priv,\n \t\t RK3568_GMAC_CLK_TX_DL_CFG(tx_delay));\n \n \tregmap_write(bsp_priv-\u003egrf, con1,\n+\t\t RK3568_GMAC_MODE_RMII_RGMII |\n \t\t RK3568_GMAC_RXCLK_DLY_ENABLE |\n \t\t RK3568_GMAC_TXCLK_DLY_ENABLE);\n }\n \n+static void rk3568_set_to_sgmii(struct rk_priv_data *bsp_priv)\n+{\n+\tu32 con1;\n+\n+\tcon1 = (bsp_priv-\u003eid == 1) ? RK3568_GRF_GMAC1_CON1 :\n+\t\t\t\t RK3568_GRF_GMAC0_CON1;\n+\n+\tregmap_write(bsp_priv-\u003egrf, con1, RK3568_GMAC_MODE_SGMII_QSGMII);\n+}\n+\n static const struct rk_gmac_ops rk3568_ops = {\n \t.init = rk3568_init,\n \t.set_to_rgmii = rk3568_set_to_rgmii,\n+\t.set_to_sgmii = rk3568_set_to_sgmii,\n+\n \t.set_speed = rk_set_clk_mac_speed,\n \n \t.gmac_phy_intf_sel_mask = GENMASK_U16(6, 4),\n \n \t.supports_rmii = true,\n+\t.supports_sgmii = true,\n \n \t.regs_valid = true,\n \t.regs = {\n@@ -1208,6 +1228,43 @@ static void rk_phy_powerdown(struct rk_priv_data *bsp_priv)\n \t\tdev_err(bsp_priv-\u003edev, \"fail to disable phy-supply\\n\");\n }\n \n+static int rk_pcs_init(struct stmmac_priv *priv)\n+{\n+\tstruct device_node *np = priv-\u003edevice-\u003eof_node;\n+\tstruct device_node *pcs_node;\n+\tstruct dw_xpcs *xpcs;\n+\n+\tpcs_node = of_parse_phandle(np, \"pcs-handle\", 0);\n+\tif (!pcs_node)\n+\t\treturn -ENODEV;\n+\n+\txpcs = xpcs_rk_create(priv-\u003edevice, pcs_node);\n+\tof_node_put(pcs_node);\n+\tif (IS_ERR(xpcs))\n+\t\treturn PTR_ERR(xpcs);\n+\n+\tpriv-\u003ehw-\u003expcs = xpcs;\n+\treturn 0;\n+}\n+\n+static void rk_pcs_exit(struct stmmac_priv *priv)\n+{\n+\tif (!priv-\u003ehw-\u003expcs)\n+\t\treturn;\n+\n+\txpcs_destroy(priv-\u003ehw-\u003expcs);\n+\tpriv-\u003ehw-\u003expcs = NULL;\n+}\n+\n+static struct phylink_pcs *rk_select_pcs(struct stmmac_priv *priv,\n+\t\t\t\t\t phy_interface_t interface)\n+{\n+\tif (!priv-\u003ehw-\u003expcs)\n+\t\treturn NULL;\n+\n+\treturn xpcs_to_phylink_pcs(priv-\u003ehw-\u003expcs);\n+}\n+\n static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,\n \t\t\t\t\t struct plat_stmmacenet_data *plat,\n \t\t\t\t\t const struct rk_gmac_ops *ops)\n@@ -1330,6 +1387,7 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,\n \n \tbsp_priv-\u003esupports_rgmii = ops-\u003esupports_rgmii || !!ops-\u003eset_to_rgmii;\n \tbsp_priv-\u003esupports_rmii = ops-\u003esupports_rmii || !!ops-\u003eset_to_rmii;\n+\tbsp_priv-\u003esupports_sgmii = ops-\u003esupports_sgmii || !!ops-\u003eset_to_sgmii;\n \n \tif (ops-\u003einit) {\n \t\tret = ops-\u003einit(bsp_priv);\n@@ -1361,6 +1419,10 @@ static int rk_gmac_check_ops(struct rk_priv_data *bsp_priv)\n \t\tif (!bsp_priv-\u003esupports_rmii)\n \t\t\treturn -EINVAL;\n \t\tbreak;\n+\tcase PHY_INTERFACE_MODE_SGMII:\n+\t\tif (!bsp_priv-\u003esupports_sgmii)\n+\t\t\treturn -EINVAL;\n+\t\tbreak;\n \tdefault:\n \t\tdev_err(bsp_priv-\u003edev,\n \t\t\t\"unsupported interface %d\", bsp_priv-\u003ephy_iface);\n@@ -1379,6 +1441,9 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)\n \tif (ret)\n \t\treturn ret;\n \n+\tif (bsp_priv-\u003ephy_iface == PHY_INTERFACE_MODE_SGMII)\n+\t\tgoto set_mode;\n+\n \tret = rk_get_phy_intf_sel(bsp_priv-\u003ephy_iface);\n \tif (ret \u003c 0)\n \t\treturn ret;\n@@ -1416,7 +1481,8 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)\n \t\t}\n \t}\n \n-\t/*rmii or rgmii*/\n+set_mode:\n+\t/* rmii, rgmii, sgmii */\n \tswitch (bsp_priv-\u003ephy_iface) {\n \tcase PHY_INTERFACE_MODE_RGMII:\n \t\tdev_info(dev, \"init for RGMII\\n\");\n@@ -1447,6 +1513,11 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)\n \t\tif (bsp_priv-\u003eops-\u003eset_to_rmii)\n \t\t\tbsp_priv-\u003eops-\u003eset_to_rmii(bsp_priv);\n \t\tbreak;\n+\tcase PHY_INTERFACE_MODE_SGMII:\n+\t\tdev_info(dev, \"init for SGMII\\n\");\n+\t\tif (bsp_priv-\u003eops-\u003eset_to_sgmii)\n+\t\t\tbsp_priv-\u003eops-\u003eset_to_sgmii(bsp_priv);\n+\t\tbreak;\n \tdefault:\n \t\tdev_err(dev, \"NO interface defined!\\n\");\n \t}\n@@ -1486,6 +1557,9 @@ static void rk_get_interfaces(struct stmmac_priv *priv, void *bsp_priv,\n \n \tif (rk-\u003esupports_rmii)\n \t\t__set_bit(PHY_INTERFACE_MODE_RMII, interfaces);\n+\n+\tif (rk-\u003esupports_sgmii)\n+\t\t__set_bit(PHY_INTERFACE_MODE_SGMII, interfaces);\n }\n \n static int rk_set_clk_tx_rate(void *bsp_priv_, struct clk *clk_tx_i,\n@@ -1602,6 +1676,17 @@ static int rk_gmac_probe(struct platform_device *pdev)\n \tplat_dat-\u003esuspend = rk_gmac_suspend;\n \tplat_dat-\u003eresume = rk_gmac_resume;\n \n+\tif (plat_dat-\u003ephy_interface == PHY_INTERFACE_MODE_SGMII) {\n+\t\t/* SGMII clock always runs at 125 MHz */\n+\t\tplat_dat-\u003eset_clk_tx_rate = NULL;\n+\n+\t\t/* SGMII requires a PCS */\n+\t\tplat_dat-\u003edefault_an_inband = true;\n+\t\tplat_dat-\u003epcs_init = rk_pcs_init;\n+\t\tplat_dat-\u003epcs_exit = rk_pcs_exit;\n+\t\tplat_dat-\u003eselect_pcs = rk_select_pcs;\n+\t}\n+\n \tplat_dat-\u003ebsp_priv = rk_gmac_setup(pdev, plat_dat, data);\n \tif (IS_ERR(plat_dat-\u003ebsp_priv))\n \t\treturn PTR_ERR(plat_dat-\u003ebsp_priv);\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c\nindex 13634965bc19a1..01df4776edb3fd 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c\n@@ -35,8 +35,11 @@ static int rzn1_dwmac_pcs_init(struct stmmac_priv *priv)\n \n static void rzn1_dwmac_pcs_exit(struct stmmac_priv *priv)\n {\n-\tif (priv-\u003ehw-\u003ephylink_pcs)\n-\t\tmiic_destroy(priv-\u003ehw-\u003ephylink_pcs);\n+\tif (!priv-\u003ehw-\u003ephylink_pcs)\n+\t\treturn;\n+\n+\tmiic_destroy(priv-\u003ehw-\u003ephylink_pcs);\n+\tpriv-\u003ehw-\u003ephylink_pcs = NULL;\n }\n \n static struct phylink_pcs *rzn1_dwmac_select_pcs(struct stmmac_priv *priv,\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c\nindex 1d7f0a57d28897..6d4bc1fe8f7511 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c\n@@ -539,8 +539,11 @@ static int socfpga_dwmac_pcs_init(struct stmmac_priv *priv)\n \n static void socfpga_dwmac_pcs_exit(struct stmmac_priv *priv)\n {\n-\tif (priv-\u003ehw-\u003ephylink_pcs)\n-\t\tlynx_pcs_destroy(priv-\u003ehw-\u003ephylink_pcs);\n+\tif (!priv-\u003ehw-\u003ephylink_pcs)\n+\t\treturn;\n+\n+\tlynx_pcs_destroy(priv-\u003ehw-\u003ephylink_pcs);\n+\tpriv-\u003ehw-\u003ephylink_pcs = NULL;\n }\n \n static struct phylink_pcs *socfpga_dwmac_select_pcs(struct stmmac_priv *priv,\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c\nindex afe98ff5bdcb05..d2f77f0c223a74 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c\n@@ -426,36 +426,15 @@ int stmmac_mdio_reset(struct mii_bus *bus)\n int stmmac_pcs_setup(struct net_device *ndev)\n {\n \tstruct stmmac_priv *priv = netdev_priv(ndev);\n-\tstruct fwnode_handle *devnode, *pcsnode;\n-\tstruct dw_xpcs *xpcs = NULL;\n-\tint addr, ret;\n-\n-\tdevnode = dev_fwnode(priv-\u003edevice);\n-\n-\tif (priv-\u003eplat-\u003epcs_init) {\n-\t\tret = priv-\u003eplat-\u003epcs_init(priv);\n-\t} else if (fwnode_property_present(devnode, \"pcs-handle\")) {\n-\t\tpcsnode = fwnode_find_reference(devnode, \"pcs-handle\", 0);\n-\t\txpcs = xpcs_create_fwnode(pcsnode);\n-\t\tfwnode_handle_put(pcsnode);\n-\t\tret = PTR_ERR_OR_ZERO(xpcs);\n-\t} else if (priv-\u003eplat-\u003emdio_bus_data \u0026\u0026\n-\t\t priv-\u003eplat-\u003emdio_bus_data-\u003epcs_mask) {\n-\t\taddr = ffs(priv-\u003eplat-\u003emdio_bus_data-\u003epcs_mask) - 1;\n-\t\txpcs = xpcs_create_mdiodev(priv-\u003emii, addr);\n-\t\tret = PTR_ERR_OR_ZERO(xpcs);\n-\t} else {\n+\tint ret;\n+\n+\tif (!priv-\u003eplat-\u003epcs_init)\n \t\treturn 0;\n-\t}\n \n+\tret = priv-\u003eplat-\u003epcs_init(priv);\n \tif (ret)\n \t\treturn dev_err_probe(priv-\u003edevice, ret, \"No xPCS found\\n\");\n \n-\tif (xpcs)\n-\t\txpcs_config_eee_mult_fact(xpcs, priv-\u003eplat-\u003emult_fact_100ns);\n-\n-\tpriv-\u003ehw-\u003expcs = xpcs;\n-\n \treturn 0;\n }\n \n@@ -463,14 +442,10 @@ void stmmac_pcs_clean(struct net_device *ndev)\n {\n \tstruct stmmac_priv *priv = netdev_priv(ndev);\n \n-\tif (priv-\u003eplat-\u003epcs_exit)\n-\t\tpriv-\u003eplat-\u003epcs_exit(priv);\n-\n-\tif (!priv-\u003ehw-\u003expcs)\n+\tif (!priv-\u003eplat-\u003epcs_exit)\n \t\treturn;\n \n-\txpcs_destroy(priv-\u003ehw-\u003expcs);\n-\tpriv-\u003ehw-\u003expcs = NULL;\n+\tpriv-\u003eplat-\u003epcs_exit(priv);\n }\n \n struct stmmac_clk_rate {\ndiff --git a/drivers/net/pcs/Kconfig b/drivers/net/pcs/Kconfig\nindex e417fd66f660ad..3286bc93e70264 100644\n--- a/drivers/net/pcs/Kconfig\n+++ b/drivers/net/pcs/Kconfig\n@@ -12,6 +12,28 @@ config PCS_XPCS\n \t This module provides a driver and helper functions for Synopsys\n \t DesignWare XPCS controllers.\n \n+if PCS_XPCS\n+\n+config PCS_XPCS_PLATFORM\n+\ttristate \"Generic XPCS controller support\"\n+\tdefault PCS_XPCS\n+\thelp\n+\t Generic DWXPCS driver for platforms that don't require any\n+\t platform specific code to function or is using platform\n+\t data for setup.\n+\n+\t If you have a controller with this interface, say Y or M here.\n+\n+config PCS_XPCS_ROCKCHIP\n+\ttristate \"Rockchip XPCS controller support\"\n+\tdepends on OF \u0026\u0026 (ARCH_ROCKCHIP || COMPILE_TEST)\n+\thelp\n+\t Support for XPCS controller on Rockchip RK356x SoC.\n+\n+\t If you have a Rockchip SoC with this interface, say Y or M here.\n+\n+endif # PCS_XPCS\n+\n config PCS_LYNX\n \ttristate\n \thelp\ndiff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile\nindex 4f7920618b9005..c809b7f942a518 100644\n--- a/drivers/net/pcs/Makefile\n+++ b/drivers/net/pcs/Makefile\n@@ -1,10 +1,9 @@\n # SPDX-License-Identifier: GPL-2.0\n # Makefile for Linux PCS drivers\n \n-pcs_xpcs-$(CONFIG_PCS_XPCS)\t:= pcs-xpcs.o pcs-xpcs-plat.o \\\n-\t\t\t\t pcs-xpcs-nxp.o pcs-xpcs-wx.o\n-\n-obj-$(CONFIG_PCS_XPCS)\t\t+= pcs_xpcs.o\n+obj-$(CONFIG_PCS_XPCS) += pcs-xpcs.o pcs-xpcs-nxp.o pcs-xpcs-wx.o\n+obj-$(CONFIG_PCS_XPCS_PLATFORM) += pcs-xpcs-plat.o\n+obj-$(CONFIG_PCS_XPCS_ROCKCHIP) += pcs-xpcs-rk.o\n obj-$(CONFIG_PCS_LYNX)\t\t+= pcs-lynx.o\n obj-$(CONFIG_PCS_MTK_LYNXI)\t+= pcs-mtk-lynxi.o\n obj-$(CONFIG_PCS_RZN1_MIIC)\t+= pcs-rzn1-miic.o\ndiff --git a/drivers/net/pcs/pcs-xpcs-rk.c b/drivers/net/pcs/pcs-xpcs-rk.c\nnew file mode 100644\nindex 00000000000000..ed969a38d544d5\n--- /dev/null\n+++ b/drivers/net/pcs/pcs-xpcs-rk.c\n@@ -0,0 +1,526 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Rockchip XPCS platform device driver\n+ *\n+ * Based on the Synopsys DesignWare XPCS platform driver.\n+ * Copyright (C) 2024 Serge Semin\n+ *\n+ * Adapted for Rockchip SoCs, with reference to the Rockchip OEM driver.\n+ * Copyright (C) 2026 Coia Prant\n+ */\n+\n+#include \u003clinux/atomic.h\u003e\n+#include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/clk.h\u003e\n+#include \u003clinux/device.h\u003e\n+#include \u003clinux/io.h\u003e\n+#include \u003clinux/iopoll.h\u003e\n+#include \u003clinux/mdio.h\u003e\n+#include \u003clinux/module.h\u003e\n+#include \u003clinux/of.h\u003e\n+#include \u003clinux/of_platform.h\u003e\n+#include \u003clinux/pcs/pcs-xpcs-rk.h\u003e\n+#include \u003clinux/phy.h\u003e\n+#include \u003clinux/phy/phy.h\u003e\n+#include \u003clinux/platform_device.h\u003e\n+#include \u003clinux/pm_domain.h\u003e\n+#include \u003clinux/pm_runtime.h\u003e\n+#include \u003clinux/property.h\u003e\n+#include \u003clinux/sizes.h\u003e\n+\n+#include \"pcs-xpcs.h\"\n+\n+struct dw_xpcs_rk {\n+\tstruct platform_device *pdev;\n+\tstruct mii_bus *bus;\n+\tvoid __iomem *reg_base;\n+\tstruct phy *serdes_phy;\n+\tstruct clk *csr_clk;\n+\tstruct clk *eee_clk;\n+};\n+\n+static ptrdiff_t xpcs_rk_addr_format(int dev, int reg)\n+{\n+\treturn FIELD_PREP(0x70000, dev) | FIELD_PREP(0xffff, reg);\n+}\n+\n+static int xpcs_rk_read_reg(struct dw_xpcs_rk *pxpcs, int dev, int reg)\n+{\n+\tptrdiff_t csr;\n+\tint ret;\n+\n+\tcsr = xpcs_rk_addr_format(dev, reg);\n+\n+\tret = pm_runtime_resume_and_get(\u0026pxpcs-\u003epdev-\u003edev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = readl(pxpcs-\u003ereg_base + (csr \u003c\u003c 2)) \u0026 0xffff;\n+\n+\tpm_runtime_put(\u0026pxpcs-\u003epdev-\u003edev);\n+\treturn ret;\n+}\n+\n+static int xpcs_rk_write_reg(struct dw_xpcs_rk *pxpcs, int dev, int reg, u16 val)\n+{\n+\tptrdiff_t csr;\n+\tint ret;\n+\n+\tcsr = xpcs_rk_addr_format(dev, reg);\n+\n+\tret = pm_runtime_resume_and_get(\u0026pxpcs-\u003epdev-\u003edev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\twritel(val, pxpcs-\u003ereg_base + (csr \u003c\u003c 2));\n+\n+\tpm_runtime_put(\u0026pxpcs-\u003epdev-\u003edev);\n+\treturn 0;\n+}\n+\n+#define ROCKCHIP_MMD_MII1\t2\n+#define ROCKCHIP_MMD_MII2\t3\n+#define ROCKCHIP_MMD_MII3\t4\n+#define ROCKCHIP_MMD_PMAPMD\t6\n+#define ROCKCHIP_MMD_MII\t7\n+\n+static bool xpcs_rk_mdio_addr_validate(int addr)\n+{\n+\treturn !(addr \u003c 0 || addr \u003e 3);\n+}\n+\n+static int xpcs_rk_mdio_read_remapping(int addr, int dev, int reg)\n+{\n+\tswitch (dev) {\n+\tcase MDIO_MMD_PMAPMD:\n+\t\treturn ROCKCHIP_MMD_PMAPMD;\n+\tcase MDIO_MMD_VEND2:\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -ENXIO;\n+\t}\n+\n+\tswitch (addr) {\n+\tcase 0:\n+\t\treturn ROCKCHIP_MMD_MII;\n+\tcase 1:\n+\t\treturn ROCKCHIP_MMD_MII1;\n+\tcase 2:\n+\t\treturn ROCKCHIP_MMD_MII2;\n+\tcase 3:\n+\t\treturn ROCKCHIP_MMD_MII3;\n+\tdefault:\n+\t\treturn -ENODEV;\n+\t}\n+}\n+\n+static int xpcs_rk_mdio_write_remapping(int addr, int dev, int reg)\n+{\n+\tswitch (dev) {\n+\tcase MDIO_MMD_PMAPMD:\n+\t\treturn ROCKCHIP_MMD_PMAPMD;\n+\tcase MDIO_MMD_VEND2:\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -ENXIO;\n+\t}\n+\n+\t/* Writable only on MII */\n+\tswitch (reg) {\n+\tcase DW_VR_MII_AN_CTRL:\n+\tcase DW_VR_MII_AN_INTR_STS:\n+\tcase DW_VR_MII_EEE_MCTRL0:\n+\tcase DW_VR_MII_EEE_MCTRL1:\n+\tcase DW_VR_MII_DIG_CTRL2:\n+\t\treturn ROCKCHIP_MMD_MII;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\tswitch (addr) {\n+\tcase 0:\n+\t\treturn ROCKCHIP_MMD_MII;\n+\tcase 1:\n+\t\treturn ROCKCHIP_MMD_MII1;\n+\tcase 2:\n+\t\treturn ROCKCHIP_MMD_MII2;\n+\tcase 3:\n+\t\treturn ROCKCHIP_MMD_MII3;\n+\tdefault:\n+\t\treturn -ENODEV;\n+\t}\n+}\n+\n+static int xpcs_rk_read_c22(struct mii_bus *bus, int addr, int reg)\n+{\n+\tstruct dw_xpcs_rk *pxpcs = bus-\u003epriv;\n+\tint dev;\n+\n+\tif (!xpcs_rk_mdio_addr_validate(addr))\n+\t\treturn -ENODEV;\n+\n+\tdev = xpcs_rk_mdio_read_remapping(addr, MDIO_MMD_VEND2, reg);\n+\tif (dev \u003c 0)\n+\t\treturn 0xffff;\n+\n+\treturn xpcs_rk_read_reg(pxpcs, dev, reg);\n+}\n+\n+static int xpcs_rk_write_c22(struct mii_bus *bus, int addr, int reg, u16 val)\n+{\n+\tstruct dw_xpcs_rk *pxpcs = bus-\u003epriv;\n+\tint dev;\n+\n+\tif (!xpcs_rk_mdio_addr_validate(addr))\n+\t\treturn -ENODEV;\n+\n+\tdev = xpcs_rk_mdio_write_remapping(addr, MDIO_MMD_VEND2, reg);\n+\tif (dev \u003c 0)\n+\t\treturn 0;\n+\n+\treturn xpcs_rk_write_reg(pxpcs, dev, reg, val);\n+}\n+\n+static int xpcs_rk_read_c45(struct mii_bus *bus, int addr, int dev, int reg)\n+{\n+\tstruct dw_xpcs_rk *pxpcs = bus-\u003epriv;\n+\n+\tif (!xpcs_rk_mdio_addr_validate(addr))\n+\t\treturn -ENODEV;\n+\n+\tdev = xpcs_rk_mdio_read_remapping(addr, dev, reg);\n+\tif (dev \u003c 0)\n+\t\treturn 0xffff;\n+\n+\treturn xpcs_rk_read_reg(pxpcs, dev, reg);\n+}\n+\n+static int xpcs_rk_write_c45(struct mii_bus *bus, int addr, int dev, int reg, u16 val)\n+{\n+\tstruct dw_xpcs_rk *pxpcs = bus-\u003epriv;\n+\n+\tif (!xpcs_rk_mdio_addr_validate(addr))\n+\t\treturn -ENODEV;\n+\n+\tdev = xpcs_rk_mdio_write_remapping(addr, dev, reg);\n+\tif (dev \u003c 0)\n+\t\treturn 0;\n+\n+\treturn xpcs_rk_write_reg(pxpcs, dev, reg, val);\n+}\n+\n+static struct dw_xpcs_rk *xpcs_rk_create_data(struct platform_device *pdev)\n+{\n+\tstruct dw_xpcs_rk *pxpcs;\n+\n+\tpxpcs = devm_kzalloc(\u0026pdev-\u003edev, sizeof(*pxpcs), GFP_KERNEL);\n+\tif (!pxpcs)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tpxpcs-\u003epdev = pdev;\n+\n+\tdev_set_drvdata(\u0026pdev-\u003edev, pxpcs);\n+\n+\treturn pxpcs;\n+}\n+\n+static int xpcs_rk_serdes_phy_init(struct dw_xpcs_rk *pxpcs)\n+{\n+\tstruct device *dev = \u0026pxpcs-\u003epdev-\u003edev;\n+\n+\tpxpcs-\u003eserdes_phy = devm_phy_get(dev, \"serdes\");\n+\tif (IS_ERR(pxpcs-\u003eserdes_phy))\n+\t\treturn dev_err_probe(dev, PTR_ERR(pxpcs-\u003eserdes_phy),\n+\t\t\t\t\t\"Failed to get SerDes PHY\\n\");\n+\n+\treturn 0;\n+}\n+\n+static void xpcs_rk_serdes_phy_poweroff(void *data)\n+{\n+\tstruct dw_xpcs_rk *pxpcs = data;\n+\tstruct device *dev = \u0026pxpcs-\u003epdev-\u003edev;\n+\n+\tphy_power_off(pxpcs-\u003eserdes_phy);\n+\tphy_exit(pxpcs-\u003eserdes_phy);\n+\n+\tdev_pm_genpd_rpm_always_on(dev, false);\n+}\n+\n+static int xpcs_rk_serdes_phy_poweron(struct dw_xpcs_rk *pxpcs)\n+{\n+\tstruct device *dev = \u0026pxpcs-\u003epdev-\u003edev;\n+\tint ret;\n+\n+\tret = dev_pm_genpd_rpm_always_on(dev, true);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to power on power-domains\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = phy_init(pxpcs-\u003eserdes_phy);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to init SerDes PHY\\n\");\n+\t\tgoto pm_domain;\n+\t}\n+\n+\tret = phy_power_on(pxpcs-\u003eserdes_phy);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to power on SerDes PHY\\n\");\n+\t\tgoto serdes_phy;\n+\t}\n+\n+\tret = devm_add_action_or_reset(dev, xpcs_rk_serdes_phy_poweroff, pxpcs);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to register devm for SerDes PHY: %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+\n+serdes_phy:\n+\tphy_exit(pxpcs-\u003eserdes_phy);\n+pm_domain:\n+\tdev_pm_genpd_rpm_always_on(dev, false);\n+\treturn ret;\n+}\n+\n+static int xpcs_rk_init_res(struct dw_xpcs_rk *pxpcs)\n+{\n+\tstruct platform_device *pdev = pxpcs-\u003epdev;\n+\tstruct device *dev = \u0026pdev-\u003edev;\n+\tstruct resource *res;\n+\n+\tres = platform_get_resource(pdev, IORESOURCE_MEM, 0);\n+\tif (!res) {\n+\t\tdev_err(dev, \"No reg-space found\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (resource_size(res) \u003c SZ_2M) {\n+\t\tdev_err(dev, \"Invalid reg-space size\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tpxpcs-\u003ereg_base = devm_ioremap_resource(dev, res);\n+\tif (IS_ERR(pxpcs-\u003ereg_base)) {\n+\t\tdev_err(dev, \"Failed to map reg-space\\n\");\n+\t\treturn PTR_ERR(pxpcs-\u003ereg_base);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void xpcs_rk_exit_clk(void *data)\n+{\n+\tstruct dw_xpcs_rk *pxpcs = data;\n+\n+\tclk_disable_unprepare(pxpcs-\u003eeee_clk);\n+}\n+\n+static int xpcs_rk_init_clk(struct dw_xpcs_rk *pxpcs)\n+{\n+\tstruct device *dev = \u0026pxpcs-\u003epdev-\u003edev;\n+\tint ret;\n+\n+\tpxpcs-\u003ecsr_clk = devm_clk_get(dev, \"csr\");\n+\tif (IS_ERR(pxpcs-\u003ecsr_clk))\n+\t\treturn dev_err_probe(dev, PTR_ERR(pxpcs-\u003ecsr_clk),\n+\t\t\t\t\t \"Failed to get CSR clock\\n\");\n+\n+\tpm_runtime_set_suspended(dev);\n+\tret = devm_pm_runtime_enable(dev);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to enable runtime-PM\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tpxpcs-\u003eeee_clk = devm_clk_get(dev, \"eee\");\n+\tif (IS_ERR(pxpcs-\u003eeee_clk))\n+\t\treturn dev_err_probe(dev, PTR_ERR(pxpcs-\u003eeee_clk),\n+\t\t\t\t\t \"Failed to get EEE clock\\n\");\n+\n+\tret = clk_prepare_enable(pxpcs-\u003eeee_clk);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to enable EEE clock\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = devm_add_action_or_reset(dev, xpcs_rk_exit_clk, pxpcs);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to register devm for EEE clock: %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int xpcs_rk_init_bus(struct dw_xpcs_rk *pxpcs)\n+{\n+\tstruct device *dev = \u0026pxpcs-\u003epdev-\u003edev;\n+\tstatic atomic_t id = ATOMIC_INIT(-1);\n+\tstruct mii_bus *bus;\n+\tint ret;\n+\n+\tbus = devm_mdiobus_alloc_size(dev, 0);\n+\tif (!bus)\n+\t\treturn -ENOMEM;\n+\n+\tbus-\u003ename = \"Rockchip DW XPCS MCI/APB3\";\n+\tbus-\u003eread = xpcs_rk_read_c22;\n+\tbus-\u003ewrite = xpcs_rk_write_c22;\n+\tbus-\u003eread_c45 = xpcs_rk_read_c45;\n+\tbus-\u003ewrite_c45 = xpcs_rk_write_c45;\n+\tbus-\u003ephy_mask = ~0;\n+\tbus-\u003eparent = dev;\n+\tbus-\u003epriv = pxpcs;\n+\n+\tsnprintf(bus-\u003eid, MII_BUS_ID_SIZE,\n+\t\t \"rockchip_dwxpcs-%x\", atomic_inc_return(\u0026id));\n+\n+\t/*\n+\t * MDIO-bus here serves as just a back-end engine abstracting out\n+\t * the MDIO and MCI/APB3 IO interfaces utilized for the Rockchip DWXPCS CSRs\n+\t * access.\n+\t */\n+\tret = devm_mdiobus_register(dev, bus);\n+\tif (ret) {\n+\t\tdev_err(dev, \"Failed to create MDIO bus\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tpxpcs-\u003ebus = bus;\n+\treturn 0;\n+}\n+\n+static int xpcs_rk_probe(struct platform_device *pdev)\n+{\n+\tstruct dw_xpcs_rk *pxpcs;\n+\tint ret;\n+\n+\tpxpcs = xpcs_rk_create_data(pdev);\n+\tif (IS_ERR(pxpcs))\n+\t\treturn PTR_ERR(pxpcs);\n+\n+\t/*\n+\t * The XPCS may be attached to a power domain (e.g. PD_PIPE). The domain\n+\t * must be powered on before any register access, otherwise the SoC will\n+\t * trigger a synchronous external abort (SError).\n+\t *\n+\t * Accessing the XPCS registers also requires a TX clock from the SerDes,\n+\t * which is needed for the soft reset.\n+\t */\n+\tret = xpcs_rk_serdes_phy_init(pxpcs);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = xpcs_rk_serdes_phy_poweron(pxpcs);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = xpcs_rk_init_res(pxpcs);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = xpcs_rk_init_clk(pxpcs);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = xpcs_rk_init_bus(pxpcs);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+struct dw_xpcs *xpcs_rk_create(struct device *dev, struct device_node *np)\n+{\n+\tstruct platform_device *pdev;\n+\tstruct device_node *pcs_np;\n+\tstruct dw_xpcs_rk *pxpcs;\n+\tstruct dw_xpcs *xpcs;\n+\tu32 port;\n+\n+\tif (!of_device_is_available(np))\n+\t\treturn ERR_PTR(-ENODEV);\n+\n+\tif (of_property_read_u32(np, \"reg\", \u0026port))\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\tif (!xpcs_rk_mdio_addr_validate((int)port))\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\t/* The XPCS pdev is attached to the parent node */\n+\tpcs_np = of_get_parent(np);\n+\tif (!pcs_np)\n+\t\treturn ERR_PTR(-ENODEV);\n+\n+\tif (!of_device_is_available(pcs_np)) {\n+\t\tof_node_put(pcs_np);\n+\t\treturn ERR_PTR(-ENODEV);\n+\t}\n+\n+\tpdev = of_find_device_by_node(pcs_np);\n+\tof_node_put(pcs_np);\n+\tif (!pdev)\n+\t\treturn ERR_PTR(-EPROBE_DEFER);\n+\n+\tpxpcs = platform_get_drvdata(pdev);\n+\tif (!pxpcs || !pxpcs-\u003ebus) {\n+\t\tput_device(\u0026pdev-\u003edev);\n+\t\treturn ERR_PTR(-EPROBE_DEFER);\n+\t}\n+\n+\txpcs = xpcs_create_mdiodev(pxpcs-\u003ebus, (int)port);\n+\tif (IS_ERR(xpcs)) {\n+\t\tput_device(\u0026pdev-\u003edev);\n+\t\treturn xpcs;\n+\t}\n+\n+\tdevice_link_add(dev, \u0026pdev-\u003edev, DL_FLAG_AUTOREMOVE_CONSUMER);\n+\tput_device(\u0026pdev-\u003edev);\n+\treturn xpcs;\n+}\n+EXPORT_SYMBOL_GPL(xpcs_rk_create);\n+\n+static int __maybe_unused xpcs_rk_pm_runtime_suspend(struct device *dev)\n+{\n+\tstruct dw_xpcs_rk *pxpcs = dev_get_drvdata(dev);\n+\n+\tclk_disable_unprepare(pxpcs-\u003ecsr_clk);\n+\n+\treturn 0;\n+}\n+\n+static int __maybe_unused xpcs_rk_pm_runtime_resume(struct device *dev)\n+{\n+\tstruct dw_xpcs_rk *pxpcs = dev_get_drvdata(dev);\n+\n+\treturn clk_prepare_enable(pxpcs-\u003ecsr_clk);\n+}\n+\n+static const struct dev_pm_ops xpcs_rk_pm_ops = {\n+\tSET_RUNTIME_PM_OPS(xpcs_rk_pm_runtime_suspend,\n+\t\t\t xpcs_rk_pm_runtime_resume,\n+\t\t\t NULL)\n+};\n+\n+static const struct of_device_id xpcs_rk_of_ids[] = {\n+\t{ .compatible = \"rockchip,rk3568-xpcs\" },\n+\t{ /* sentinel */ },\n+};\n+MODULE_DEVICE_TABLE(of, xpcs_rk_of_ids);\n+\n+static struct platform_driver xpcs_rk_driver = {\n+\t.probe = xpcs_rk_probe,\n+\t.driver = {\n+\t\t.name = \"rk_xpcs-dwxpcs\",\n+\t\t.pm = \u0026xpcs_rk_pm_ops,\n+\t\t.of_match_table = xpcs_rk_of_ids,\n+\t},\n+};\n+module_platform_driver(xpcs_rk_driver);\n+\n+MODULE_DESCRIPTION(\"Rockchip XPCS platform device driver\");\n+MODULE_AUTHOR(\"Coia Prant \u003ccoiaprant@gmail.com\u003e\");\n+MODULE_LICENSE(\"GPL\");\ndiff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c\nindex e69fa2f0a0e8d8..cf370ba247cacf 100644\n--- a/drivers/net/pcs/pcs-xpcs.c\n+++ b/drivers/net/pcs/pcs-xpcs.c\n@@ -816,6 +816,11 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,\n \tif (ret \u003c 0)\n \t\treturn ret;\n \n+\t/* Clear CL37 AN complete status */\n+\tret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n \tif (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)\n \t\tret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,\n \t\t\t\t mdio_ctrl | BMCR_ANENABLE);\n@@ -884,7 +889,7 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,\n \n \tif (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {\n \t\tret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,\n-\t\t\t\t mdio_ctrl | BMCR_ANENABLE);\n+\t\t\t\t mdio_ctrl | BMCR_ANENABLE | BMCR_ANRESTART);\n \t\tif (ret \u003c 0)\n \t\t\treturn ret;\n \t}\n@@ -1058,6 +1063,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,\n \n \t/* Reset link_state */\n \tstate-\u003elink = false;\n+\tstate-\u003ean_complete = false;\n \tstate-\u003espeed = SPEED_UNKNOWN;\n \tstate-\u003eduplex = DUPLEX_UNKNOWN;\n \tstate-\u003epause = 0;\n@@ -1069,6 +1075,8 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,\n \tif (ret \u003c 0)\n \t\treturn ret;\n \n+\tstate-\u003ean_complete = ret \u0026 DW_VR_MII_AN_STS_C37_ANCMPLT_INTR;\n+\n \tif (ret \u0026 DW_VR_MII_C37_ANSGM_SP_LNKSTS) {\n \t\tint speed_value;\n \n@@ -1086,7 +1094,24 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,\n \t\t\tstate-\u003eduplex = DUPLEX_FULL;\n \t\telse\n \t\t\tstate-\u003eduplex = DUPLEX_HALF;\n-\t} else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* Clear AN complete status or interrupt */\n+\tif (state-\u003ean_complete)\n+\t\txpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);\n+\n+\tif (xpcs-\u003einfo.pma != WX_TXGBE_XPCS_PMA_10G_ID) {\n+\t\t/* If the link down, restart Auto-Negotiation */\n+\t\tif (state-\u003ean_complete)\n+\t\t\txpcs_modify(xpcs, MDIO_MMD_VEND2, MII_BMCR, BMCR_ANRESTART,\n+\t\t\t\t BMCR_ANRESTART);\n+\n+\t\treturn 0;\n+\t}\n+\n+\tif (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {\n \t\tint speed, duplex;\n \n \t\tstate-\u003elink = true;\n@@ -1112,7 +1137,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,\n \t\telse if (duplex \u0026 ADVERTISE_1000XHALF)\n \t\t\tstate-\u003eduplex = DUPLEX_HALF;\n \n-\t\txpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);\n+\t\treturn 0;\n \t}\n \n \treturn 0;\ndiff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c\nindex 2b0f152f547091..ff290bc18589ad 100644\n--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c\n+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c\n@@ -186,6 +186,7 @@ struct rockchip_combphy_grfcfg {\n \tstruct combphy_reg pipe_xpcs_phy_ready;\n \tstruct combphy_reg pipe_pcie1l0_sel;\n \tstruct combphy_reg pipe_pcie1l1_sel;\n+\tstruct combphy_reg pipe_sgmii_mac_sel;\n \tstruct combphy_reg u3otg0_port_en;\n \tstruct combphy_reg u3otg1_port_en;\n };\n@@ -212,6 +213,7 @@ struct rockchip_combphy_priv {\n \tbool enable_ssc;\n \tbool ext_refclk;\n \tstruct clk *refclk;\n+\tu32 sgmii_mac_sel;\n };\n \n static void rockchip_combphy_updatel(struct rockchip_combphy_priv *priv,\n@@ -375,6 +377,9 @@ static int rockchip_combphy_parse_dt(struct device *dev, struct rockchip_combphy\n \n \tpriv-\u003eext_refclk = device_property_present(dev, \"rockchip,ext-refclk\");\n \n+\tpriv-\u003esgmii_mac_sel = 0;\n+\tdevice_property_read_u32(dev, \"rockchip,sgmii-mac-sel\", \u0026priv-\u003esgmii_mac_sel);\n+\n \tpriv-\u003ephy_rst = devm_reset_control_get_exclusive(dev, \"phy\");\n \t/* fallback to old behaviour */\n \tif (PTR_ERR(priv-\u003ephy_rst) == -ENOENT)\n@@ -873,6 +878,8 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)\n \t\tbreak;\n \n \tcase PHY_TYPE_SGMII:\n+\t\trockchip_combphy_param_write(priv-\u003epipe_grf, \u0026cfg-\u003epipe_sgmii_mac_sel,\n+\t\t\t\t\tpriv-\u003esgmii_mac_sel \u003e 0);\n \t\trockchip_combphy_param_write(priv-\u003epipe_grf, \u0026cfg-\u003epipe_xpcs_phy_ready, true);\n \t\trockchip_combphy_param_write(priv-\u003ephy_grf, \u0026cfg-\u003epipe_phymode_sel, true);\n \t\trockchip_combphy_param_write(priv-\u003ephy_grf, \u0026cfg-\u003epipe_sel_qsgmii, true);\n@@ -984,6 +991,7 @@ static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = {\n \t.con3_for_sata\t\t= { 0x000c, 15, 0, 0x00, 0x4407 },\n \t/* pipe-grf */\n \t.pipe_con0_for_sata\t= { 0x0000, 15, 0, 0x00, 0x2220 },\n+\t.pipe_sgmii_mac_sel\t= { 0x0040, 1, 1, 0x00, 0x01 },\n \t.pipe_xpcs_phy_ready\t= { 0x0040, 2, 2, 0x00, 0x01 },\n \t.u3otg0_port_en\t\t= { 0x0104, 15, 0, 0x0181, 0x1100 },\n \t.u3otg1_port_en\t\t= { 0x0144, 15, 0, 0x0181, 0x1100 },\ndiff --git a/include/linux/pcs/pcs-xpcs-rk.h b/include/linux/pcs/pcs-xpcs-rk.h\nnew file mode 100644\nindex 00000000000000..28723d5bd75cce\n--- /dev/null\n+++ b/include/linux/pcs/pcs-xpcs-rk.h\n@@ -0,0 +1,11 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef __LINUX_PCS_XPCS_ROCKCHIP_H\n+#define __LINUX_PCS_XPCS_ROCKCHIP_H\n+\n+#include \u003clinux/device.h\u003e\n+#include \u003clinux/of.h\u003e\n+#include \u003clinux/pcs/pcs-xpcs.h\u003e\n+\n+struct dw_xpcs *xpcs_rk_create(struct device *dev, struct device_node *np);\n+\n+#endif /* __LINUX_PCS_XPCS_ROCKCHIP_H */\n"
} |
| 2/1 | 2026/07/15 05:42 | agent | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"STMMAC_ETH",
"DWMAC_INTEL",
"PCS_XPCS",
"PCS_XPCS_ROCKCHIP",
"DWMAC_ROCKCHIP",
"PHY_ROCKCHIP_NANENG_COMBO_PHY"
],
"FocusSymbols": [
"xpcs_config_aneg_c37_sgmii",
"xpcs_config_aneg_c37_1000basex",
"xpcs_get_state_c37_sgmii",
"intel_mgbe_pcs_init",
"stmmac_pcs_setup",
"rk_pcs_init",
"xpcs_rk_probe",
"xpcs_rk_create"
],
"Reasoning": "The patch introduces support for the Rockchip RK3568 XPCS, which includes a new platform driver and modifications to the generic pcs-xpcs driver and stmmac driver. While the Rockchip-specific platform driver might be difficult to reach in a generic virtualized environment without specific device tree configurations, the patch also refactors how the PCS is initialized in the generic stmmac driver and modifies the Intel mGBE PCI driver (dwmac-intel.c) to use the new initialization flow. Furthermore, it modifies the generic pcs-xpcs.c driver's auto-negotiation and state polling logic for SGMII and 1000BASE-X interfaces. These generic and PCI-based changes are reachable via PCI fuzzing and netlink interface manipulation, making the patch worth fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). 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. 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. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. 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 7193ed0e0aacf4d35974c523bd45360f98c52e8d
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 15 05:42:26 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/devicetree/bindings/net/pcs/rockchip-dwxpcs.yaml b/Documentation/devicetree/bindings/net/pcs/rockchip-dwxpcs.yaml
new file mode 100644
index 00000000000000..14fadf67c793a8
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/pcs/rockchip-dwxpcs.yaml
@@ -0,0 +1,126 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/pcs/rockchip-dwxpcs.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip RK3568 Synopsys DesignWare Ethernet PCS
+
+maintainers:
+ - Coia Prant <coiaprant@gmail.com>
+
+description: |
+ Rockchip RK3568 SoC integrates a Synopsys DesignWare Ethernet Physical
+ Coding Sublayer (XPCS).
+ The PCS provides an interface between the Media Access Control (MAC)
+ and the Physical Medium Attachment (PMA) sublayer through a Media
+ Independent Interface (GMII).
+
+ The XPCS is accessed over the APB3 bus and internally connected to a
+ Naneng Combo SerDes PHY.
+ It supports 1000BASE-X, SGMII and QSGMII modes.
+
+ The block contains four MII ports (pcs-mii@0..3) that can be
+ individually enabled and routed to one of the Ethernet GMAC controllers
+ via the pcs-handle property in the MAC device tree node.
+
+properties:
+ compatible:
+ const: rockchip,rk3568-xpcs
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ reg:
+ description: |
+ Base address and size of the XPCS register space mapped over the
+ APB3 bus.
+ maxItems: 1
+
+ clocks:
+ description: |
+ Clock sources for the XPCS:
+ - csr: APB3 bus interface clock (clk_csr_i), required for register
+ access.
+ - eee: EEE clock (clk_eee_i), required for Energy Efficient
+ Ethernet (EEE) operation.
+ minItems: 2
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: csr
+ - const: eee
+
+ phys:
+ description: |
+ The phandle of SerDes PHY (Naneng Combo PHY) that provides
+ the serial lanes for 1000BASE-X / SGMII / QSGMII.
+ The SerDes must be powered on and initialised before any XPCS
+ register access.
+ maxItems: 1
+
+ phy-names:
+ const: serdes
+
+ power-domains:
+ description: |
+ Power domain for the XPCS.
+ On RK3568 this is typically the PD_PIPE power domain, which also
+ supplies the SerDes PHY.
+ maxItems: 1
+
+patternProperties:
+ "^pcs-mii@[0-3]$":
+ type: object
+ description: |
+ One of the four MII ports of the XPCS.
+ The port number is specified by the reg property (0..3).
+ The port is linked to an Ethernet MAC controller via the
+ pcs-handle property in the MAC's device tree node.
+
+ properties:
+ reg:
+ minimum: 0
+ maximum: 3
+ description: |
+ MII port number of PCS.
+
+ status: true
+
+ required:
+ - reg
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/rk3568-cru.h>
+ #include <dt-bindings/power/rk3568-power.h>
+
+ pcs@fda00000 {
+ compatible = "rockchip,rk3568-xpcs";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0 0xfda00000 0x0 0x200000>;
+ clocks = <&cru PCLK_XPCS>, <&cru CLK_XPCS_EEE>;
+ clock-names = "csr", "eee";
+ phys = <&combphy2 PHY_TYPE_SGMII>;
+ phy-names = "serdes";
+ power-domains = <&power RK3568_PD_PIPE>;
+
+ pcs-mii@0 {
+ reg = <0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml
index 379b08bd9e97a6..6173192e31ab2f 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-naneng-combphy.yaml
@@ -80,6 +80,13 @@ properties:
description:
Some additional pipe settings are accessed through GRF regs.
+ rockchip,sgmii-mac-sel:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ default: 0
+ description:
+ Select gmac0 or gmac1 to be used as SGMII controller.
+
"#phy-cells":
const: 1
diff --git a/MAINTAINERS b/MAINTAINERS
index 806bd2d80d1535..8cba6a0a2dd034 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23463,6 +23463,15 @@ S: Maintained
F: Documentation/devicetree/bindings/sound/rockchip,rk3576-sai.yaml
F: sound/soc/rockchip/rockchip_sai.*
+ROCKCHIP XPCS DRIVER
+M: Coia Prant <coiaprant@gmail.com>
+L: netdev@vger.kernel.org
+L: linux-rockchip@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/net/pcs/rockchip-dwxpcs.yaml
+F: drivers/net/pcs/pcs-xpcs-rk.c
+F: include/linux/pcs/pcs-xpcs-rk.h
+
ROCKER DRIVER
M: Jiri Pirko <jiri@resnulli.us>
L: netdev@vger.kernel.org
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-photonicat.dts b/arch/arm64/boot/dts/rockchip/rk3568-photonicat.dts
index 58c1052ba8ef35..91c17b624fd17d 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-photonicat.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-photonicat.dts
@@ -3,6 +3,7 @@
/dts-v1/;
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/soc/rockchip,vop2.h>
#include "rk3568.dtsi"
@@ -242,6 +243,7 @@ &combphy1 {
&combphy2 {
status = "okay";
+ rockchip,sgmii-mac-sel = <0>;
};
&cpu0 {
@@ -260,9 +262,18 @@ &cpu3 {
cpu-supply = <&vdd_cpu>;
};
-/* Motorcomm YT8521SC LAN port (require SGMII) */
+/* Motorcomm YT8521SC LAN port */
&gmac0 {
- status = "disabled";
+ assigned-clocks = <&cru SCLK_GMAC0_RX_TX>;
+ assigned-clock-parents = <&xpcs_gmac0_clk>;
+ pcs-handle = <&xpcs_mii0>;
+ managed = "in-band-status";
+ phy-handle = <&sgmii_phy>;
+ phy-mode = "sgmii";
+ phy-supply = <&vcc_3v3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac0_miim>;
+ status = "okay";
};
/* Motorcomm YT8521SC WAN port */
@@ -341,6 +352,39 @@ &i2s0_8ch {
status = "okay";
};
+&mdio0 {
+ sgmii_phy: ethernet-phy@3 {
+ compatible = "ethernet-phy-id0000.011a";
+ reg = <0x3>;
+ max-speed = <1000>;
+ eee-broken-10gt;
+ eee-broken-10gkx4;
+ eee-broken-10gkr;
+ reset-assert-us = <20000>;
+ reset-deassert-us = <100000>;
+ reset-gpios = <&gpio3 RK_PC6 GPIO_ACTIVE_LOW>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@1 {
+ reg = <1>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+
+ led@2 {
+ reg = <2>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+ };
+ };
+};
+
&mdio1 {
rgmii_phy: ethernet-phy@3 {
compatible = "ethernet-phy-ieee802.3-c22";
@@ -350,6 +394,25 @@ rgmii_phy: ethernet-phy@3 {
reset-gpios = <&gpio4 RK_PC0 GPIO_ACTIVE_LOW>;
rx-internal-delay-ps = <1500>;
tx-internal-delay-ps = <1500>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@1 {
+ reg = <1>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_WAN;
+ default-state = "keep";
+ };
+
+ led@2 {
+ reg = <2>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_WAN;
+ default-state = "keep";
+ };
+ };
};
};
@@ -586,3 +649,13 @@ &xin32k {
pinctrl-names = "default";
pinctrl-0 = <&clk32k_out1>;
};
+
+&xpcs {
+ status = "okay";
+ phys = <&combphy2 PHY_TYPE_SGMII>;
+ phy-names = "serdes";
+};
+
+&xpcs_mii0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
index 3bc653f027f1fe..989e164c0eb398 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
@@ -110,6 +110,51 @@ sata0: sata@fc000000 {
status = "disabled";
};
+ xpcs: pcs@fda00000 {
+ compatible = "rockchip,rk3568-xpcs";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0 0xfda00000 0x0 0x200000>;
+ clocks = <&cru PCLK_XPCS>, <&cru CLK_XPCS_EEE>;
+ clock-names = "csr", "eee";
+ power-domains = <&power RK3568_PD_PIPE>;
+ status = "disabled";
+
+ xpcs_mii0: pcs-mii@0 {
+ reg = <0>;
+ status = "disabled";
+ };
+
+ xpcs_mii1: pcs-mii@1 {
+ reg = <1>;
+ status = "disabled";
+ };
+
+ xpcs_mii2: pcs-mii@2 {
+ reg = <2>;
+ status = "disabled";
+ };
+
+ xpcs_mii3: pcs-mii@3 {
+ reg = <3>;
+ status = "disabled";
+ };
+ };
+
+ xpcs_gmac0_clk: xpcs-gmac0-clock {
+ compatible = "fixed-clock";
+ clock-frequency = <125000000>;
+ clock-output-names = "clk_gmac0_xpcs_mii";
+ #clock-cells = <0>;
+ };
+
+ xpcs_gmac1_clk: xpcs-gmac1-clock {
+ compatible = "fixed-clock";
+ clock-frequency = <125000000>;
+ clock-output-names = "clk_gmac1_xpcs_mii";
+ #clock-cells = <0>;
+ };
+
pipe_phy_grf0: syscon@fdc70000 {
compatible = "rockchip,rk3568-pipe-phy-grf", "syscon";
reg = <0x0 0xfdc70000 0x0 0x1000>;
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index e3dd5adda5acaf..5088acc06982e0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -170,6 +170,7 @@ config DWMAC_ROCKCHIP
default ARCH_ROCKCHIP
depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
select MFD_SYSCON
+ select PCS_XPCS_ROCKCHIP
help
Support for Ethernet controller on Rockchip RK3288 SoC.
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
index b8d467ba6d72d3..081323c32bcc11 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
@@ -572,13 +572,47 @@ static void common_default_data(struct plat_stmmacenet_data *plat)
plat->mdio_bus_data->needs_reset = true;
}
+static int intel_mgbe_pcs_init(struct stmmac_priv *priv)
+{
+ struct fwnode_handle *devnode, *pcsnode;
+ struct dw_xpcs *xpcs = NULL;
+ int addr;
+
+ devnode = dev_fwnode(priv->device);
+
+ if (fwnode_property_present(devnode, "pcs-handle")) {
+ pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0);
+ xpcs = xpcs_create_fwnode(pcsnode);
+ fwnode_handle_put(pcsnode);
+ } else {
+ addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1;
+ xpcs = xpcs_create_mdiodev(priv->mii, addr);
+ }
+
+ if (IS_ERR(xpcs))
+ return PTR_ERR(xpcs);
+
+ xpcs_config_eee_mult_fact(xpcs, priv->plat->mult_fact_100ns);
+
+ priv->hw->xpcs = xpcs;
+ return 0;
+}
+
+static void intel_mgbe_pcs_exit(struct stmmac_priv *priv)
+{
+ if (!priv->hw->xpcs)
+ return;
+
+ xpcs_destroy(priv->hw->xpcs);
+ priv->hw->xpcs = NULL;
+}
+
static struct phylink_pcs *intel_mgbe_select_pcs(struct stmmac_priv *priv,
phy_interface_t interface)
{
- /* plat->mdio_bus_data->has_xpcs has been set true, so there
- * should always be an XPCS. The original code would always
- * return this if present.
- */
+ if (!priv->hw->xpcs)
+ return NULL;
+
return xpcs_to_phylink_pcs(priv->hw->xpcs);
}
@@ -702,6 +736,8 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
plat->phy_interface == PHY_INTERFACE_MODE_1000BASEX) {
plat->mdio_bus_data->pcs_mask = BIT_U32(INTEL_MGBE_XPCS_ADDR);
plat->default_an_inband = true;
+ plat->pcs_init = intel_mgbe_pcs_init;
+ plat->pcs_exit = intel_mgbe_pcs_exit;
plat->select_pcs = intel_mgbe_select_pcs;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
index 19f34e18bfef2c..9af32c26f9c146 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
@@ -81,8 +81,11 @@ static int renesas_gmac_pcs_init(struct stmmac_priv *priv)
static void renesas_gmac_pcs_exit(struct stmmac_priv *priv)
{
- if (priv->hw->phylink_pcs)
- miic_destroy(priv->hw->phylink_pcs);
+ if (!priv->hw->phylink_pcs)
+ return;
+
+ miic_destroy(priv->hw->phylink_pcs);
+ priv->hw->phylink_pcs = NULL;
}
static struct phylink_pcs *renesas_gmac_select_pcs(struct stmmac_priv *priv,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
index 8d7042e6892619..eca482b4b6bfc0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
+#include <linux/pcs/pcs-xpcs-rk.h>
#include <linux/pm_runtime.h>
#include "stmmac_platform.h"
@@ -47,6 +48,7 @@ struct rk_gmac_ops {
void (*set_to_rgmii)(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay);
void (*set_to_rmii)(struct rk_priv_data *bsp_priv);
+ void (*set_to_sgmii)(struct rk_priv_data *bsp_priv);
int (*set_speed)(struct rk_priv_data *bsp_priv,
phy_interface_t interface, int speed);
void (*integrated_phy_powerup)(struct rk_priv_data *bsp_priv);
@@ -63,6 +65,7 @@ struct rk_gmac_ops {
bool clock_grf_reg_in_php;
bool supports_rgmii;
bool supports_rmii;
+ bool supports_sgmii;
bool php_grf_required;
bool regs_valid;
u32 regs[];
@@ -98,6 +101,7 @@ struct rk_priv_data {
bool integrated_phy;
bool supports_rgmii;
bool supports_rmii;
+ bool supports_sgmii;
struct clk_bulk_data *clks;
int num_clks;
@@ -809,6 +813,8 @@ static const struct rk_gmac_ops rk3528_ops = {
#define RK3568_GRF_GMAC1_CON1 0x038c
/* RK3568_GRF_GMAC0_CON1 && RK3568_GRF_GMAC1_CON1 */
+#define RK3568_GMAC_MODE_RMII_RGMII GRF_CLR_BIT(7)
+#define RK3568_GMAC_MODE_SGMII_QSGMII GRF_BIT(7)
#define RK3568_GMAC_FLOW_CTRL GRF_BIT(3)
#define RK3568_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3)
#define RK3568_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1)
@@ -851,18 +857,32 @@ static void rk3568_set_to_rgmii(struct rk_priv_data *bsp_priv,
RK3568_GMAC_CLK_TX_DL_CFG(tx_delay));
regmap_write(bsp_priv->grf, con1,
+ RK3568_GMAC_MODE_RMII_RGMII |
RK3568_GMAC_RXCLK_DLY_ENABLE |
RK3568_GMAC_TXCLK_DLY_ENABLE);
}
+static void rk3568_set_to_sgmii(struct rk_priv_data *bsp_priv)
+{
+ u32 con1;
+
+ con1 = (bsp_priv->id == 1) ? RK3568_GRF_GMAC1_CON1 :
+ RK3568_GRF_GMAC0_CON1;
+
+ regmap_write(bsp_priv->grf, con1, RK3568_GMAC_MODE_SGMII_QSGMII);
+}
+
static const struct rk_gmac_ops rk3568_ops = {
.init = rk3568_init,
.set_to_rgmii = rk3568_set_to_rgmii,
+ .set_to_sgmii = rk3568_set_to_sgmii,
+
.set_speed = rk_set_clk_mac_speed,
.gmac_phy_intf_sel_mask = GENMASK_U16(6, 4),
.supports_rmii = true,
+ .supports_sgmii = true,
.regs_valid = true,
.regs = {
@@ -1208,6 +1228,43 @@ static void rk_phy_powerdown(struct rk_priv_data *bsp_priv)
dev_err(bsp_priv->dev, "fail to disable phy-supply\n");
}
+static int rk_pcs_init(struct stmmac_priv *priv)
+{
+ struct device_node *np = priv->device->of_node;
+ struct device_node *pcs_node;
+ struct dw_xpcs *xpcs;
+
+ pcs_node = of_parse_phandle(np, "pcs-handle", 0);
+ if (!pcs_node)
+ return -ENODEV;
+
+ xpcs = xpcs_rk_create(priv->device, pcs_node);
+ of_node_put(pcs_node);
+ if (IS_ERR(xpcs))
+ return PTR_ERR(xpcs);
+
+ priv->hw->xpcs = xpcs;
+ return 0;
+}
+
+static void rk_pcs_exit(struct stmmac_priv *priv)
+{
+ if (!priv->hw->xpcs)
+ return;
+
+ xpcs_destroy(priv->hw->xpcs);
+ priv->hw->xpcs = NULL;
+}
+
+static struct phylink_pcs *rk_select_pcs(struct stmmac_priv *priv,
+ phy_interface_t interface)
+{
+ if (!priv->hw->xpcs)
+ return NULL;
+
+ return xpcs_to_phylink_pcs(priv->hw->xpcs);
+}
+
static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
const struct rk_gmac_ops *ops)
@@ -1330,6 +1387,7 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
bsp_priv->supports_rgmii = ops->supports_rgmii || !!ops->set_to_rgmii;
bsp_priv->supports_rmii = ops->supports_rmii || !!ops->set_to_rmii;
+ bsp_priv->supports_sgmii = ops->supports_sgmii || !!ops->set_to_sgmii;
if (ops->init) {
ret = ops->init(bsp_priv);
@@ -1361,6 +1419,10 @@ static int rk_gmac_check_ops(struct rk_priv_data *bsp_priv)
if (!bsp_priv->supports_rmii)
return -EINVAL;
break;
+ case PHY_INTERFACE_MODE_SGMII:
+ if (!bsp_priv->supports_sgmii)
+ return -EINVAL;
+ break;
default:
dev_err(bsp_priv->dev,
"unsupported interface %d", bsp_priv->phy_iface);
@@ -1379,6 +1441,9 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
if (ret)
return ret;
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_SGMII)
+ goto set_mode;
+
ret = rk_get_phy_intf_sel(bsp_priv->phy_iface);
if (ret < 0)
return ret;
@@ -1416,7 +1481,8 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
}
}
- /*rmii or rgmii*/
+set_mode:
+ /* rmii, rgmii, sgmii */
switch (bsp_priv->phy_iface) {
case PHY_INTERFACE_MODE_RGMII:
dev_info(dev, "init for RGMII\n");
@@ -1447,6 +1513,11 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
if (bsp_priv->ops->set_to_rmii)
bsp_priv->ops->set_to_rmii(bsp_priv);
break;
+ case PHY_INTERFACE_MODE_SGMII:
+ dev_info(dev, "init for SGMII\n");
+ if (bsp_priv->ops->set_to_sgmii)
+ bsp_priv->ops->set_to_sgmii(bsp_priv);
+ break;
default:
dev_err(dev, "NO interface defined!\n");
}
@@ -1486,6 +1557,9 @@ static void rk_get_interfaces(struct stmmac_priv *priv, void *bsp_priv,
if (rk->supports_rmii)
__set_bit(PHY_INTERFACE_MODE_RMII, interfaces);
+
+ if (rk->supports_sgmii)
+ __set_bit(PHY_INTERFACE_MODE_SGMII, interfaces);
}
static int rk_set_clk_tx_rate(void *bsp_priv_, struct clk *clk_tx_i,
@@ -1602,6 +1676,17 @@ static int rk_gmac_probe(struct platform_device *pdev)
plat_dat->suspend = rk_gmac_suspend;
plat_dat->resume = rk_gmac_resume;
+ if (plat_dat->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+ /* SGMII clock always runs at 125 MHz */
+ plat_dat->set_clk_tx_rate = NULL;
+
+ /* SGMII requires a PCS */
+ plat_dat->default_an_inband = true;
+ plat_dat->pcs_init = rk_pcs_init;
+ plat_dat->pcs_exit = rk_pcs_exit;
+ plat_dat->select_pcs = rk_select_pcs;
+ }
+
plat_dat->bsp_priv = rk_gmac_setup(pdev, plat_dat, data);
if (IS_ERR(plat_dat->bsp_priv))
return PTR_ERR(plat_dat->bsp_priv);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c
index 13634965bc19a1..01df4776edb3fd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c
@@ -35,8 +35,11 @@ static int rzn1_dwmac_pcs_init(struct stmmac_priv *priv)
static void rzn1_dwmac_pcs_exit(struct stmmac_priv *priv)
{
- if (priv->hw->phylink_pcs)
- miic_destroy(priv->hw->phylink_pcs);
+ if (!priv->hw->phylink_pcs)
+ return;
+
+ miic_destroy(priv->hw->phylink_pcs);
+ priv->hw->phylink_pcs = NULL;
}
static struct phylink_pcs *rzn1_dwmac_select_pcs(struct stmmac_priv *priv,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 1d7f0a57d28897..6d4bc1fe8f7511 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -539,8 +539,11 @@ static int socfpga_dwmac_pcs_init(struct stmmac_priv *priv)
static void socfpga_dwmac_pcs_exit(struct stmmac_priv *priv)
{
- if (priv->hw->phylink_pcs)
- lynx_pcs_destroy(priv->hw->phylink_pcs);
+ if (!priv->hw->phylink_pcs)
+ return;
+
+ lynx_pcs_destroy(priv->hw->phylink_pcs);
+ priv->hw->phylink_pcs = NULL;
}
static struct phylink_pcs *socfpga_dwmac_select_pcs(struct stmmac_priv *priv,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index afe98ff5bdcb05..d2f77f0c223a74 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -426,36 +426,15 @@ int stmmac_mdio_reset(struct mii_bus *bus)
int stmmac_pcs_setup(struct net_device *ndev)
{
struct stmmac_priv *priv = netdev_priv(ndev);
- struct fwnode_handle *devnode, *pcsnode;
- struct dw_xpcs *xpcs = NULL;
- int addr, ret;
-
- devnode = dev_fwnode(priv->device);
-
- if (priv->plat->pcs_init) {
- ret = priv->plat->pcs_init(priv);
- } else if (fwnode_property_present(devnode, "pcs-handle")) {
- pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0);
- xpcs = xpcs_create_fwnode(pcsnode);
- fwnode_handle_put(pcsnode);
- ret = PTR_ERR_OR_ZERO(xpcs);
- } else if (priv->plat->mdio_bus_data &&
- priv->plat->mdio_bus_data->pcs_mask) {
- addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1;
- xpcs = xpcs_create_mdiodev(priv->mii, addr);
- ret = PTR_ERR_OR_ZERO(xpcs);
- } else {
+ int ret;
+
+ if (!priv->plat->pcs_init)
return 0;
- }
+ ret = priv->plat->pcs_init(priv);
if (ret)
return dev_err_probe(priv->device, ret, "No xPCS found\n");
- if (xpcs)
- xpcs_config_eee_mult_fact(xpcs, priv->plat->mult_fact_100ns);
-
- priv->hw->xpcs = xpcs;
-
return 0;
}
@@ -463,14 +442,10 @@ void stmmac_pcs_clean(struct net_device *ndev)
{
struct stmmac_priv *priv = netdev_priv(ndev);
- if (priv->plat->pcs_exit)
- priv->plat->pcs_exit(priv);
-
- if (!priv->hw->xpcs)
+ if (!priv->plat->pcs_exit)
return;
- xpcs_destroy(priv->hw->xpcs);
- priv->hw->xpcs = NULL;
+ priv->plat->pcs_exit(priv);
}
struct stmmac_clk_rate {
diff --git a/drivers/net/pcs/Kconfig b/drivers/net/pcs/Kconfig
index e417fd66f660ad..3286bc93e70264 100644
--- a/drivers/net/pcs/Kconfig
+++ b/drivers/net/pcs/Kconfig
@@ -12,6 +12,28 @@ config PCS_XPCS
This module provides a driver and helper functions for Synopsys
DesignWare XPCS controllers.
+if PCS_XPCS
+
+config PCS_XPCS_PLATFORM
+ tristate "Generic XPCS controller support"
+ default PCS_XPCS
+ help
+ Generic DWXPCS driver for platforms that don't require any
+ platform specific code to function or is using platform
+ data for setup.
+
+ If you have a controller with this interface, say Y or M here.
+
+config PCS_XPCS_ROCKCHIP
+ tristate "Rockchip XPCS controller support"
+ depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
+ help
+ Support for XPCS controller on Rockchip RK356x SoC.
+
+ If you have a Rockchip SoC with this interface, say Y or M here.
+
+endif # PCS_XPCS
+
config PCS_LYNX
tristate
help
diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile
index 4f7920618b9005..c809b7f942a518 100644
--- a/drivers/net/pcs/Makefile
+++ b/drivers/net/pcs/Makefile
@@ -1,10 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for Linux PCS drivers
-pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-plat.o \
- pcs-xpcs-nxp.o pcs-xpcs-wx.o
-
-obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
+obj-$(CONFIG_PCS_XPCS) += pcs-xpcs.o pcs-xpcs-nxp.o pcs-xpcs-wx.o
+obj-$(CONFIG_PCS_XPCS_PLATFORM) += pcs-xpcs-plat.o
+obj-$(CONFIG_PCS_XPCS_ROCKCHIP) += pcs-xpcs-rk.o
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o
obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
diff --git a/drivers/net/pcs/pcs-xpcs-rk.c b/drivers/net/pcs/pcs-xpcs-rk.c
new file mode 100644
index 00000000000000..ed969a38d544d5
--- /dev/null
+++ b/drivers/net/pcs/pcs-xpcs-rk.c
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip XPCS platform device driver
+ *
+ * Based on the Synopsys DesignWare XPCS platform driver.
+ * Copyright (C) 2024 Serge Semin
+ *
+ * Adapted for Rockchip SoCs, with reference to the Rockchip OEM driver.
+ * Copyright (C) 2026 Coia Prant
+ */
+
+#include <linux/atomic.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pcs/pcs-xpcs-rk.h>
+#include <linux/phy.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/sizes.h>
+
+#include "pcs-xpcs.h"
+
+struct dw_xpcs_rk {
+ struct platform_device *pdev;
+ struct mii_bus *bus;
+ void __iomem *reg_base;
+ struct phy *serdes_phy;
+ struct clk *csr_clk;
+ struct clk *eee_clk;
+};
+
+static ptrdiff_t xpcs_rk_addr_format(int dev, int reg)
+{
+ return FIELD_PREP(0x70000, dev) | FIELD_PREP(0xffff, reg);
+}
+
+static int xpcs_rk_read_reg(struct dw_xpcs_rk *pxpcs, int dev, int reg)
+{
+ ptrdiff_t csr;
+ int ret;
+
+ csr = xpcs_rk_addr_format(dev, reg);
+
+ ret = pm_runtime_resume_and_get(&pxpcs->pdev->dev);
+ if (ret)
+ return ret;
+
+ ret = readl(pxpcs->reg_base + (csr << 2)) & 0xffff;
+
+ pm_runtime_put(&pxpcs->pdev->dev);
+ return ret;
+}
+
+static int xpcs_rk_write_reg(struct dw_xpcs_rk *pxpcs, int dev, int reg, u16 val)
+{
+ ptrdiff_t csr;
+ int ret;
+
+ csr = xpcs_rk_addr_format(dev, reg);
+
+ ret = pm_runtime_resume_and_get(&pxpcs->pdev->dev);
+ if (ret)
+ return ret;
+
+ writel(val, pxpcs->reg_base + (csr << 2));
+
+ pm_runtime_put(&pxpcs->pdev->dev);
+ return 0;
+}
+
+#define ROCKCHIP_MMD_MII1 2
+#define ROCKCHIP_MMD_MII2 3
+#define ROCKCHIP_MMD_MII3 4
+#define ROCKCHIP_MMD_PMAPMD 6
+#define ROCKCHIP_MMD_MII 7
+
+static bool xpcs_rk_mdio_addr_validate(int addr)
+{
+ return !(addr < 0 || addr > 3);
+}
+
+static int xpcs_rk_mdio_read_remapping(int addr, int dev, int reg)
+{
+ switch (dev) {
+ case MDIO_MMD_PMAPMD:
+ return ROCKCHIP_MMD_PMAPMD;
+ case MDIO_MMD_VEND2:
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ switch (addr) {
+ case 0:
+ return ROCKCHIP_MMD_MII;
+ case 1:
+ return ROCKCHIP_MMD_MII1;
+ case 2:
+ return ROCKCHIP_MMD_MII2;
+ case 3:
+ return ROCKCHIP_MMD_MII3;
+ default:
+ return -ENODEV;
+ }
+}
+
+static int xpcs_rk_mdio_write_remapping(int addr, int dev, int reg)
+{
+ switch (dev) {
+ case MDIO_MMD_PMAPMD:
+ return ROCKCHIP_MMD_PMAPMD;
+ case MDIO_MMD_VEND2:
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ /* Writable only on MII */
+ switch (reg) {
+ case DW_VR_MII_AN_CTRL:
+ case DW_VR_MII_AN_INTR_STS:
+ case DW_VR_MII_EEE_MCTRL0:
+ case DW_VR_MII_EEE_MCTRL1:
+ case DW_VR_MII_DIG_CTRL2:
+ return ROCKCHIP_MMD_MII;
+ default:
+ break;
+ }
+
+ switch (addr) {
+ case 0:
+ return ROCKCHIP_MMD_MII;
+ case 1:
+ return ROCKCHIP_MMD_MII1;
+ case 2:
+ return ROCKCHIP_MMD_MII2;
+ case 3:
+ return ROCKCHIP_MMD_MII3;
+ default:
+ return -ENODEV;
+ }
+}
+
+static int xpcs_rk_read_c22(struct mii_bus *bus, int addr, int reg)
+{
+ struct dw_xpcs_rk *pxpcs = bus->priv;
+ int dev;
+
+ if (!xpcs_rk_mdio_addr_validate(addr))
+ return -ENODEV;
+
+ dev = xpcs_rk_mdio_read_remapping(addr, MDIO_MMD_VEND2, reg);
+ if (dev < 0)
+ return 0xffff;
+
+ return xpcs_rk_read_reg(pxpcs, dev, reg);
+}
+
+static int xpcs_rk_write_c22(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+ struct dw_xpcs_rk *pxpcs = bus->priv;
+ int dev;
+
+ if (!xpcs_rk_mdio_addr_validate(addr))
+ return -ENODEV;
+
+ dev = xpcs_rk_mdio_write_remapping(addr, MDIO_MMD_VEND2, reg);
+ if (dev < 0)
+ return 0;
+
+ return xpcs_rk_write_reg(pxpcs, dev, reg, val);
+}
+
+static int xpcs_rk_read_c45(struct mii_bus *bus, int addr, int dev, int reg)
+{
+ struct dw_xpcs_rk *pxpcs = bus->priv;
+
+ if (!xpcs_rk_mdio_addr_validate(addr))
+ return -ENODEV;
+
+ dev = xpcs_rk_mdio_read_remapping(addr, dev, reg);
+ if (dev < 0)
+ return 0xffff;
+
+ return xpcs_rk_read_reg(pxpcs, dev, reg);
+}
+
+static int xpcs_rk_write_c45(struct mii_bus *bus, int addr, int dev, int reg, u16 val)
+{
+ struct dw_xpcs_rk *pxpcs = bus->priv;
+
+ if (!xpcs_rk_mdio_addr_validate(addr))
+ return -ENODEV;
+
+ dev = xpcs_rk_mdio_write_remapping(addr, dev, reg);
+ if (dev < 0)
+ return 0;
+
+ return xpcs_rk_write_reg(pxpcs, dev, reg, val);
+}
+
+static struct dw_xpcs_rk *xpcs_rk_create_data(struct platform_device *pdev)
+{
+ struct dw_xpcs_rk *pxpcs;
+
+ pxpcs = devm_kzalloc(&pdev->dev, sizeof(*pxpcs), GFP_KERNEL);
+ if (!pxpcs)
+ return ERR_PTR(-ENOMEM);
+
+ pxpcs->pdev = pdev;
+
+ dev_set_drvdata(&pdev->dev, pxpcs);
+
+ return pxpcs;
+}
+
+static int xpcs_rk_serdes_phy_init(struct dw_xpcs_rk *pxpcs)
+{
+ struct device *dev = &pxpcs->pdev->dev;
+
+ pxpcs->serdes_phy = devm_phy_get(dev, "serdes");
+ if (IS_ERR(pxpcs->serdes_phy))
+ return dev_err_probe(dev, PTR_ERR(pxpcs->serdes_phy),
+ "Failed to get SerDes PHY\n");
+
+ return 0;
+}
+
+static void xpcs_rk_serdes_phy_poweroff(void *data)
+{
+ struct dw_xpcs_rk *pxpcs = data;
+ struct device *dev = &pxpcs->pdev->dev;
+
+ phy_power_off(pxpcs->serdes_phy);
+ phy_exit(pxpcs->serdes_phy);
+
+ dev_pm_genpd_rpm_always_on(dev, false);
+}
+
+static int xpcs_rk_serdes_phy_poweron(struct dw_xpcs_rk *pxpcs)
+{
+ struct device *dev = &pxpcs->pdev->dev;
+ int ret;
+
+ ret = dev_pm_genpd_rpm_always_on(dev, true);
+ if (ret) {
+ dev_err(dev, "Failed to power on power-domains\n");
+ return ret;
+ }
+
+ ret = phy_init(pxpcs->serdes_phy);
+ if (ret) {
+ dev_err(dev, "Failed to init SerDes PHY\n");
+ goto pm_domain;
+ }
+
+ ret = phy_power_on(pxpcs->serdes_phy);
+ if (ret) {
+ dev_err(dev, "Failed to power on SerDes PHY\n");
+ goto serdes_phy;
+ }
+
+ ret = devm_add_action_or_reset(dev, xpcs_rk_serdes_phy_poweroff, pxpcs);
+ if (ret) {
+ dev_err(dev, "Failed to register devm for SerDes PHY: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+
+serdes_phy:
+ phy_exit(pxpcs->serdes_phy);
+pm_domain:
+ dev_pm_genpd_rpm_always_on(dev, false);
+ return ret;
+}
+
+static int xpcs_rk_init_res(struct dw_xpcs_rk *pxpcs)
+{
+ struct platform_device *pdev = pxpcs->pdev;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "No reg-space found\n");
+ return -EINVAL;
+ }
+
+ if (resource_size(res) < SZ_2M) {
+ dev_err(dev, "Invalid reg-space size\n");
+ return -EINVAL;
+ }
+
+ pxpcs->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pxpcs->reg_base)) {
+ dev_err(dev, "Failed to map reg-space\n");
+ return PTR_ERR(pxpcs->reg_base);
+ }
+
+ return 0;
+}
+
+static void xpcs_rk_exit_clk(void *data)
+{
+ struct dw_xpcs_rk *pxpcs = data;
+
+ clk_disable_unprepare(pxpcs->eee_clk);
+}
+
+static int xpcs_rk_init_clk(struct dw_xpcs_rk *pxpcs)
+{
+ struct device *dev = &pxpcs->pdev->dev;
+ int ret;
+
+ pxpcs->csr_clk = devm_clk_get(dev, "csr");
+ if (IS_ERR(pxpcs->csr_clk))
+ return dev_err_probe(dev, PTR_ERR(pxpcs->csr_clk),
+ "Failed to get CSR clock\n");
+
+ pm_runtime_set_suspended(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret) {
+ dev_err(dev, "Failed to enable runtime-PM\n");
+ return ret;
+ }
+
+ pxpcs->eee_clk = devm_clk_get(dev, "eee");
+ if (IS_ERR(pxpcs->eee_clk))
+ return dev_err_probe(dev, PTR_ERR(pxpcs->eee_clk),
+ "Failed to get EEE clock\n");
+
+ ret = clk_prepare_enable(pxpcs->eee_clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable EEE clock\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, xpcs_rk_exit_clk, pxpcs);
+ if (ret) {
+ dev_err(dev, "Failed to register devm for EEE clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int xpcs_rk_init_bus(struct dw_xpcs_rk *pxpcs)
+{
+ struct device *dev = &pxpcs->pdev->dev;
+ static atomic_t id = ATOMIC_INIT(-1);
+ struct mii_bus *bus;
+ int ret;
+
+ bus = devm_mdiobus_alloc_size(dev, 0);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->name = "Rockchip DW XPCS MCI/APB3";
+ bus->read = xpcs_rk_read_c22;
+ bus->write = xpcs_rk_write_c22;
+ bus->read_c45 = xpcs_rk_read_c45;
+ bus->write_c45 = xpcs_rk_write_c45;
+ bus->phy_mask = ~0;
+ bus->parent = dev;
+ bus->priv = pxpcs;
+
+ snprintf(bus->id, MII_BUS_ID_SIZE,
+ "rockchip_dwxpcs-%x", atomic_inc_return(&id));
+
+ /*
+ * MDIO-bus here serves as just a back-end engine abstracting out
+ * the MDIO and MCI/APB3 IO interfaces utilized for the Rockchip DWXPCS CSRs
+ * access.
+ */
+ ret = devm_mdiobus_register(dev, bus);
+ if (ret) {
+ dev_err(dev, "Failed to create MDIO bus\n");
+ return ret;
+ }
+
+ pxpcs->bus = bus;
+ return 0;
+}
+
+static int xpcs_rk_probe(struct platform_device *pdev)
+{
+ struct dw_xpcs_rk *pxpcs;
+ int ret;
+
+ pxpcs = xpcs_rk_create_data(pdev);
+ if (IS_ERR(pxpcs))
+ return PTR_ERR(pxpcs);
+
+ /*
+ * The XPCS may be attached to a power domain (e.g. PD_PIPE). The domain
+ * must be powered on before any register access, otherwise the SoC will
+ * trigger a synchronous external abort (SError).
+ *
+ * Accessing the XPCS registers also requires a TX clock from the SerDes,
+ * which is needed for the soft reset.
+ */
+ ret = xpcs_rk_serdes_phy_init(pxpcs);
+ if (ret)
+ return ret;
+
+ ret = xpcs_rk_serdes_phy_poweron(pxpcs);
+ if (ret)
+ return ret;
+
+ ret = xpcs_rk_init_res(pxpcs);
+ if (ret)
+ return ret;
+
+ ret = xpcs_rk_init_clk(pxpcs);
+ if (ret)
+ return ret;
+
+ ret = xpcs_rk_init_bus(pxpcs);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct dw_xpcs *xpcs_rk_create(struct device *dev, struct device_node *np)
+{
+ struct platform_device *pdev;
+ struct device_node *pcs_np;
+ struct dw_xpcs_rk *pxpcs;
+ struct dw_xpcs *xpcs;
+ u32 port;
+
+ if (!of_device_is_available(np))
+ return ERR_PTR(-ENODEV);
+
+ if (of_property_read_u32(np, "reg", &port))
+ return ERR_PTR(-EINVAL);
+
+ if (!xpcs_rk_mdio_addr_validate((int)port))
+ return ERR_PTR(-EINVAL);
+
+ /* The XPCS pdev is attached to the parent node */
+ pcs_np = of_get_parent(np);
+ if (!pcs_np)
+ return ERR_PTR(-ENODEV);
+
+ if (!of_device_is_available(pcs_np)) {
+ of_node_put(pcs_np);
+ return ERR_PTR(-ENODEV);
+ }
+
+ pdev = of_find_device_by_node(pcs_np);
+ of_node_put(pcs_np);
+ if (!pdev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ pxpcs = platform_get_drvdata(pdev);
+ if (!pxpcs || !pxpcs->bus) {
+ put_device(&pdev->dev);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ xpcs = xpcs_create_mdiodev(pxpcs->bus, (int)port);
+ if (IS_ERR(xpcs)) {
+ put_device(&pdev->dev);
+ return xpcs;
+ }
+
+ device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
+ put_device(&pdev->dev);
+ return xpcs;
+}
+EXPORT_SYMBOL_GPL(xpcs_rk_create);
+
+static int __maybe_unused xpcs_rk_pm_runtime_suspend(struct device *dev)
+{
+ struct dw_xpcs_rk *pxpcs = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(pxpcs->csr_clk);
+
+ return 0;
+}
+
+static int __maybe_unused xpcs_rk_pm_runtime_resume(struct device *dev)
+{
+ struct dw_xpcs_rk *pxpcs = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(pxpcs->csr_clk);
+}
+
+static const struct dev_pm_ops xpcs_rk_pm_ops = {
+ SET_RUNTIME_PM_OPS(xpcs_rk_pm_runtime_suspend,
+ xpcs_rk_pm_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id xpcs_rk_of_ids[] = {
+ { .compatible = "rockchip,rk3568-xpcs" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, xpcs_rk_of_ids);
+
+static struct platform_driver xpcs_rk_driver = {
+ .probe = xpcs_rk_probe,
+ .driver = {
+ .name = "rk_xpcs-dwxpcs",
+ .pm = &xpcs_rk_pm_ops,
+ .of_match_table = xpcs_rk_of_ids,
+ },
+};
+module_platform_driver(xpcs_rk_driver);
+
+MODULE_DESCRIPTION("Rockchip XPCS platform device driver");
+MODULE_AUTHOR("Coia Prant <coiaprant@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index e69fa2f0a0e8d8..cf370ba247cacf 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -816,6 +816,11 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
if (ret < 0)
return ret;
+ /* Clear CL37 AN complete status */
+ ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);
+ if (ret < 0)
+ return ret;
+
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,
mdio_ctrl | BMCR_ANENABLE);
@@ -884,7 +889,7 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,
- mdio_ctrl | BMCR_ANENABLE);
+ mdio_ctrl | BMCR_ANENABLE | BMCR_ANRESTART);
if (ret < 0)
return ret;
}
@@ -1058,6 +1063,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
/* Reset link_state */
state->link = false;
+ state->an_complete = false;
state->speed = SPEED_UNKNOWN;
state->duplex = DUPLEX_UNKNOWN;
state->pause = 0;
@@ -1069,6 +1075,8 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
if (ret < 0)
return ret;
+ state->an_complete = ret & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR;
+
if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) {
int speed_value;
@@ -1086,7 +1094,24 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
state->duplex = DUPLEX_FULL;
else
state->duplex = DUPLEX_HALF;
- } else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {
+
+ return 0;
+ }
+
+ /* Clear AN complete status or interrupt */
+ if (state->an_complete)
+ xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);
+
+ if (xpcs->info.pma != WX_TXGBE_XPCS_PMA_10G_ID) {
+ /* If the link down, restart Auto-Negotiation */
+ if (state->an_complete)
+ xpcs_modify(xpcs, MDIO_MMD_VEND2, MII_BMCR, BMCR_ANRESTART,
+ BMCR_ANRESTART);
+
+ return 0;
+ }
+
+ if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {
int speed, duplex;
state->link = true;
@@ -1112,7 +1137,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
else if (duplex & ADVERTISE_1000XHALF)
state->duplex = DUPLEX_HALF;
- xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);
+ return 0;
}
return 0;
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
index 2b0f152f547091..ff290bc18589ad 100644
--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -186,6 +186,7 @@ struct rockchip_combphy_grfcfg {
struct combphy_reg pipe_xpcs_phy_ready;
struct combphy_reg pipe_pcie1l0_sel;
struct combphy_reg pipe_pcie1l1_sel;
+ struct combphy_reg pipe_sgmii_mac_sel;
struct combphy_reg u3otg0_port_en;
struct combphy_reg u3otg1_port_en;
};
@@ -212,6 +213,7 @@ struct rockchip_combphy_priv {
bool enable_ssc;
bool ext_refclk;
struct clk *refclk;
+ u32 sgmii_mac_sel;
};
static void rockchip_combphy_updatel(struct rockchip_combphy_priv *priv,
@@ -375,6 +377,9 @@ static int rockchip_combphy_parse_dt(struct device *dev, struct rockchip_combphy
priv->ext_refclk = device_property_present(dev, "rockchip,ext-refclk");
+ priv->sgmii_mac_sel = 0;
+ device_property_read_u32(dev, "rockchip,sgmii-mac-sel", &priv->sgmii_mac_sel);
+
priv->phy_rst = devm_reset_control_get_exclusive(dev, "phy");
/* fallback to old behaviour */
if (PTR_ERR(priv->phy_rst) == -ENOENT)
@@ -873,6 +878,8 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)
break;
case PHY_TYPE_SGMII:
+ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_sgmii_mac_sel,
+ priv->sgmii_mac_sel > 0);
rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true);
rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true);
rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true);
@@ -984,6 +991,7 @@ static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = {
.con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 },
/* pipe-grf */
.pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 },
+ .pipe_sgmii_mac_sel = { 0x0040, 1, 1, 0x00, 0x01 },
.pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 },
.u3otg0_port_en = { 0x0104, 15, 0, 0x0181, 0x1100 },
.u3otg1_port_en = { 0x0144, 15, 0, 0x0181, 0x1100 },
diff --git a/include/linux/pcs/pcs-xpcs-rk.h b/include/linux/pcs/pcs-xpcs-rk.h
new file mode 100644
index 00000000000000..28723d5bd75cce
--- /dev/null
+++ b/include/linux/pcs/pcs-xpcs-rk.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_PCS_XPCS_ROCKCHIP_H
+#define __LINUX_PCS_XPCS_ROCKCHIP_H
+
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/pcs/pcs-xpcs.h>
+
+struct dw_xpcs *xpcs_rk_create(struct device *dev, struct device_node *np);
+
+#endif /* __LINUX_PCS_XPCS_ROCKCHIP_H */
|
| 3/2 | 2026/07/15 05:42 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20099 output: 28 thoughts: 4606 Thoughts: **Patch Series Analysis for Fuzzing Potential** Okay, so we have this patch series introducing support for the Rockchip RK3568 Synopsys DesignWare Ethernet PCS (XPCS). My initial thought is to assess its fuzzing potential. The changes are spread across several files, so let's break this down. Firstly, the Rockchip-specific driver, `pcs-xpcs-rk.c`, is a platform driver. These are a pain in the neck from a fuzzing perspective because they usually rely heavily on the Device Tree. Unless we're dealing with a syzkaller-specific pseudo-device, or the device tree is already available, which is unlikely in a generic environment, it's probably going to be difficult to reach this driver. Syzkaller *could* try to inject the platform device with `syz_dev_init` or modify the DT if supported, but typically, syzkaller prefers simpler, more direct approaches for network drivers like virtual interfaces. In this case, `pcs-xpcs-rk.c` registers a platform driver with a compatible string, and its `probe` function does some initialization that includes getting a "serdes" phy and resources. Without the right dependencies in place, the `probe` function will likely fail. The device tree will need to exist and have the correct entries for the platform driver to succeed, which is unlikely in most standard syzkaller configurations. Secondly, and more interestingly, there are changes to the `stmmac` driver. This is a generic driver, so it could have good potential for fuzzing. There is a refactoring happening where `stmmac_pcs_setup` is being modified, and this affects how PCS is initialized in the `stmmac` driver. The patch delegates the initialization to `priv->plat->pcs_init`. The `stmmac` changes lead to the `dwmac-intel.c` file getting modified. This file is for Intel PCI devices. This is where things get interesting because syzkaller *does* have PCI fuzzing capabilities! If the Intel driver is enabled, and syzkaller can inject a suitable Intel PCI device, we're in business. We'll be able to probe `intel_mgbe_pcs_init` and from there the `stmmac_pcs_setup` functions. This would mean reaching `xpcs_create_mdiodev`. The PCI device emulation/injection is important here. There's some functional logic as well. The patch also modifies `pcs-xpcs.c`, specifically functions like `xpcs_config_aneg_c37_sgmii`, `xpcs_config_aneg_c37_1000basex`, and `xpcs_get_state_c37_sgmii`. These are generic PCS driver functions and, since Intel mGBE uses SGMII or 1000BASEX, these functions *are* reachable through the Intel mGBE driver, especially if the device is configured correctly and SGMII or 1000BASEX modes are active. These modifications in `pcs-xpcs.c` should be the main area of focus. `xpcs_get_state_c37_sgmii` is called periodically by phylink, so they will be exercised during device operation. So, the key here is PCI fuzzing, specifically targeting the Intel mGBE driver (`dwmac-intel.c`). If syzkaller can inject or emulate an Intel PCI device, and the configurations are right, we can exercise those generic `pcs-xpcs.c` functions. I'll need to check the Kconfig options to be sure the right configs are set, but it looks like we will need: * `CONFIG_STMMAC_ETH` * `CONFIG_DWMAC_INTEL` * `CONFIG_PCS_XPCS` * `CONFIG_PCI` It looks like the `DWMAC_INTEL` config selects `PCS_XPCS`. Let's get to work with testing these functions. |
| 4/2 | 2026/07/15 05:43 | tool | grepper |
0mArgs:{
"Expression": "xpcs_config_aneg_c37_sgmii"
}Results: {
"Output": "drivers/net/pcs/pcs-xpcs.c=721=static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface)\n--\ndrivers/net/pcs/pcs-xpcs.c-749-\ndrivers/net/pcs/pcs-xpcs.c:750:static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,\ndrivers/net/pcs/pcs-xpcs.c-751-\t\t\t\t unsigned int neg_mode)\n--\ndrivers/net/pcs/pcs-xpcs.c=916=static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,\n--\ndrivers/net/pcs/pcs-xpcs.c-947-\tcase DW_AN_C37_SGMII:\ndrivers/net/pcs/pcs-xpcs.c:948:\t\tret = xpcs_config_aneg_c37_sgmii(xpcs, neg_mode);\ndrivers/net/pcs/pcs-xpcs.c-949-\t\tif (ret)\n"
} |
| 5/2 | 2026/07/15 05:43 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25891 output: 27 thoughts: 35 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Diving into the Configuration Process** Alright, so the breadcrumb trail starts with `xpcs_do_config`. That's the entry point, the function that kicks off the whole configuration process. Now, I need to understand what's happening *within* that call. My next stop is clearly `xpcs_get_state_c37_sgmii`. This looks like where we're digging for configuration state related to SGMII – a particular type of signaling interface, most likely, based on the naming. I need to understand how we're pulling that state, from where, and how it informs the rest of the configuration. Is it pulling from registers, memory, a config file? Is it checking for errors? I'll probably be checking the return values and any state variables used as a result of that call to understand the flow and any potential issues there. Let's see what's really happening. |
| 6/2 | 2026/07/15 05:43 | tool | grepper |
0mArgs:{
"Expression": "xpcs_get_state_c37_sgmii"
}Results: {
"Output": "drivers/net/pcs/pcs-xpcs.c=986=static int xpcs_get_state_c73(struct dw_xpcs *xpcs,\n--\ndrivers/net/pcs/pcs-xpcs.c-1058-\ndrivers/net/pcs/pcs-xpcs.c:1059:static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,\ndrivers/net/pcs/pcs-xpcs.c-1060-\t\t\t\t struct phylink_link_state *state)\n--\ndrivers/net/pcs/pcs-xpcs.c=1204=static void xpcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,\n--\ndrivers/net/pcs/pcs-xpcs.c-1225-\tcase DW_AN_C37_SGMII:\ndrivers/net/pcs/pcs-xpcs.c:1226:\t\tret = xpcs_get_state_c37_sgmii(xpcs, state);\ndrivers/net/pcs/pcs-xpcs.c-1227-\t\tif (ret)\ndrivers/net/pcs/pcs-xpcs.c-1228-\t\t\tdev_err(\u0026xpcs-\u003emdiodev-\u003edev, \"%s returned %pe\\n\",\ndrivers/net/pcs/pcs-xpcs.c:1229:\t\t\t\t\"xpcs_get_state_c37_sgmii\", ERR_PTR(ret));\ndrivers/net/pcs/pcs-xpcs.c-1230-\t\tbreak;\n"
} |
| 7/2 | 2026/07/15 05:43 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 26566 output: 22 thoughts: 29 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating the PCS State Retrieval** Right, so this is coming from a call to `xpcs_get_state`. Interesting. I need to understand what's triggering this state check. *That* specific function call is the root of the issue. Now, the next logical question is, where does this get called from and what is happening around it? I need to think about what is initializing the PCS to begin with. I should check the `intel_mgbe_pcs_init` function. Is it responsible for setting up the initial PCS state or, is it called before `xpcs_get_state`? Is there some form of race condition? Understanding the relationship between *these* two functions is the key to understanding the state. |
| 8/2 | 2026/07/15 05:43 | tool | grepper |
0mArgs:{
"Expression": "intel_mgbe_pcs_init"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c=565=static void common_default_data(struct plat_stmmacenet_data *plat)\n--\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c-574-\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c:575:static int intel_mgbe_pcs_init(struct stmmac_priv *priv)\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c-576-{\n--\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c=619=static int intel_mgbe_common_data(struct pci_dev *pdev,\n--\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c-738-\t\tplat-\u003edefault_an_inband = true;\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c:739:\t\tplat-\u003epcs_init = intel_mgbe_pcs_init;\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c-740-\t\tplat-\u003epcs_exit = intel_mgbe_pcs_exit;\n"
} |
| 9/2 | 2026/07/15 05:43 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27125 output: 21 thoughts: 40 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating the PCS Initialization** Alright, so the clue leads me to `plat->pcs_init`, and I can see it's called from `stmmac_pcs_setup`. That confirms the assignment and the call stack; so far, so good. Now, let's dive into `stmmac_pcs_setup`. I need to understand the context of that call, see *how* it's being used, and what data is being passed in. Gotta figure out what kind of setup it's performing, what parameters matter, and where the potential bottlenecks or customization points might be. Knowing *how* it's all put together is key to understanding the deeper implications of the initialization. |
| 10/2 | 2026/07/15 05:43 | tool | grepper |
0mArgs:{
"Expression": "stmmac_pcs_setup"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/stmmac.h=401=void stmmac_mdio_unlock(struct stmmac_priv *priv);\ndrivers/net/ethernet/stmicro/stmmac/stmmac.h:402:int stmmac_pcs_setup(struct net_device *ndev);\ndrivers/net/ethernet/stmicro/stmmac/stmmac.h-403-void stmmac_pcs_clean(struct net_device *ndev);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=7797=static int __stmmac_dvr_probe(struct device *device,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-8045-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:8046:\tret = stmmac_pcs_setup(ndev);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-8047-\tif (ret)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c=381=int stmmac_mdio_reset(struct mii_bus *bus)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c-425-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c:426:int stmmac_pcs_setup(struct net_device *ndev)\ndrivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c-427-{\n"
} |
| 11/2 | 2026/07/15 05:43 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27799 output: 21 thoughts: 823 Thoughts: **Fuzzing Targets and Configuration Analysis for Intel mGBE Device** Okay, so this is all triggered from `__stmmac_dvr_probe`. The relevant code changes in `pcs-xpcs.c`, `stmmac_mdio.c`, and `dwmac-intel.c` are definitely reachable through PCI fuzzing of the Intel mGBE device. The Rockchip-specific changes in `pcs-xpcs-rk.c` and `dwmac-rk.c` are less of a priority for a generic fuzzing environment, but it doesn't hurt to keep an eye on them. Let's nail down the key functions to target: `xpcs_config_aneg_c37_sgmii`, `xpcs_config_aneg_c37_1000basex`, `xpcs_get_state_c37_sgmii`, `intel_mgbe_pcs_init`, and `stmmac_pcs_setup`. Right, `stmmac_pcs_setup` and `intel_mgbe_pcs_init` are both called during the probe. But `xpcs_config_aneg_c37_sgmii` and `xpcs_get_state_c37_sgmii` are triggered by phylink, which is then triggered by netlink, like an `ip link set up` command. Those phylink functions are definitely good targets, especially since they're user-space controllable. Now, let's verify the necessary configurations. I need: `DWMAC_INTEL`, `STMMAC_ETH`, `PCS_XPCS`, and `PCI`. Hang on... `PCS_XPCS` is a tristate, and it's selected by `DWMAC_INTEL`. Okay, reading the patch... `PCS_XPCS_PLATFORM` is the generic driver. Ah, got it. `pcs-xpcs.c` is compiled when `CONFIG_PCS_XPCS` is enabled. Okay, looking at the makefile, so `pcs-xpcs.c` is compiled when `CONFIG_PCS_XPCS` is enabled. `CONFIG_DWMAC_INTEL` selects `PCS_XPCS`. So, it looks like `DWMAC_INTEL` and `STMMAC_ETH` are the core requirements. Let me just double-check other potentially relevant configs. Ah, `DWMAC_ROCKCHIP` selects `PCS_XPCS_ROCKCHIP`. We might as well enable `DWMAC_ROCKCHIP` and `PCS_XPCS_ROCKCHIP` too, just in case, but they're dependent on `ARCH_ROCKCHIP || COMPILE_TEST`. Since we want to focus on the generic parts, `DWMAC_INTEL` is the most important. Alright, final verification: We need `STMMAC_ETH`, `DWMAC_INTEL`, and `PCS_XPCS`. I'm reasonably confident `DWMAC_INTEL` is sufficient, but let's test it out. |
| 12/2 | 2026/07/15 05:43 | tool | grepper |
0mArgs:{
"Expression": "config DWMAC_INTEL"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/Kconfig=323=config DWMAC_IMX8\n--\ndrivers/net/ethernet/stmicro/stmmac/Kconfig-334-\ndrivers/net/ethernet/stmicro/stmmac/Kconfig:335:config DWMAC_INTEL_PLAT\ndrivers/net/ethernet/stmicro/stmmac/Kconfig-336-\ttristate \"Intel dwmac support\"\n--\ndrivers/net/ethernet/stmicro/stmmac/Kconfig=374=config STMMAC_LIBPCI\n--\ndrivers/net/ethernet/stmicro/stmmac/Kconfig-378-\ndrivers/net/ethernet/stmicro/stmmac/Kconfig:379:config DWMAC_INTEL\ndrivers/net/ethernet/stmicro/stmmac/Kconfig-380-\ttristate \"Intel GMAC support\"\n"
} |
| 13/2 | 2026/07/15 05:43 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 29558 output: 19 thoughts: 33 Thoughts: Okay, here's my take on those thoughts, framed as an expert's inner monologue: **Quick Configuration Check** Alright, right, so `DWMAC_INTEL`...that's the key, that's what's driving the connection over to `dwmac-intel.c`. Makes sense, aligns with the usual Intel-specific descriptor. Now, let's just make sure, need to also confirm the state of `PCS_XPCS`. Got to cross-reference my understanding on that one. |
| 14/2 | 2026/07/15 05:43 | tool | grepper |
0mArgs:{
"Expression": "config PCS_XPCS"
}Results: {
"Output": "drivers/net/pcs/Kconfig=6=menu \"PCS device drivers\"\ndrivers/net/pcs/Kconfig-7-\ndrivers/net/pcs/Kconfig:8:config PCS_XPCS\ndrivers/net/pcs/Kconfig-9-\ttristate \"Synopsys DesignWare Ethernet XPCS\"\n--\ndrivers/net/pcs/Kconfig=15=if PCS_XPCS\ndrivers/net/pcs/Kconfig-16-\ndrivers/net/pcs/Kconfig:17:config PCS_XPCS_PLATFORM\ndrivers/net/pcs/Kconfig-18-\ttristate \"Generic XPCS controller support\"\n--\ndrivers/net/pcs/Kconfig-26-\ndrivers/net/pcs/Kconfig:27:config PCS_XPCS_ROCKCHIP\ndrivers/net/pcs/Kconfig-28-\ttristate \"Rockchip XPCS controller support\"\n"
} |
| 15/2 | 2026/07/15 05:43 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 29948 output: 22 thoughts: 25 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person: **Navigating Configuration Space** Alright, so I'm looking at a configuration and the label `PCS_XPCS` just clicked. I know that refers to a specific system setup, so let's start with that context. Now, to understand how this is used, the next logical step is to dive into the `DWMAC_ROCKCHIP` configuration. This is where the hardware interacts with the software, and should illuminate the specifics of how this device operates. From there, I'll be able to trace how this configuration is being applied. |
| 16/2 | 2026/07/15 05:43 | tool | grepper |
0mArgs:{
"Expression": "config DWMAC_ROCKCHIP"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/Kconfig=155=config DWMAC_RENESAS_GBETH\n--\ndrivers/net/ethernet/stmicro/stmmac/Kconfig-167-\ndrivers/net/ethernet/stmicro/stmmac/Kconfig:168:config DWMAC_ROCKCHIP\ndrivers/net/ethernet/stmicro/stmmac/Kconfig-169-\ttristate \"Rockchip dwmac support\"\n"
} |
| 17/2 | 2026/07/15 05:43 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 30267 output: 26 thoughts: 1284 Thoughts: **Fuzzing Strategy Assessment** Okay, so I've been taking a look at this patch adding Rockchip RK3568 XPCS support, and I need to formulate a fuzzing strategy. My initial thought is that we can include `DWMAC_ROCKCHIP` and `PCS_XPCS_ROCKCHIP` in the configs, but they might not be reachable. Including them won't hurt, so why not. The patch introduces a Rockchip-specific platform driver, and I need to assess its reachability in a generic environment. While the platform driver itself, and the direct `rk_pcs_init` and related functions, seem hard to trigger without a specific device tree, the patch also modifies the `stmmac` driver for PCS initialization, and importantly, the Intel mGBE PCI driver (`dwmac-intel.c`) uses the new initialization flow now. This is key. The generic `pcs-xpcs` driver's auto-negotiation and state polling logic for SGMII and 1000BASE-X interfaces are modified as well. This means we have a couple of more generic code paths that could be hit. Therefore, the changes within the `stmmac` driver, and changes to the Intel PCI driver, are reachable via PCI fuzzing and netlink interface manipulation. So, this patch *is* worth fuzzing. My primary focus points for fuzzing are going to be on the functions that underwent code changes. I'll include: `xpcs_config_aneg_c37_sgmii`, `xpcs_config_aneg_c37_1000basex`, `xpcs_get_state_c37_sgmii`, `intel_mgbe_pcs_init`, and `stmmac_pcs_setup`. I also added `rk_pcs_init`, `xpcs_rk_probe` and `xpcs_rk_create` to the list. They may be reachable as well. I've confirmed that `stmmac_pcs_setup` is indeed the correct function name. I double-checked that those static functions I'm interested in are static and therefore suitable as focus symbols. They look fine. I then did a diff check on `pcs-xpcs.c` to confirm the functions I'm focusing on are the ones actually changed by this patch. My initial config setup seems sound. I'll use `STMMAC_ETH`, `DWMAC_INTEL`, `PCS_XPCS`, `PCS_XPCS_ROCKCHIP`, and `DWMAC_ROCKCHIP` to test. Oh, and I just noticed the patch also touches `phy-rockchip-naneng-combphy.c`, so I'll include `PHY_ROCKCHIP_NANENG_COMBPHY` as well. |
| 18/2 | 2026/07/15 05:44 | tool | grepper |
0mError:no matches Args: {
"Expression": "config PHY_ROCKCHIP_NANENG_COMBPHY"
}Results: {
"error": "no matches"
} |
| 19/2 | 2026/07/15 05:44 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 32200 output: 26 thoughts: 22 Thoughts: Here's my thought process, summarized as you requested: **Finding the Configuration for `phy-rockchip-naneng-combphy.c`** Okay, so I need to find the configuration information associated with `phy-rockchip-naneng-combphy.c`. Given my expertise, I'm assuming this is related to a Rockchip system-on-a-chip (SoC), probably dealing with a combined PHY (physical layer) like a USB or PCIe PHY. I'll need to locate the relevant source code and probably some device tree bindings, and then the specifics of the driver. First, I'll start by searching the code base. A quick search for that filename should point me to the correct directory structure. I'll then examine the file contents. I'm expecting to see a combination of device-specific register definitions, initialization routines, and interrupt handlers, possibly using a common PHY framework. The configuration should be present either directly within that driver file or, more likely, managed through device tree bindings. If it's device tree based, I'll need to check the corresponding DT (Device Tree) bindings documentation, usually found in a `Documentation/devicetree/bindings/` directory. This is where I'll find details on the properties that the DT node for this PHY expects, and how these settings are applied. I'll be looking for things like clock settings (clock frequencies, clock domains), power management settings (voltage rails, power domains), any required interrupt lines, and specific register addresses or offsets that will be used by this specific driver to initialize this specific PHY. The specifics will vary depending on the particular naneng-combphy. I'll also be examining how the device is connected to the other components. Essentially, I'm trying to piece together the entire device configuration to understand how this PHY is initialized and managed within the broader Rockchip SoC context, so that if there were a malfunction, I would know how to troubleshoot this device! |
| 20/2 | 2026/07/15 05:44 | tool | grepper |
0mArgs:{
"Expression": "phy-rockchip-naneng-combphy.o"
}Results: {
"Output": "drivers/phy/rockchip/Makefile=8=obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)\t+= phy-rockchip-inno-usb2.o\ndrivers/phy/rockchip/Makefile:9:obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY)\t+= phy-rockchip-naneng-combphy.o\ndrivers/phy/rockchip/Makefile-10-obj-$(CONFIG_PHY_ROCKCHIP_PCIE)\t\t+= phy-rockchip-pcie.o\n"
} |
| 21/2 | 2026/07/15 05:44 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 32803 output: 359 thoughts: 297 Thoughts: **My Assessment of the RK3568 XPCS Patch** Okay, so I'm looking at this patch for `PHY_ROCKCHIP_NANENG_COMBO_PHY`, and I need to figure out if it's worth fuzzing. I see it's adding `PHY_ROCKCHIP_NANENG_COMBO_PHY` to the configs, so that's the driver. Seems like it's a Rockchip RK3568 XPCS implementation, which also affects the `pcs-xpcs`, `stmmac`, and `dwmac-intel` drivers. The tricky part is that the platform driver itself, the Rockchip specific bit, might be hard to hit in my usual virtualized setup without very specific device tree configuration. However, the patch also touches some *generic* code. Specifically, the refactoring in the `stmmac` initialization flow is interesting, and the changes to the Intel mGBE PCI driver (`dwmac-intel.c`) to use that new initialization are definitely in scope. I can get to these, regardless of specific hardware. Additionally, the patch alters the auto-negotiation and state polling logic in the generic `pcs-xpcs.c` driver for SGMII and 1000BASE-X interfaces. Those are also reachable. Given these generic code modifications and the reachability via PCI fuzzing and netlink manipulation, I'd say this patch is definitely fuzzable. Time to call `set-results` and get this added to the campaign. |
| 22/2 | 2026/07/15 05:44 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"STMMAC_ETH",
"DWMAC_INTEL",
"PCS_XPCS",
"PCS_XPCS_ROCKCHIP",
"DWMAC_ROCKCHIP",
"PHY_ROCKCHIP_NANENG_COMBO_PHY"
],
"FocusSymbols": [
"xpcs_config_aneg_c37_sgmii",
"xpcs_config_aneg_c37_1000basex",
"xpcs_get_state_c37_sgmii",
"intel_mgbe_pcs_init",
"stmmac_pcs_setup",
"rk_pcs_init",
"xpcs_rk_probe",
"xpcs_rk_create"
],
"Reasoning": "The patch introduces support for the Rockchip RK3568 XPCS, which includes a new platform driver and modifications to the generic pcs-xpcs driver and stmmac driver. While the Rockchip-specific platform driver might be difficult to reach in a generic virtualized environment without specific device tree configurations, the patch also refactors how the PCS is initialized in the generic stmmac driver and modifies the Intel mGBE PCI driver (dwmac-intel.c) to use the new initialization flow. Furthermore, it modifies the generic pcs-xpcs.c driver's auto-negotiation and state polling logic for SGMII and 1000BASE-X interfaces. These generic and PCI-based changes are reachable via PCI fuzzing and netlink interface manipulation, making the patch worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"STMMAC_ETH",
"DWMAC_INTEL",
"PCS_XPCS",
"PCS_XPCS_ROCKCHIP",
"DWMAC_ROCKCHIP",
"PHY_ROCKCHIP_NANENG_COMBO_PHY"
],
"FocusSymbols": [
"xpcs_config_aneg_c37_sgmii",
"xpcs_config_aneg_c37_1000basex",
"xpcs_get_state_c37_sgmii",
"intel_mgbe_pcs_init",
"stmmac_pcs_setup",
"rk_pcs_init",
"xpcs_rk_probe",
"xpcs_rk_create"
],
"Reasoning": "The patch introduces support for the Rockchip RK3568 XPCS, which includes a new platform driver and modifications to the generic pcs-xpcs driver and stmmac driver. While the Rockchip-specific platform driver might be difficult to reach in a generic virtualized environment without specific device tree configurations, the patch also refactors how the PCS is initialized in the generic stmmac driver and modifies the Intel mGBE PCI driver (dwmac-intel.c) to use the new initialization flow. Furthermore, it modifies the generic pcs-xpcs.c driver's auto-negotiation and state polling logic for SGMII and 1000BASE-X interfaces. These generic and PCI-based changes are reachable via PCI fuzzing and netlink interface manipulation, making the patch worth fuzzing.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|