AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/07 14:42 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds LED support for the Motorcomm YT9215/YT9218 ethernet switch chips. The driver requires specific hardware to be present on the MDIO bus to probe successfully (it reads the chip ID register and expects specific values like 0x9002 or 0x9001). Since there is no emulation for this specific hardware in QEMU or a generic MDIO mock device in the kernel, the driver will fail to probe in virtualized environments. Consequently, the newly added LED classdev operations and SMI read/write wrappers are completely unreachable and cannot be fuzzed.",
  "WorthFuzzing": false
}

1/1 2026/07/07 14:42 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit e92362724686b0e7c96a21f6c7e699f90d99dd51\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Jul 7 14:42:44 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 52f1a55eca99bf..a3c8d8a01c6ba7 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -18230,7 +18230,7 @@ M:\tDavid Yang \u003cmmyangfl@gmail.com\u003e\n L:\tnetdev@vger.kernel.org\n S:\tMaintained\n F:\tDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml\n-F:\tdrivers/net/dsa/yt921x.*\n+F:\tdrivers/net/dsa/motorcomm/\n F:\tnet/dsa/tag_yt921x.c\n \n MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD\ndiff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig\nindex 4ab567c5bbafc6..98e9bbe47de7b1 100644\n--- a/drivers/net/dsa/Kconfig\n+++ b/drivers/net/dsa/Kconfig\n@@ -72,6 +72,8 @@ config NET_DSA_MV88E6060\n \n source \"drivers/net/dsa/microchip/Kconfig\"\n \n+source \"drivers/net/dsa/motorcomm/Kconfig\"\n+\n source \"drivers/net/dsa/mv88e6xxx/Kconfig\"\n \n source \"drivers/net/dsa/mxl862xx/Kconfig\"\n@@ -158,12 +160,4 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM\n \t  This enables support for the Vitesse VSC7385, VSC7388, VSC7395\n \t  and VSC7398 SparX integrated ethernet switches, connected over\n \t  a CPU-attached address bus and work in memory-mapped I/O mode.\n-\n-config NET_DSA_YT921X\n-\ttristate \"Motorcomm YT9215 ethernet switch chip support\"\n-\tselect NET_DSA_TAG_YT921X\n-\tselect NET_IEEE8021Q_HELPERS if DCB\n-\thelp\n-\t  This enables support for the Motorcomm YT9215 ethernet switch\n-\t  chip.\n endmenu\ndiff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile\nindex d2975badffc093..138225baa4d564 100644\n--- a/drivers/net/dsa/Makefile\n+++ b/drivers/net/dsa/Makefile\n@@ -14,11 +14,11 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o\n obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o\n obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o\n obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o\n-obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o\n obj-y\t\t\t\t+= b53/\n obj-y\t\t\t\t+= hirschmann/\n obj-y\t\t\t\t+= lantiq/\n obj-y\t\t\t\t+= microchip/\n+obj-y\t\t\t\t+= motorcomm/\n obj-y\t\t\t\t+= mv88e6xxx/\n obj-y\t\t\t\t+= mxl862xx/\n obj-y\t\t\t\t+= netc/\ndiff --git a/drivers/net/dsa/motorcomm/Kconfig b/drivers/net/dsa/motorcomm/Kconfig\nnew file mode 100644\nindex 00000000000000..3438e0b143611a\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/Kconfig\n@@ -0,0 +1,17 @@\n+# SPDX-License-Identifier: ISC\n+config NET_DSA_YT921X\n+\ttristate \"Motorcomm YT9215 ethernet switch chip support\"\n+\tselect NET_DSA_TAG_YT921X\n+\tselect NET_IEEE8021Q_HELPERS if DCB\n+\thelp\n+\t  This enables support for the Motorcomm YT9215 ethernet switch\n+\t  chip.\n+\n+config NET_DSA_YT921X_LEDS\n+\tbool \"LED support for Motorcomm YT9215\"\n+\tdefault y\n+\tdepends on NET_DSA_YT921X\n+\tdepends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_YT921X\n+\thelp\n+\t  This enables support for controlling the LEDs attached to the\n+\t  Motorcomm YT9215 switch chips.\ndiff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile\nnew file mode 100644\nindex 00000000000000..5a63db0029ffe2\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/Makefile\n@@ -0,0 +1,5 @@\n+# SPDX-License-Identifier: ISC\n+obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o\n+yt921x-objs := chip.o\n+yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o\n+yt921x-objs += smi.o\ndiff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/motorcomm/chip.c\nsimilarity index 96%\nrename from drivers/net/dsa/yt921x.c\nrename to drivers/net/dsa/motorcomm/chip.c\nindex 159b16606f6ca4..553efacd4f5ee3 100644\n--- a/drivers/net/dsa/yt921x.c\n+++ b/drivers/net/dsa/motorcomm/chip.c\n@@ -13,7 +13,6 @@\n #include \u003clinux/if_bridge.h\u003e\n #include \u003clinux/if_hsr.h\u003e\n #include \u003clinux/if_vlan.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@@ -26,7 +25,9 @@\n #include \u003cnet/ieee8021q.h\u003e\n #include \u003cnet/pkt_cls.h\u003e\n \n-#include \"yt921x.h\"\n+#include \"chip.h\"\n+#include \"leds.h\"\n+#include \"smi.h\"\n \n struct yt921x_mib_desc {\n \tunsigned int size;\n@@ -151,13 +152,8 @@ static const struct yt921x_info yt921x_infos[] = {\n \t{}\n };\n \n-#define YT921X_NAME\t\"yt921x\"\n-\n #define YT921X_VID_UNWARE\t4095\n \n-#define YT921X_POLL_SLEEP_US\t10000\n-#define YT921X_POLL_TIMEOUT_US\t100000\n-\n /* The interval should be small enough to avoid overflow of 32bit MIBs.\n  *\n  * Until we can read MIBs from stats64 call directly (i.e. sleep\n@@ -196,208 +192,6 @@ static u32 ethaddr_lo2_to_u32(const unsigned char *addr)\n \treturn (addr[4] \u003c\u003c 8) | addr[5];\n }\n \n-static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)\n-{\n-\tWARN_ON(!mutex_is_locked(\u0026priv-\u003ereg_lock));\n-\n-\treturn priv-\u003ereg_ops-\u003eread(priv-\u003ereg_ctx, reg, valp);\n-}\n-\n-static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)\n-{\n-\tWARN_ON(!mutex_is_locked(\u0026priv-\u003ereg_lock));\n-\n-\treturn priv-\u003ereg_ops-\u003ewrite(priv-\u003ereg_ctx, reg, val);\n-}\n-\n-static int\n-yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)\n-{\n-\tu32 val;\n-\tint res;\n-\tint ret;\n-\n-\tret = read_poll_timeout(yt921x_reg_read, res,\n-\t\t\t\tres || (val \u0026 mask) == *valp,\n-\t\t\t\tYT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,\n-\t\t\t\tfalse, priv, reg, \u0026val);\n-\tif (ret)\n-\t\treturn ret;\n-\tif (res)\n-\t\treturn res;\n-\n-\t*valp = val;\n-\treturn 0;\n-}\n-\n-static int\n-yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)\n-{\n-\tint res;\n-\tu32 v;\n-\tu32 u;\n-\n-\tres = yt921x_reg_read(priv, reg, \u0026v);\n-\tif (res)\n-\t\treturn res;\n-\n-\tu = v;\n-\tu \u0026= ~mask;\n-\tu |= val;\n-\tif (u == v)\n-\t\treturn 0;\n-\n-\treturn yt921x_reg_write(priv, reg, u);\n-}\n-\n-static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)\n-{\n-\treturn yt921x_reg_update_bits(priv, reg, 0, mask);\n-}\n-\n-static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)\n-{\n-\treturn yt921x_reg_update_bits(priv, reg, mask, 0);\n-}\n-\n-static int\n-yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)\n-{\n-\treturn yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);\n-}\n-\n-/* Some multi-word registers, like VLANn_CTRL, should be treated as a single\n- * long register. More specifically, writes to parts of its words won't become\n- * visible, until the last word is written.\n- *\n- * Here we require full read and write operations over these registers to\n- * eliminate potential issues, although partial reads/writes are also possible.\n- */\n-\n-static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)\n-{\n-\t*lo \u0026= ~lower_32_bits(mask);\n-\t*hi \u0026= ~upper_32_bits(mask);\n-\t*lo |= lower_32_bits(val);\n-\t*hi |= upper_32_bits(val);\n-}\n-\n-static int\n-yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,\n-\t\t unsigned int num_regs)\n-{\n-\tint res;\n-\n-\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n-\t\tres = yt921x_reg_read(priv, reg + 4 * i, \u0026vals[i]);\n-\t\tif (res)\n-\t\t\treturn res;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int\n-yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,\n-\t\t  unsigned int num_regs)\n-{\n-\tint res;\n-\n-\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n-\t\tres = yt921x_reg_write(priv, reg + 4 * i, vals[i]);\n-\t\tif (res)\n-\t\t\treturn res;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int\n-yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n-\t\t\tconst u32 *vals, unsigned int num_regs)\n-{\n-\tbool changed = false;\n-\tu32 vs[4];\n-\tint res;\n-\n-\tBUILD_BUG_ON(num_regs \u003e ARRAY_SIZE(vs));\n-\n-\tres = yt921x_regs_read(priv, reg, vs, num_regs);\n-\tif (res)\n-\t\treturn res;\n-\n-\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n-\t\tu32 u = vs[i];\n-\n-\t\tu \u0026= ~masks[i];\n-\t\tu |= vals[i];\n-\t\tif (u != vs[i])\n-\t\t\tchanged = true;\n-\n-\t\tvs[i] = u;\n-\t}\n-\n-\tif (!changed)\n-\t\treturn 0;\n-\n-\treturn yt921x_regs_write(priv, reg, vs, num_regs);\n-}\n-\n-static int\n-yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n-\t\t       unsigned int num_regs)\n-{\n-\tbool changed = false;\n-\tu32 vs[4];\n-\tint res;\n-\n-\tBUILD_BUG_ON(num_regs \u003e ARRAY_SIZE(vs));\n-\n-\tres = yt921x_regs_read(priv, reg, vs, num_regs);\n-\tif (res)\n-\t\treturn res;\n-\n-\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n-\t\tu32 u = vs[i];\n-\n-\t\tu \u0026= ~masks[i];\n-\t\tif (u != vs[i])\n-\t\t\tchanged = true;\n-\n-\t\tvs[i] = u;\n-\t}\n-\n-\tif (!changed)\n-\t\treturn 0;\n-\n-\treturn yt921x_regs_write(priv, reg, vs, num_regs);\n-}\n-\n-static int\n-yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)\n-{\n-\treturn yt921x_regs_write(priv, reg, vals, 2);\n-}\n-\n-static int\n-yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n-\t\t\t const u32 *vals)\n-{\n-\treturn yt921x_regs_update_bits(priv, reg, masks, vals, 2);\n-}\n-\n-static int\n-yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)\n-{\n-\treturn yt921x_regs_clear_bits(priv, reg, masks, 2);\n-}\n-\n-static int\n-yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)\n-{\n-\treturn yt921x_regs_write(priv, reg, vals, 3);\n-}\n-\n static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)\n {\n \tstruct yt921x_reg_mdio *mdio = context;\n@@ -4786,6 +4580,12 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)\n \tif (res)\n \t\treturn res;\n \n+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n+\tres = yt921x_leds_setup(priv);\n+\tif (res)\n+\t\tdev_warn(dev, \"Failed to setup LEDs: %d\\n\", res);\n+#endif\n+\n \treturn 0;\n }\n \n@@ -4886,6 +4686,10 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)\n \tif (!priv)\n \t\treturn;\n \n+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n+\tyt921x_leds_remove(priv);\n+#endif\n+\n \tfor (size_t i = ARRAY_SIZE(priv-\u003eports); i-- \u003e 0; ) {\n \t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n \ndiff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/motorcomm/chip.h\nsimilarity index 99%\nrename from drivers/net/dsa/yt921x.h\nrename to drivers/net/dsa/motorcomm/chip.h\nindex 55504652666952..6570ae5902adeb 100644\n--- a/drivers/net/dsa/yt921x.h\n+++ b/drivers/net/dsa/motorcomm/chip.h\n@@ -850,8 +850,13 @@ enum yt921x_fdb_entry_status {\n #define YT921X_ACL_NUM\t\t(YT921X_ACL_BLK_NUM * YT921X_ACL_ENT_PER_BLK)\n #define YT921X_UDF_NUM\t\t8\n \n+#define YT921X_LED_GROUP_NUM\t3\n+\n /* 8 internal + 2 external + 1 mcu */\n-#define YT921X_PORT_NUM\t\t\t11\n+#define YT921X_PORT_NUM\t\t11\n+#define YT921X_PORT_MCU\t\t10\n+\n+#define YT921X_NAME\t\"yt921x\"\n \n #define yt921x_port_is_internal(port) ((port) \u003c 8)\n #define yt921x_port_is_external(port) (8 \u003c= (port) \u0026\u0026 (port) \u003c 9)\n@@ -938,6 +943,13 @@ struct yt921x_port {\n \tstruct yt921x_mib mib;\n \tu64 rx_frames;\n \tu64 tx_frames;\n+\n+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n+\tunsigned char led_duty;\n+\tunsigned short led_cycle;\n+\n+\tstruct yt921x_led *leds[YT921X_LED_GROUP_NUM];\n+#endif\n };\n \n struct yt921x_reg_ops {\ndiff --git a/drivers/net/dsa/motorcomm/leds.c b/drivers/net/dsa/motorcomm/leds.c\nnew file mode 100644\nindex 00000000000000..d2e68e2f21d67e\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/leds.c\n@@ -0,0 +1,615 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#include \u003clinux/uleds.h\u003e\n+\n+#include \"chip.h\"\n+#include \"leds.h\"\n+#include \"smi.h\"\n+\n+#define to_yt921x_led(led_cdev) \\\n+\tcontainer_of_const((led_cdev), struct yt921x_led, cdev)\n+#define to_yt921x_port(led) ((led)-\u003eport)\n+#define to_yt921x_priv(pp) \\\n+\tcontainer_of_const((pp), struct yt921x_priv, ports[(pp)-\u003eindex])\n+#define to_device(priv) ((priv)-\u003eds.dev)\n+\n+static u32 yt921x_led_regaddr(struct yt921x_priv *priv, int port, int group)\n+{\n+\tswitch (group) {\n+\tcase 0:\n+\tdefault:\n+\t\treturn YT921X_LED0_PORTn(port);\n+\tcase 1:\n+\t\treturn YT921X_LED1_PORTn(port);\n+\tcase 2:\n+\t\treturn YT921X_LED2_PORTn(port);\n+\t}\n+}\n+\n+static int\n+yt921x_led_force_get(struct yt921x_priv *priv, int port, int group, bool *onp)\n+{\n+\tu32 val;\n+\tint res;\n+\n+\tres = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), \u0026val);\n+\tif (res)\n+\t\treturn res;\n+\n+\t*onp = (val \u0026 YT921X_LED2_PORT_FORCEn_M(group)) ==\n+\t       YT921X_LED2_PORT_FORCEn_ON(group);\n+\treturn 0;\n+}\n+\n+static int\n+yt921x_led_force_set(struct yt921x_priv *priv, int port, int group, bool on)\n+{\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_led *led = pp-\u003eleds[group];\n+\tu32 ctrl;\n+\tu32 mask;\n+\n+\tled-\u003euse_cycle = false;\n+\tled-\u003euse_duty = false;\n+\n+\tmask = YT921X_LED2_PORT_FORCEn_M(group);\n+\tctrl = on ? YT921X_LED2_PORT_FORCEn_ON(group) :\n+\t       YT921X_LED2_PORT_FORCEn_OFF(group);\n+\treturn yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port), mask,\n+\t\t\t\t      ctrl);\n+}\n+\n+static int\n+yt921x_led_blink_select(const struct yt921x_priv *priv, unsigned long on,\n+\t\t\tunsigned long off, unsigned short *cyclep,\n+\t\t\tunsigned char *dutyp)\n+{\n+\tstatic const unsigned char dutys[] = {\n+\t\tYT921X_LED_DUTY(1, 6),\n+\t\tYT921X_LED_DUTY(1, 4),\n+\t\tYT921X_LED_DUTY(1, 3),\n+\t\tYT921X_LED_DUTY(1, 2),\n+\t};\n+\tunsigned int cycle_upper;\n+\tunsigned int cycle_req;\n+\tunsigned int duty_req;\n+\tunsigned int cycle;\n+\tunsigned int duty;\n+\n+\tcycle = YT921X_LED_BLINK_MAX;\n+\tcycle_upper = cycle * 11585 / 8192 + 1;  /* M_SQRT2 * cycle */\n+\tif (check_add_overflow(on, off, \u0026cycle_req) || cycle_req \u003e= cycle_upper)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tfor (; cycle \u003e YT921X_LED_BLINK_MIN; cycle_upper \u003e\u003e= 1, cycle \u003e\u003e= 1)\n+\t\tif (cycle_req \u003e= cycle_upper \u003e\u003e 1)\n+\t\t\tbreak;\n+\t*cyclep = cycle;\n+\n+\tduty_req = DIV_ROUND_CLOSEST(YT921X_LED_DUTY_DENOM *\n+\t\t\t\t     (on \u003e off ? off : on), cycle_req);\n+\tfor (unsigned int i = ARRAY_SIZE(dutys) - 1;; i--)\n+\t\tif (i == 0 || duty_req \u003e= (dutys[i - 1] + dutys[i]) / 2) {\n+\t\t\tduty = dutys[i];\n+\t\t\tbreak;\n+\t\t}\n+\tif (on \u003e off)\n+\t\tduty = YT921X_LED_DUTY_DENOM - duty;\n+\t*dutyp = duty;\n+\n+\treturn 0;\n+}\n+\n+static int\n+yt921x_led_blink_set(struct yt921x_priv *priv, int port, int group,\n+\t\t     unsigned long *onp, unsigned long *offp)\n+{\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_led *led = pp-\u003eleds[group];\n+\tunsigned short cycle;\n+\tunsigned char duty;\n+\tbool use_cycle;\n+\tu32 ctrl;\n+\tu32 mask;\n+\tu32 val;\n+\tint res;\n+\n+\tif (!*onp \u0026\u0026 !*offp) {\n+\t\tcycle = YT921X_LED_BLINK_DEF;\n+\t\tduty = YT921X_LED_DUTY(1, 2);\n+\t\tfor (unsigned int i = 0; i \u003c YT921X_LED_GROUP_NUM; i++)\n+\t\t\tif (i != group \u0026\u0026 pp-\u003eleds[i] \u0026\u0026\n+\t\t\t    pp-\u003eleds[i]-\u003euse_duty) {\n+\t\t\t\tduty = pp-\u003eled_duty;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\tuse_cycle = false;\n+\t} else {\n+\t\tbool change_cycle;\n+\t\tbool change_duty;\n+\n+\t\tres = yt921x_led_blink_select(priv, *onp, *offp, \u0026cycle, \u0026duty);\n+\t\tif (res)\n+\t\t\treturn res;\n+\n+\t\tuse_cycle = cycle \u003c YT921X_LED_BLINK_DEF;\n+\t\tchange_cycle = use_cycle \u0026\u0026 cycle != pp-\u003eled_cycle;\n+\t\tchange_duty = duty != pp-\u003eled_duty;\n+\t\tif (change_cycle || change_duty)\n+\t\t\tfor (unsigned int i = 0; i \u003c YT921X_LED_GROUP_NUM;\n+\t\t\t     i++) {\n+\t\t\t\tif (i == group || !pp-\u003eleds[i])\n+\t\t\t\t\tcontinue;\n+\t\t\t\tif ((change_cycle \u0026\u0026 pp-\u003eleds[i]-\u003euse_cycle) ||\n+\t\t\t\t    (change_duty \u0026\u0026 pp-\u003eleds[i]-\u003euse_duty))\n+\t\t\t\t\treturn -EOPNOTSUPP;\n+\t\t\t}\n+\t}\n+\n+\t/* The chip seems to jam a while if changing duty directly */\n+\tres = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), \u0026val);\n+\tif (res)\n+\t\treturn res;\n+\n+\tctrl = val \u0026 ~YT921X_LED2_PORT_FORCEn_M(group);\n+\tctrl |= YT921X_LED2_PORT_FORCEn_DONTCARE(group);\n+\tif (val != ctrl) {\n+\t\tres = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), ctrl);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\tmask = YT921X_LED1_PORT_BLINK_DUTY_M | YT921X_LED1_PORT_BLINK_DUTY_COMP;\n+\tswitch (duty \u003e= YT921X_LED_DUTY(1, 2) ? duty :\n+\t\tYT921X_LED_DUTY_DENOM - duty) {\n+\tdefault:\n+\t\tduty = YT921X_LED_DUTY(1, 2);\n+\t\tfallthrough;\n+\tcase YT921X_LED_DUTY(1, 2):\n+\t\tctrl = YT921X_LED1_PORT_BLINK_DUTY_1_2;\n+\t\tbreak;\n+\tcase YT921X_LED_DUTY(2, 3):\n+\t\tctrl = YT921X_LED1_PORT_BLINK_DUTY_2_3;\n+\t\tbreak;\n+\tcase YT921X_LED_DUTY(3, 4):\n+\t\tctrl = YT921X_LED1_PORT_BLINK_DUTY_3_4;\n+\t\tbreak;\n+\tcase YT921X_LED_DUTY(5, 6):\n+\t\tctrl = YT921X_LED1_PORT_BLINK_DUTY_5_6;\n+\t\tbreak;\n+\t}\n+\tif (duty \u003c YT921X_LED_DUTY(1, 2))\n+\t\tctrl |= YT921X_LED1_PORT_BLINK_DUTY_COMP;\n+\tif (use_cycle) {\n+\t\tmask |= YT921X_LED1_PORT_OTHER_BLINK_M;\n+\t\tctrl |= YT921X_LED1_PORT_OTHER_BLINK(9 - __fls(cycle));\n+\t}\n+\tres = yt921x_reg_update_bits(priv, YT921X_LED1_PORTn(port), mask, ctrl);\n+\tif (res)\n+\t\treturn res;\n+\n+\tctrl = val \u0026 ~(YT921X_LED2_PORT_FORCEn_M(group) |\n+\t\t       YT921X_LED2_PORT_FORCE_BLINKn_M(group));\n+\tctrl |= YT921X_LED2_PORT_FORCEn_BLINK(group);\n+\tif (use_cycle)\n+\t\tctrl |= YT921X_LED2_PORT_FORCE_BLINKn_OTHER(group);\n+\telse\n+\t\tctrl |= YT921X_LED2_PORT_FORCE_BLINKn(group, __fls(cycle) - 9);\n+\tres = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), ctrl);\n+\tif (res)\n+\t\treturn res;\n+\n+\tled-\u003euse_cycle = use_cycle;\n+\tif (use_cycle)\n+\t\tpp-\u003eled_cycle = cycle;\n+\tled-\u003euse_duty = true;\n+\tpp-\u003eled_duty = duty;\n+\n+\t*onp = DIV_ROUND_CLOSEST(duty * cycle, YT921X_LED_DUTY_DENOM);\n+\t*offp = cycle - *onp;\n+\treturn 0;\n+}\n+\n+static const u32 yt921x_led_trigger_maps[__TRIGGER_NETDEV_MAX] = {\n+\t[TRIGGER_NETDEV_LINK]\t\t= YT921X_LEDx_PORT_ACT_DUPLEX_HALF |\n+\t\t\t\t\t  YT921X_LEDx_PORT_ACT_DUPLEX_FULL,\n+\t[TRIGGER_NETDEV_LINK_10]\t= YT921X_LEDx_PORT_ACT_10M,\n+\t[TRIGGER_NETDEV_LINK_100]\t= YT921X_LEDx_PORT_ACT_100M,\n+\t[TRIGGER_NETDEV_LINK_1000]\t= YT921X_LEDx_PORT_ACT_1000M,\n+\t[TRIGGER_NETDEV_HALF_DUPLEX]\t= YT921X_LEDx_PORT_ACT_DUPLEX_HALF,\n+\t[TRIGGER_NETDEV_FULL_DUPLEX]\t= YT921X_LEDx_PORT_ACT_DUPLEX_FULL,\n+\t[TRIGGER_NETDEV_TX]\t\t= YT921X_LEDx_PORT_ACT_TX,\n+\t[TRIGGER_NETDEV_RX]\t\t= YT921X_LEDx_PORT_ACT_RX,\n+};\n+\n+static bool\n+yt921x_led_trigger_is_supported(const struct yt921x_priv *priv, int port,\n+\t\t\t\tint group, unsigned long flags)\n+{\n+\tunsigned int i;\n+\n+\tfor_each_set_bit(i, \u0026flags, __TRIGGER_NETDEV_MAX)\n+\t\tif (!yt921x_led_trigger_maps[i])\n+\t\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+static int\n+yt921x_led_trigger_get(struct yt921x_priv *priv, int port, int group,\n+\t\t       unsigned long *flagsp)\n+{\n+\tu32 addr;\n+\tu32 val;\n+\tint res;\n+\n+\tres = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), \u0026val);\n+\tif (res)\n+\t\treturn res;\n+\n+\tif ((val \u0026 YT921X_LED2_PORT_FORCEn_M(group)) !=\n+\t    YT921X_LED2_PORT_FORCEn_DONTCARE(group)) {\n+\t\t*flagsp = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tif (group != 2) {\n+\t\taddr = yt921x_led_regaddr(priv, port, group);\n+\t\tres = yt921x_reg_read(priv, addr, \u0026val);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\t*flagsp = 0;\n+\tfor (unsigned int i = 0; i \u003c __TRIGGER_NETDEV_MAX; i++) {\n+\t\tu32 mask = yt921x_led_trigger_maps[i];\n+\n+\t\tif (mask \u0026\u0026 (val \u0026 mask) == mask)\n+\t\t\t*flagsp |= BIT(i);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+yt921x_led_trigger_set(struct yt921x_priv *priv, int port, int group,\n+\t\t       unsigned long flags)\n+{\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_led *led = pp-\u003eleds[group];\n+\tunsigned int i;\n+\tu32 addr;\n+\tu32 ctrl;\n+\tu32 mask;\n+\tint res;\n+\n+\tctrl = 0;\n+\tfor_each_set_bit(i, \u0026flags, __TRIGGER_NETDEV_MAX) {\n+\t\tif (!yt921x_led_trigger_maps[i])\n+\t\t\treturn -EOPNOTSUPP;\n+\n+\t\tctrl |= yt921x_led_trigger_maps[i];\n+\t}\n+\n+\tled-\u003euse_cycle = false;\n+\tled-\u003euse_duty = false;\n+\n+\tmask = !group ? YT921X_LED0_PORT_ACT_M : YT921X_LEDx_PORT_ACT_M;\n+\tif (group == 2) {\n+\t\tmask |= YT921X_LED2_PORT_FORCEn_M(group);\n+\t\tctrl |= YT921X_LED2_PORT_FORCEn_DONTCARE(group);\n+\t}\n+\taddr = yt921x_led_regaddr(priv, port, group);\n+\tres = yt921x_reg_update_bits(priv, addr, mask, ctrl);\n+\tif (res)\n+\t\treturn res;\n+\n+\tif (group != 2) {\n+\t\tmask = YT921X_LED2_PORT_FORCEn_M(group);\n+\t\tctrl = YT921X_LED2_PORT_FORCEn_DONTCARE(group);\n+\t\tres = yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port),\n+\t\t\t\t\t     mask, ctrl);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+yt921x_cled_brightness_set_blocking(struct led_classdev *led_cdev,\n+\t\t\t\t    enum led_brightness brightness)\n+{\n+\tstruct yt921x_led *led = to_yt921x_led(led_cdev);\n+\tstruct yt921x_port *pp = to_yt921x_port(led);\n+\tstruct yt921x_priv *priv = to_yt921x_priv(pp);\n+\tint res;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tres = yt921x_led_force_set(priv, pp-\u003eindex, led-\u003egroup, brightness);\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\n+\treturn res;\n+}\n+\n+static int\n+yt921x_cled_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,\n+\t\t      unsigned long *delay_off)\n+{\n+\tstruct yt921x_led *led = to_yt921x_led(led_cdev);\n+\tstruct yt921x_port *pp = to_yt921x_port(led);\n+\tstruct yt921x_priv *priv = to_yt921x_priv(pp);\n+\tint res;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tres = yt921x_led_blink_set(priv, pp-\u003eindex, led-\u003egroup, delay_on,\n+\t\t\t\t   delay_off);\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\n+\treturn res;\n+}\n+\n+static struct device * __maybe_unused\n+yt921x_cled_hw_control_get_device(struct led_classdev *led_cdev)\n+{\n+\tstruct yt921x_led *led = to_yt921x_led(led_cdev);\n+\tstruct yt921x_port *pp = to_yt921x_port(led);\n+\tstruct yt921x_priv *priv = to_yt921x_priv(pp);\n+\tstruct dsa_port *dp;\n+\n+\tdp = dsa_to_port(\u0026priv-\u003eds, pp-\u003eindex);\n+\tif (!dp || !dp-\u003euser)\n+\t\treturn NULL;\n+\treturn \u0026dp-\u003euser-\u003edev;\n+}\n+\n+static int __maybe_unused\n+yt921x_cled_hw_control_is_supported(struct led_classdev *led_cdev,\n+\t\t\t\t    unsigned long flags)\n+{\n+\tstruct yt921x_led *led = to_yt921x_led(led_cdev);\n+\tstruct yt921x_port *pp = to_yt921x_port(led);\n+\tstruct yt921x_priv *priv = to_yt921x_priv(pp);\n+\n+\tif (yt921x_led_trigger_is_supported(priv, pp-\u003eindex, led-\u003egroup, flags))\n+\t\treturn 0;\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int __maybe_unused\n+yt921x_cled_hw_control_get(struct led_classdev *led_cdev, unsigned long *flagsp)\n+{\n+\tstruct yt921x_led *led = to_yt921x_led(led_cdev);\n+\tstruct yt921x_port *pp = to_yt921x_port(led);\n+\tstruct yt921x_priv *priv = to_yt921x_priv(pp);\n+\tint res;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tres = yt921x_led_trigger_get(priv, pp-\u003eindex, led-\u003egroup, flagsp);\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\n+\treturn res;\n+}\n+\n+static int __maybe_unused\n+yt921x_cled_hw_control_set(struct led_classdev *led_cdev, unsigned long flags)\n+{\n+\tstruct yt921x_led *led = to_yt921x_led(led_cdev);\n+\tstruct yt921x_port *pp = to_yt921x_port(led);\n+\tstruct yt921x_priv *priv = to_yt921x_priv(pp);\n+\tint res;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tres = yt921x_led_trigger_set(priv, pp-\u003eindex, led-\u003egroup, flags);\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\n+\treturn res;\n+}\n+\n+static int\n+yt921x_led_setup(struct yt921x_priv *priv, int port,\n+\t\t struct fwnode_handle *fwnode, u32 *inv_maskp, u32 *inv_ctrlp)\n+{\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct device *dev = to_device(priv);\n+\tstruct led_init_data init_data;\n+\tstruct led_classdev *led_cdev;\n+\tchar name[LED_MAX_NAME_SIZE];\n+\tenum led_default_state state;\n+\tstruct yt921x_led *led;\n+\tbool force_high;\n+\tbool force_low;\n+\tint polarity;\n+\tu32 group;\n+\tbool on;\n+\tint res;\n+\n+\tif (port == YT921X_PORT_MCU) {\n+\t\tdev_err(dev, \"No LEDs for port %d\\n\", port);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tres = fwnode_property_read_u32(fwnode, \"reg\", \u0026group);\n+\tif (res)\n+\t\treturn res;\n+\tif (group \u003e= YT921X_LED_GROUP_NUM) {\n+\t\tdev_err(dev, \"Invalid LED reg %u for port %d\\n\", group, port);\n+\t\treturn -EINVAL;\n+\t}\n+\tif (pp-\u003eleds[group])\n+\t\treturn -EEXIST;\n+\n+\tforce_high = fwnode_property_read_bool(fwnode, \"active-high\");\n+\tforce_low = fwnode_property_read_bool(fwnode, \"active-low\");\n+\tif (force_high \u0026\u0026 force_low) {\n+\t\tdev_err(dev, \"Duplicate polarities for LED %02d:%02u\\n\",\n+\t\t\tport, group);\n+\t\treturn -EINVAL;\n+\t}\n+\tpolarity = force_high ? 1 : force_low ? -1 : 0;\n+\n+\tled = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);\n+\tif (!led)\n+\t\treturn -ENOMEM;\n+\tled-\u003eport = pp;\n+\tled-\u003egroup = group;\n+\n+\tstate = led_init_default_state_get(fwnode);\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tpp-\u003eleds[group] = led;\n+\tswitch (state) {\n+\tcase LEDS_DEFSTATE_OFF:\n+\tcase LEDS_DEFSTATE_ON:\n+\tdefault:\n+\t\ton = state == LEDS_DEFSTATE_ON;\n+\t\tres = yt921x_led_force_set(priv, port, group, on);\n+\t\tbreak;\n+\tcase LEDS_DEFSTATE_KEEP:\n+\t\tres = yt921x_led_force_get(priv, port, group, \u0026on);\n+\t\tbreak;\n+\t}\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\tif (res)\n+\t\tgoto err;\n+\n+\tled_cdev = \u0026led-\u003ecdev;\n+\tled_cdev-\u003ebrightness = on;\n+\tled_cdev-\u003emax_brightness = 1;\n+\tled_cdev-\u003eflags = LED_RETAIN_AT_SHUTDOWN;\n+\tled_cdev-\u003ebrightness_set_blocking = yt921x_cled_brightness_set_blocking;\n+\tled_cdev-\u003eblink_set = yt921x_cled_blink_set;\n+#ifdef CONFIG_LEDS_TRIGGERS\n+\tled_cdev-\u003ehw_control_trigger = \"netdev\";\n+\tled_cdev-\u003ehw_control_get_device = yt921x_cled_hw_control_get_device;\n+\tled_cdev-\u003ehw_control_is_supported = yt921x_cled_hw_control_is_supported;\n+\tled_cdev-\u003ehw_control_get = yt921x_cled_hw_control_get;\n+\tled_cdev-\u003ehw_control_set = yt921x_cled_hw_control_set;\n+#endif\n+\n+\tsnprintf(name, sizeof(name), YT921X_NAME \"-%u:%02d:%02u\",\n+\t\t priv-\u003eds.index, port, group);\n+\tinit_data = (typeof(init_data)){\n+\t\t.fwnode = fwnode,\n+\t\t.devicename = name,\n+\t\t.devname_mandatory = true,\n+\t};\n+\tres = devm_led_classdev_register_ext(dev, led_cdev, \u0026init_data);\n+\tif (res)\n+\t\tgoto err;\n+\n+\tif (polarity) {\n+\t\tu32 bit = YT921X_LED_PAR_INV_INVnm(group, port);\n+\n+\t\t*inv_maskp |= bit;\n+\t\tif (polarity \u003e 0)\n+\t\t\t*inv_ctrlp |= bit;\n+\t}\n+\treturn 0;\n+\n+err:\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tpp-\u003eleds[group] = NULL;\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\n+\tdevm_kfree(dev, led);\n+\treturn res;\n+}\n+\n+void yt921x_leds_remove(struct yt921x_priv *priv)\n+{\n+\tstruct device *dev = to_device(priv);\n+\n+\tfor (int port = 0; port \u003c YT921X_PORT_NUM; port++) {\n+\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\n+\t\tfor (int group = 0; group \u003c YT921X_LED_GROUP_NUM; group++) {\n+\t\t\tstruct yt921x_led *led = pp-\u003eleds[group];\n+\n+\t\t\tif (led)\n+\t\t\t\tdevm_led_classdev_unregister(dev, \u0026led-\u003ecdev);\n+\t\t}\n+\t}\n+\n+\tfor (int port = 0; port \u003c YT921X_PORT_NUM; port++) {\n+\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\n+\t\tfor (int group = 0; group \u003c YT921X_LED_GROUP_NUM; group++) {\n+\t\t\tstruct yt921x_led *led = pp-\u003eleds[group];\n+\n+\t\t\tif (led) {\n+\t\t\t\tpp-\u003eleds[group] = NULL;\n+\t\t\t\tdevm_kfree(dev, led);\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+int yt921x_leds_setup(struct yt921x_priv *priv)\n+{\n+\tstruct dsa_switch *ds = \u0026priv-\u003eds;\n+\tstruct dsa_port *dp;\n+\tu32 inv_ctrl = 0;\n+\tu32 inv_mask = 0;\n+\tu32 ctrl;\n+\tu32 mask;\n+\tint res;\n+\n+\tdsa_switch_for_each_port(dp, ds) {\n+\t\tstruct device_node *leds_np;\n+\t\tint port = dp-\u003eindex;\n+\n+\t\tif (!dp-\u003edn)\n+\t\t\tcontinue;\n+\n+\t\tleds_np = of_get_child_by_name(dp-\u003edn, \"leds\");\n+\t\tif (!leds_np)\n+\t\t\tcontinue;\n+\n+\t\tfor_each_child_of_node_scoped(leds_np, led_np) {\n+\t\t\tres = yt921x_led_setup(priv, port,\n+\t\t\t\t\t       of_fwnode_handle(led_np),\n+\t\t\t\t\t       \u0026inv_mask, \u0026inv_ctrl);\n+\t\t\tif (res) {\n+\t\t\t\tof_node_put(leds_np);\n+\t\t\t\tgoto err;\n+\t\t\t}\n+\t\t}\n+\n+\t\tof_node_put(leds_np);\n+\t}\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tmask = YT921X_LED_CTRL_MODE_M | YT921X_LED_CTRL_PORT_NUM_M |\n+\t       YT921X_LED_CTRL_EN;\n+\tctrl = YT921X_LED_CTRL_MODE_PARALLEL |\n+\t       YT921X_LED_CTRL_PORT_NUM(YT921X_PORT_NUM - 1) |\n+\t       YT921X_LED_CTRL_EN;\n+\tres = yt921x_reg_update_bits(priv, YT921X_LED_CTRL, mask, ctrl);\n+\tif (res)\n+\t\tgoto end;\n+\n+\t/* Inversion is internal - force on will give low logic.\n+\t * In the rest of the file, treat LEDs as if active-low.\n+\t */\n+\tif (inv_mask)\n+\t\tres = yt921x_reg_update_bits(priv, YT921X_LED_PAR_INV, inv_mask,\n+\t\t\t\t\t     inv_ctrl);\n+\n+end:\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\n+\tif (res)\n+\t\tgoto err;\n+\n+\treturn 0;\n+\n+err:\n+\tyt921x_leds_remove(priv);\n+\treturn res;\n+}\ndiff --git a/drivers/net/dsa/motorcomm/leds.h b/drivers/net/dsa/motorcomm/leds.h\nnew file mode 100644\nindex 00000000000000..67373a190c8f4b\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/leds.h\n@@ -0,0 +1,121 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#ifndef _YT_LEDS_H\n+#define _YT_LEDS_H\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/bits.h\u003e\n+#include \u003clinux/kconfig.h\u003e\n+#include \u003clinux/leds.h\u003e\n+\n+#define YT921X_LED_CTRL\t\t\t0xd0000\n+#define  YT921X_LED_CTRL_EN\t\t\tBIT(21)\n+#define  YT921X_LED_CTRL_LOOPDETECT_BLINK_M\tGENMASK(20, 19)\t/* cycle = 512 * x ms */\n+#define   YT921X_LED_CTRL_LOOPDETECT_BLINK(x)\t\tFIELD_PREP(YT921X_LED_CTRL_LOOPDETECT_BLINK_M, (x))\n+#define  YT921X_LED_CTRL_PORT_NUM_M\t\tGENMASK(16, 13)\n+#define   YT921X_LED_CTRL_PORT_NUM(x)\t\t\tFIELD_PREP(YT921X_LED_CTRL_PORT_NUM_M, (x))\n+#define  YT921X_LED_CTRL_MODE_M\t\t\tGENMASK(1, 0)\n+#define   YT921X_LED_CTRL_MODE(x)\t\t\tFIELD_PREP(YT921X_LED_CTRL_MODE_M, (x))\n+#define   YT921X_LED_CTRL_MODE_PARALLEL\t\t\tYT921X_LED_CTRL_MODE(0)\n+#define   YT921X_LED_CTRL_MODE_SERIAL\t\t\tYT921X_LED_CTRL_MODE(2)\n+#define YT921X_LED0_PORTn(port)\t\t(0xd0004 + 4 * (port))\n+#define  YT921X_LED0_PORT_ACT_M\t\t\tGENMASK(17, 0)\n+#define  YT921X_LED0_PORT_ACT_LINK_TRY_DIS\tBIT(17)\n+#define  YT921X_LED0_PORT_ACT_COLLISION_BLINK_INDI\tBIT(16)\n+#define YT921X_LED1_PORTn(port)\t\t(0xd0040 + 4 * (port))\n+#define  YT921X_LED1_PORT_OTHER_BLINK_M\t\tGENMASK(31, 30)\t/* cycle = 512 \u003e\u003e x ms */\n+#define   YT921X_LED1_PORT_OTHER_BLINK(x)\t\tFIELD_PREP(YT921X_LED1_PORT_OTHER_BLINK_M, (x))\n+#define  YT921X_LED1_PORT_EEE_BLINK_M\t\tGENMASK(29, 28)\t/* cycle = 512 \u003e\u003e x ms */\n+#define   YT921X_LED1_PORT_EEE_BLINK(x)\t\t\tFIELD_PREP(YT921X_LED1_PORT_EEE_BLINK_M, (x))\n+#define  YT921X_LED1_PORT_BLINK_DUTY_COMP\tBIT(27)\n+#define  YT921X_LED1_PORT_BLINK_DUTY_M\t\tGENMASK(26, 25)\n+#define   YT921X_LED1_PORT_BLINK_DUTY(x)\t\tFIELD_PREP(YT921X_LED1_PORT_BLINK_DUTY_M, (x))\n+#define   YT921X_LED1_PORT_BLINK_DUTY_1_2\t\tYT921X_LED1_PORT_BLINK_DUTY(0)\n+#define   YT921X_LED1_PORT_BLINK_DUTY_2_3\t\tYT921X_LED1_PORT_BLINK_DUTY(1)\n+#define   YT921X_LED1_PORT_BLINK_DUTY_3_4\t\tYT921X_LED1_PORT_BLINK_DUTY(2)\n+#define   YT921X_LED1_PORT_BLINK_DUTY_5_6\t\tYT921X_LED1_PORT_BLINK_DUTY(3)\n+#define YT921X_LED2_PORTn(port)\t\t(0xd0080 + 4 * (port))\n+#define  YT921X_LED2_PORT_FORCEn_M(grp)\t\tGENMASK(4 * (grp) + 19, 4 * (grp) + 18)\n+#define   YT921X_LED2_PORT_FORCEn(grp, x)\t\t((x) \u003c\u003c (4 * (grp) + 18))\n+#define   YT921X_LED2_PORT_FORCEn_DONTCARE(grp)\t\tYT921X_LED2_PORT_FORCEn(grp, 0)\n+#define   YT921X_LED2_PORT_FORCEn_BLINK(grp)\t\tYT921X_LED2_PORT_FORCEn(grp, 1)\n+#define   YT921X_LED2_PORT_FORCEn_ON(grp)\t\tYT921X_LED2_PORT_FORCEn(grp, 2)\n+#define   YT921X_LED2_PORT_FORCEn_OFF(grp)\t\tYT921X_LED2_PORT_FORCEn(grp, 3)\n+#define  YT921X_LED2_PORT_FORCE_BLINKn_M(grp)\tGENMASK(4 * (grp) + 17, 4 * (grp) + 16)\t/* cycle = 512 \u003c\u003c x ms */\n+#define   YT921X_LED2_PORT_FORCE_BLINKn(grp, x)\t\t((x) \u003c\u003c (4 * (grp) + 16))\n+#define   YT921X_LED2_PORT_FORCE_BLINKn_OTHER(grp)\tYT921X_LED2_PORT_FORCE_BLINKn(grp, 3)\n+#define  YT921X_LEDx_PORT_ACT_M\t\t\tGENMASK(15, 0)\n+#define  YT921X_LEDx_PORT_ACT_EEE_BLINK\t\tBIT(15)\n+#define  YT921X_LEDx_PORT_ACT_LOOPDETECT_BLINK\tBIT(14)\n+#define  YT921X_LEDx_PORT_ACT_ACTIVE_BLINK\tBIT(13)\n+#define  YT921X_LEDx_PORT_ACT_DUPLEX_FULL\tBIT(12)\n+#define  YT921X_LEDx_PORT_ACT_DUPLEX_HALF\tBIT(11)\n+#define  YT921X_LEDx_PORT_ACT_TX_BLINK\t\tBIT(10)\n+#define  YT921X_LEDx_PORT_ACT_RX_BLINK\t\tBIT(9)\n+#define  YT921X_LEDx_PORT_ACT_TX\t\tBIT(8)\n+#define  YT921X_LEDx_PORT_ACT_RX\t\tBIT(7)\n+#define  YT921X_LEDx_PORT_ACT_1000M\t\tBIT(6)\n+#define  YT921X_LEDx_PORT_ACT_100M\t\tBIT(5)\n+#define  YT921X_LEDx_PORT_ACT_10M\t\tBIT(4)\n+#define  YT921X_LEDx_PORT_ACT_COLLISION_BLINK\tBIT(3)\n+#define  YT921X_LEDx_PORT_ACT_1000M_BLINK\tBIT(2)\n+#define  YT921X_LEDx_PORT_ACT_100M_BLINK\tBIT(1)\n+#define  YT921X_LEDx_PORT_ACT_10M_BLINK\t\tBIT(0)\n+#define YT921X_LED_SER_CTRL\t\t0xd0100\n+#define  YT921X_LED_SER_CTRL_UNK\t\tGENMASK(25, 24)\t/* delay? */\n+#define  YT921X_LED_SER_CTRL_ACTIVE_LOW\t\tBIT(4)\n+#define  YT921X_LED_SER_CTRL_GRP_NUM_M\t\tGENMASK(1, 0)\t/* #grp - 1 */\n+#define   YT921X_LED_SER_CTRL_GRP_NUM(x)\t\tFIELD_PREP(YT921X_LED_SER_CTRL_GRP_NUM_M, (x))\n+#define YT921X_LED_SER_MAPnm(grp, port)\t(0xd0104 + 8 * (2 - (grp)) + 4 * ((port) / 5))\n+#define  YT921X_LED_SER_MAP_DSTn_PORT_M(port)\tGENMASK(6 * ((port) % 5) + 5, 6 * ((port) % 5) + 2)\n+#define   YT921X_LED_SER_MAP_DSTn_PORT(port, x)\t\t((x) \u003c\u003c (6 * ((port) % 5) + 2))\n+#define  YT921X_LED_SER_MAP_DSTn_LED_M(port)\tGENMASK(6 * ((port) % 5) + 1, 6 * ((port) % 5))\n+#define   YT921X_LED_SER_MAP_DSTn_LED(port, x)\t\t((x) \u003c\u003c (6 * ((port) % 5)))\n+#define YT921X_LED_PAR_PORTS\t\t0xd01c4\n+#define YT921X_LED_PAR_INV\t\t0xd01c8\n+#define  YT921X_LED_PAR_INV_INVnm(grp, port)\tBIT(10 * (grp) + (port))\n+#define YT921X_LED_PAR_MAPn(port)\t(0xd01d0 + 4 * (port))\n+#define  YT921X_LED_PAR_MAP_DSTn_PORT_M(grp)\tGENMASK(6 * (grp) + 5, 6 * (grp) + 2)\n+#define   YT921X_LED_PAR_MAP_DSTn_PORT(grp, x)\t\t((x) \u003c\u003c (6 * (grp) + 2))\n+#define  YT921X_LED_PAR_MAP_DSTn_LED_M(grp)\tGENMASK(6 * (grp) + 1, 6 * (grp))\n+#define   YT921X_LED_PAR_MAP_DSTn_LED(grp, x)\t\t((x) \u003c\u003c (6 * (grp)))\n+\n+#define YT921X_LED_BLINK_MIN\t64\n+#define YT921X_LED_BLINK_DEF\t512\n+#define YT921X_LED_BLINK_MAX\t2048\n+\n+/* 2 * lcm(2, 3, 4, 6) */\n+#define YT921X_LED_DUTY_DENOM\t\t24\n+#define YT921X_LED_DUTY(nom, denom)\t(YT921X_LED_DUTY_DENOM * (nom) / (denom))\n+\n+struct yt921x_priv;\n+\n+struct yt921x_led {\n+\tstruct led_classdev cdev;\n+\tstruct yt921x_port *port;\n+\tunsigned char group;\n+\n+\tbool use_cycle:1;\n+\tbool use_duty:1;\n+};\n+\n+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n+\n+void yt921x_leds_remove(struct yt921x_priv *priv);\n+int yt921x_leds_setup(struct yt921x_priv *priv);\n+\n+#else\n+\n+static inline void yt921x_leds_remove(struct yt921x_priv *priv) {}\n+\n+static inline int yt921x_leds_setup(struct yt921x_priv *priv)\n+{\n+\treturn 0;\n+}\n+\n+#endif\n+\n+#endif\ndiff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c\nnew file mode 100644\nindex 00000000000000..9054896e4cd12b\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/smi.c\n@@ -0,0 +1,157 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#include \u003clinux/iopoll.h\u003e\n+\n+#include \"chip.h\"\n+#include \"smi.h\"\n+\n+#define YT921X_POLL_SLEEP_US\t10000\n+#define YT921X_POLL_TIMEOUT_US\t100000\n+\n+int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)\n+{\n+\tlockdep_assert_held_once(\u0026priv-\u003ereg_lock);\n+\n+\treturn priv-\u003ereg_ops-\u003eread(priv-\u003ereg_ctx, reg, valp);\n+}\n+\n+int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)\n+{\n+\tlockdep_assert_held_once(\u0026priv-\u003ereg_lock);\n+\n+\treturn priv-\u003ereg_ops-\u003ewrite(priv-\u003ereg_ctx, reg, val);\n+}\n+\n+int yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)\n+{\n+\tu32 val;\n+\tint res;\n+\tint ret;\n+\n+\tret = read_poll_timeout(yt921x_reg_read, res,\n+\t\t\t\tres || (val \u0026 mask) == *valp,\n+\t\t\t\tYT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,\n+\t\t\t\tfalse, priv, reg, \u0026val);\n+\tif (ret)\n+\t\treturn ret;\n+\tif (res)\n+\t\treturn res;\n+\n+\t*valp = val;\n+\treturn 0;\n+}\n+\n+int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)\n+{\n+\tint res;\n+\tu32 v;\n+\tu32 u;\n+\n+\tres = yt921x_reg_read(priv, reg, \u0026v);\n+\tif (res)\n+\t\treturn res;\n+\n+\tu = v;\n+\tu \u0026= ~mask;\n+\tu |= val;\n+\tif (u == v)\n+\t\treturn 0;\n+\n+\treturn yt921x_reg_write(priv, reg, u);\n+}\n+\n+int\n+yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,\n+\t\t unsigned int num_regs)\n+{\n+\tint res;\n+\n+\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n+\t\tres = yt921x_reg_read(priv, reg + 4 * i, \u0026vals[i]);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int\n+yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,\n+\t\t  unsigned int num_regs)\n+{\n+\tint res;\n+\n+\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n+\t\tres = yt921x_reg_write(priv, reg + 4 * i, vals[i]);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int\n+yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n+\t\t\tconst u32 *vals, unsigned int num_regs)\n+{\n+\tbool changed = false;\n+\tu32 vs[4];\n+\tint res;\n+\n+\tif (WARN_ON_ONCE(num_regs \u003e ARRAY_SIZE(vs)))\n+\t\treturn -EINVAL;\n+\n+\tres = yt921x_regs_read(priv, reg, vs, num_regs);\n+\tif (res)\n+\t\treturn res;\n+\n+\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n+\t\tu32 u = vs[i];\n+\n+\t\tu \u0026= ~masks[i];\n+\t\tu |= vals[i];\n+\t\tif (u != vs[i])\n+\t\t\tchanged = true;\n+\n+\t\tvs[i] = u;\n+\t}\n+\n+\tif (!changed)\n+\t\treturn 0;\n+\n+\treturn yt921x_regs_write(priv, reg, vs, num_regs);\n+}\n+\n+int\n+yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n+\t\t       unsigned int num_regs)\n+{\n+\tbool changed = false;\n+\tu32 vs[4];\n+\tint res;\n+\n+\tif (WARN_ON_ONCE(num_regs \u003e ARRAY_SIZE(vs)))\n+\t\treturn -EINVAL;\n+\n+\tres = yt921x_regs_read(priv, reg, vs, num_regs);\n+\tif (res)\n+\t\treturn res;\n+\n+\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n+\t\tu32 u = vs[i];\n+\n+\t\tu \u0026= ~masks[i];\n+\t\tif (u != vs[i])\n+\t\t\tchanged = true;\n+\n+\t\tvs[i] = u;\n+\t}\n+\n+\tif (!changed)\n+\t\treturn 0;\n+\n+\treturn yt921x_regs_write(priv, reg, vs, num_regs);\n+}\ndiff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h\nnew file mode 100644\nindex 00000000000000..2e956065eb90ee\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/smi.h\n@@ -0,0 +1,88 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#ifndef _YT_SMI_H\n+#define _YT_SMI_H\n+\n+#include \u003clinux/types.h\u003e\n+#include \u003clinux/wordpart.h\u003e\n+\n+struct yt921x_priv;\n+\n+int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp);\n+int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val);\n+int yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp);\n+int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask,\n+\t\t\t   u32 val);\n+\n+static inline int\n+yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)\n+{\n+\treturn yt921x_reg_update_bits(priv, reg, 0, mask);\n+}\n+\n+static inline int\n+yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)\n+{\n+\treturn yt921x_reg_update_bits(priv, reg, mask, 0);\n+}\n+\n+static inline int\n+yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)\n+{\n+\treturn yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);\n+}\n+\n+/* Some multi-word registers, like VLANn_CTRL, should be treated as a single\n+ * long register. More specifically, writes to parts of its words won't become\n+ * visible, until the last word is written.\n+ *\n+ * Here we require full read and write operations over these registers to\n+ * eliminate potential issues, although partial reads/writes are also possible.\n+ */\n+\n+static inline void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)\n+{\n+\t*lo \u0026= ~lower_32_bits(mask);\n+\t*hi \u0026= ~upper_32_bits(mask);\n+\t*lo |= lower_32_bits(val);\n+\t*hi |= upper_32_bits(val);\n+}\n+\n+int yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,\n+\t\t     unsigned int num_regs);\n+int yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,\n+\t\t      unsigned int num_regs);\n+int yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n+\t\t\t    const u32 *vals, unsigned int num_regs);\n+int yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n+\t\t\t   unsigned int num_regs);\n+\n+static inline int\n+yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)\n+{\n+\treturn yt921x_regs_write(priv, reg, vals, 2);\n+}\n+\n+static inline int\n+yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n+\t\t\t const u32 *vals)\n+{\n+\treturn yt921x_regs_update_bits(priv, reg, masks, vals, 2);\n+}\n+\n+static inline int\n+yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)\n+{\n+\treturn yt921x_regs_clear_bits(priv, reg, masks, 2);\n+}\n+\n+static inline int\n+yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)\n+{\n+\treturn yt921x_regs_write(priv, reg, vals, 3);\n+}\n+\n+#endif\n"
}

2/1 2026/07/07 14:42 agent patch-evaluator
1m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds LED support for the Motorcomm YT9215/YT9218 ethernet switch chips. The driver requires specific hardware to be present on the MDIO bus to probe successfully (it reads the chip ID register and expects specific values like 0x9002 or 0x9001). Since there is no emulation for this specific hardware in QEMU or a generic MDIO mock device in the kernel, the driver will fail to probe in virtualized environments. Consequently, the newly added LED classdev operations and SMI read/write wrappers are completely unreachable and cannot be fuzzed.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

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 e92362724686b0e7c96a21f6c7e699f90d99dd51
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Jul 7 14:42:44 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index 52f1a55eca99bf..a3c8d8a01c6ba7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18230,7 +18230,7 @@ M:	David Yang <mmyangfl@gmail.com>
 L:	netdev@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml
-F:	drivers/net/dsa/yt921x.*
+F:	drivers/net/dsa/motorcomm/
 F:	net/dsa/tag_yt921x.c
 
 MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4ab567c5bbafc6..98e9bbe47de7b1 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -72,6 +72,8 @@ config NET_DSA_MV88E6060
 
 source "drivers/net/dsa/microchip/Kconfig"
 
+source "drivers/net/dsa/motorcomm/Kconfig"
+
 source "drivers/net/dsa/mv88e6xxx/Kconfig"
 
 source "drivers/net/dsa/mxl862xx/Kconfig"
@@ -158,12 +160,4 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM
 	  This enables support for the Vitesse VSC7385, VSC7388, VSC7395
 	  and VSC7398 SparX integrated ethernet switches, connected over
 	  a CPU-attached address bus and work in memory-mapped I/O mode.
-
-config NET_DSA_YT921X
-	tristate "Motorcomm YT9215 ethernet switch chip support"
-	select NET_DSA_TAG_YT921X
-	select NET_IEEE8021Q_HELPERS if DCB
-	help
-	  This enables support for the Motorcomm YT9215 ethernet switch
-	  chip.
 endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index d2975badffc093..138225baa4d564 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -14,11 +14,11 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
 obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
 obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
 obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
-obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
 obj-y				+= b53/
 obj-y				+= hirschmann/
 obj-y				+= lantiq/
 obj-y				+= microchip/
+obj-y				+= motorcomm/
 obj-y				+= mv88e6xxx/
 obj-y				+= mxl862xx/
 obj-y				+= netc/
diff --git a/drivers/net/dsa/motorcomm/Kconfig b/drivers/net/dsa/motorcomm/Kconfig
new file mode 100644
index 00000000000000..3438e0b143611a
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: ISC
+config NET_DSA_YT921X
+	tristate "Motorcomm YT9215 ethernet switch chip support"
+	select NET_DSA_TAG_YT921X
+	select NET_IEEE8021Q_HELPERS if DCB
+	help
+	  This enables support for the Motorcomm YT9215 ethernet switch
+	  chip.
+
+config NET_DSA_YT921X_LEDS
+	bool "LED support for Motorcomm YT9215"
+	default y
+	depends on NET_DSA_YT921X
+	depends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_YT921X
+	help
+	  This enables support for controlling the LEDs attached to the
+	  Motorcomm YT9215 switch chips.
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
new file mode 100644
index 00000000000000..5a63db0029ffe2
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: ISC
+obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
+yt921x-objs := chip.o
+yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o
+yt921x-objs += smi.o
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/motorcomm/chip.c
similarity index 96%
rename from drivers/net/dsa/yt921x.c
rename to drivers/net/dsa/motorcomm/chip.c
index 159b16606f6ca4..553efacd4f5ee3 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -13,7 +13,6 @@
 #include <linux/if_bridge.h>
 #include <linux/if_hsr.h>
 #include <linux/if_vlan.h>
-#include <linux/iopoll.h>
 #include <linux/mdio.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -26,7 +25,9 @@
 #include <net/ieee8021q.h>
 #include <net/pkt_cls.h>
 
-#include "yt921x.h"
+#include "chip.h"
+#include "leds.h"
+#include "smi.h"
 
 struct yt921x_mib_desc {
 	unsigned int size;
@@ -151,13 +152,8 @@ static const struct yt921x_info yt921x_infos[] = {
 	{}
 };
 
-#define YT921X_NAME	"yt921x"
-
 #define YT921X_VID_UNWARE	4095
 
-#define YT921X_POLL_SLEEP_US	10000
-#define YT921X_POLL_TIMEOUT_US	100000
-
 /* The interval should be small enough to avoid overflow of 32bit MIBs.
  *
  * Until we can read MIBs from stats64 call directly (i.e. sleep
@@ -196,208 +192,6 @@ static u32 ethaddr_lo2_to_u32(const unsigned char *addr)
 	return (addr[4] << 8) | addr[5];
 }
 
-static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
-{
-	WARN_ON(!mutex_is_locked(&priv->reg_lock));
-
-	return priv->reg_ops->read(priv->reg_ctx, reg, valp);
-}
-
-static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
-{
-	WARN_ON(!mutex_is_locked(&priv->reg_lock));
-
-	return priv->reg_ops->write(priv->reg_ctx, reg, val);
-}
-
-static int
-yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
-{
-	u32 val;
-	int res;
-	int ret;
-
-	ret = read_poll_timeout(yt921x_reg_read, res,
-				res || (val & mask) == *valp,
-				YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
-				false, priv, reg, &val);
-	if (ret)
-		return ret;
-	if (res)
-		return res;
-
-	*valp = val;
-	return 0;
-}
-
-static int
-yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
-{
-	int res;
-	u32 v;
-	u32 u;
-
-	res = yt921x_reg_read(priv, reg, &v);
-	if (res)
-		return res;
-
-	u = v;
-	u &= ~mask;
-	u |= val;
-	if (u == v)
-		return 0;
-
-	return yt921x_reg_write(priv, reg, u);
-}
-
-static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
-{
-	return yt921x_reg_update_bits(priv, reg, 0, mask);
-}
-
-static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
-{
-	return yt921x_reg_update_bits(priv, reg, mask, 0);
-}
-
-static int
-yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
-{
-	return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
-}
-
-/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
- * long register. More specifically, writes to parts of its words won't become
- * visible, until the last word is written.
- *
- * Here we require full read and write operations over these registers to
- * eliminate potential issues, although partial reads/writes are also possible.
- */
-
-static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)
-{
-	*lo &= ~lower_32_bits(mask);
-	*hi &= ~upper_32_bits(mask);
-	*lo |= lower_32_bits(val);
-	*hi |= upper_32_bits(val);
-}
-
-static int
-yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
-		 unsigned int num_regs)
-{
-	int res;
-
-	for (unsigned int i = 0; i < num_regs; i++) {
-		res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
-		  unsigned int num_regs)
-{
-	int res;
-
-	for (unsigned int i = 0; i < num_regs; i++) {
-		res = yt921x_reg_write(priv, reg + 4 * i, vals[i]);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
-			const u32 *vals, unsigned int num_regs)
-{
-	bool changed = false;
-	u32 vs[4];
-	int res;
-
-	BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
-
-	res = yt921x_regs_read(priv, reg, vs, num_regs);
-	if (res)
-		return res;
-
-	for (unsigned int i = 0; i < num_regs; i++) {
-		u32 u = vs[i];
-
-		u &= ~masks[i];
-		u |= vals[i];
-		if (u != vs[i])
-			changed = true;
-
-		vs[i] = u;
-	}
-
-	if (!changed)
-		return 0;
-
-	return yt921x_regs_write(priv, reg, vs, num_regs);
-}
-
-static int
-yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
-		       unsigned int num_regs)
-{
-	bool changed = false;
-	u32 vs[4];
-	int res;
-
-	BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
-
-	res = yt921x_regs_read(priv, reg, vs, num_regs);
-	if (res)
-		return res;
-
-	for (unsigned int i = 0; i < num_regs; i++) {
-		u32 u = vs[i];
-
-		u &= ~masks[i];
-		if (u != vs[i])
-			changed = true;
-
-		vs[i] = u;
-	}
-
-	if (!changed)
-		return 0;
-
-	return yt921x_regs_write(priv, reg, vs, num_regs);
-}
-
-static int
-yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
-{
-	return yt921x_regs_write(priv, reg, vals, 2);
-}
-
-static int
-yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
-			 const u32 *vals)
-{
-	return yt921x_regs_update_bits(priv, reg, masks, vals, 2);
-}
-
-static int
-yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)
-{
-	return yt921x_regs_clear_bits(priv, reg, masks, 2);
-}
-
-static int
-yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
-{
-	return yt921x_regs_write(priv, reg, vals, 3);
-}
-
 static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
 {
 	struct yt921x_reg_mdio *mdio = context;
@@ -4786,6 +4580,12 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)
 	if (res)
 		return res;
 
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+	res = yt921x_leds_setup(priv);
+	if (res)
+		dev_warn(dev, "Failed to setup LEDs: %d\n", res);
+#endif
+
 	return 0;
 }
 
@@ -4886,6 +4686,10 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)
 	if (!priv)
 		return;
 
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+	yt921x_leds_remove(priv);
+#endif
+
 	for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
 		struct yt921x_port *pp = &priv->ports[i];
 
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/motorcomm/chip.h
similarity index 99%
rename from drivers/net/dsa/yt921x.h
rename to drivers/net/dsa/motorcomm/chip.h
index 55504652666952..6570ae5902adeb 100644
--- a/drivers/net/dsa/yt921x.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -850,8 +850,13 @@ enum yt921x_fdb_entry_status {
 #define YT921X_ACL_NUM		(YT921X_ACL_BLK_NUM * YT921X_ACL_ENT_PER_BLK)
 #define YT921X_UDF_NUM		8
 
+#define YT921X_LED_GROUP_NUM	3
+
 /* 8 internal + 2 external + 1 mcu */
-#define YT921X_PORT_NUM			11
+#define YT921X_PORT_NUM		11
+#define YT921X_PORT_MCU		10
+
+#define YT921X_NAME	"yt921x"
 
 #define yt921x_port_is_internal(port) ((port) < 8)
 #define yt921x_port_is_external(port) (8 <= (port) && (port) < 9)
@@ -938,6 +943,13 @@ struct yt921x_port {
 	struct yt921x_mib mib;
 	u64 rx_frames;
 	u64 tx_frames;
+
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+	unsigned char led_duty;
+	unsigned short led_cycle;
+
+	struct yt921x_led *leds[YT921X_LED_GROUP_NUM];
+#endif
 };
 
 struct yt921x_reg_ops {
diff --git a/drivers/net/dsa/motorcomm/leds.c b/drivers/net/dsa/motorcomm/leds.c
new file mode 100644
index 00000000000000..d2e68e2f21d67e
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/leds.c
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include <linux/uleds.h>
+
+#include "chip.h"
+#include "leds.h"
+#include "smi.h"
+
+#define to_yt921x_led(led_cdev) \
+	container_of_const((led_cdev), struct yt921x_led, cdev)
+#define to_yt921x_port(led) ((led)->port)
+#define to_yt921x_priv(pp) \
+	container_of_const((pp), struct yt921x_priv, ports[(pp)->index])
+#define to_device(priv) ((priv)->ds.dev)
+
+static u32 yt921x_led_regaddr(struct yt921x_priv *priv, int port, int group)
+{
+	switch (group) {
+	case 0:
+	default:
+		return YT921X_LED0_PORTn(port);
+	case 1:
+		return YT921X_LED1_PORTn(port);
+	case 2:
+		return YT921X_LED2_PORTn(port);
+	}
+}
+
+static int
+yt921x_led_force_get(struct yt921x_priv *priv, int port, int group, bool *onp)
+{
+	u32 val;
+	int res;
+
+	res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val);
+	if (res)
+		return res;
+
+	*onp = (val & YT921X_LED2_PORT_FORCEn_M(group)) ==
+	       YT921X_LED2_PORT_FORCEn_ON(group);
+	return 0;
+}
+
+static int
+yt921x_led_force_set(struct yt921x_priv *priv, int port, int group, bool on)
+{
+	struct yt921x_port *pp = &priv->ports[port];
+	struct yt921x_led *led = pp->leds[group];
+	u32 ctrl;
+	u32 mask;
+
+	led->use_cycle = false;
+	led->use_duty = false;
+
+	mask = YT921X_LED2_PORT_FORCEn_M(group);
+	ctrl = on ? YT921X_LED2_PORT_FORCEn_ON(group) :
+	       YT921X_LED2_PORT_FORCEn_OFF(group);
+	return yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port), mask,
+				      ctrl);
+}
+
+static int
+yt921x_led_blink_select(const struct yt921x_priv *priv, unsigned long on,
+			unsigned long off, unsigned short *cyclep,
+			unsigned char *dutyp)
+{
+	static const unsigned char dutys[] = {
+		YT921X_LED_DUTY(1, 6),
+		YT921X_LED_DUTY(1, 4),
+		YT921X_LED_DUTY(1, 3),
+		YT921X_LED_DUTY(1, 2),
+	};
+	unsigned int cycle_upper;
+	unsigned int cycle_req;
+	unsigned int duty_req;
+	unsigned int cycle;
+	unsigned int duty;
+
+	cycle = YT921X_LED_BLINK_MAX;
+	cycle_upper = cycle * 11585 / 8192 + 1;  /* M_SQRT2 * cycle */
+	if (check_add_overflow(on, off, &cycle_req) || cycle_req >= cycle_upper)
+		return -EOPNOTSUPP;
+
+	for (; cycle > YT921X_LED_BLINK_MIN; cycle_upper >>= 1, cycle >>= 1)
+		if (cycle_req >= cycle_upper >> 1)
+			break;
+	*cyclep = cycle;
+
+	duty_req = DIV_ROUND_CLOSEST(YT921X_LED_DUTY_DENOM *
+				     (on > off ? off : on), cycle_req);
+	for (unsigned int i = ARRAY_SIZE(dutys) - 1;; i--)
+		if (i == 0 || duty_req >= (dutys[i - 1] + dutys[i]) / 2) {
+			duty = dutys[i];
+			break;
+		}
+	if (on > off)
+		duty = YT921X_LED_DUTY_DENOM - duty;
+	*dutyp = duty;
+
+	return 0;
+}
+
+static int
+yt921x_led_blink_set(struct yt921x_priv *priv, int port, int group,
+		     unsigned long *onp, unsigned long *offp)
+{
+	struct yt921x_port *pp = &priv->ports[port];
+	struct yt921x_led *led = pp->leds[group];
+	unsigned short cycle;
+	unsigned char duty;
+	bool use_cycle;
+	u32 ctrl;
+	u32 mask;
+	u32 val;
+	int res;
+
+	if (!*onp && !*offp) {
+		cycle = YT921X_LED_BLINK_DEF;
+		duty = YT921X_LED_DUTY(1, 2);
+		for (unsigned int i = 0; i < YT921X_LED_GROUP_NUM; i++)
+			if (i != group && pp->leds[i] &&
+			    pp->leds[i]->use_duty) {
+				duty = pp->led_duty;
+				break;
+			}
+
+		use_cycle = false;
+	} else {
+		bool change_cycle;
+		bool change_duty;
+
+		res = yt921x_led_blink_select(priv, *onp, *offp, &cycle, &duty);
+		if (res)
+			return res;
+
+		use_cycle = cycle < YT921X_LED_BLINK_DEF;
+		change_cycle = use_cycle && cycle != pp->led_cycle;
+		change_duty = duty != pp->led_duty;
+		if (change_cycle || change_duty)
+			for (unsigned int i = 0; i < YT921X_LED_GROUP_NUM;
+			     i++) {
+				if (i == group || !pp->leds[i])
+					continue;
+				if ((change_cycle && pp->leds[i]->use_cycle) ||
+				    (change_duty && pp->leds[i]->use_duty))
+					return -EOPNOTSUPP;
+			}
+	}
+
+	/* The chip seems to jam a while if changing duty directly */
+	res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val);
+	if (res)
+		return res;
+
+	ctrl = val & ~YT921X_LED2_PORT_FORCEn_M(group);
+	ctrl |= YT921X_LED2_PORT_FORCEn_DONTCARE(group);
+	if (val != ctrl) {
+		res = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), ctrl);
+		if (res)
+			return res;
+	}
+
+	mask = YT921X_LED1_PORT_BLINK_DUTY_M | YT921X_LED1_PORT_BLINK_DUTY_COMP;
+	switch (duty >= YT921X_LED_DUTY(1, 2) ? duty :
+		YT921X_LED_DUTY_DENOM - duty) {
+	default:
+		duty = YT921X_LED_DUTY(1, 2);
+		fallthrough;
+	case YT921X_LED_DUTY(1, 2):
+		ctrl = YT921X_LED1_PORT_BLINK_DUTY_1_2;
+		break;
+	case YT921X_LED_DUTY(2, 3):
+		ctrl = YT921X_LED1_PORT_BLINK_DUTY_2_3;
+		break;
+	case YT921X_LED_DUTY(3, 4):
+		ctrl = YT921X_LED1_PORT_BLINK_DUTY_3_4;
+		break;
+	case YT921X_LED_DUTY(5, 6):
+		ctrl = YT921X_LED1_PORT_BLINK_DUTY_5_6;
+		break;
+	}
+	if (duty < YT921X_LED_DUTY(1, 2))
+		ctrl |= YT921X_LED1_PORT_BLINK_DUTY_COMP;
+	if (use_cycle) {
+		mask |= YT921X_LED1_PORT_OTHER_BLINK_M;
+		ctrl |= YT921X_LED1_PORT_OTHER_BLINK(9 - __fls(cycle));
+	}
+	res = yt921x_reg_update_bits(priv, YT921X_LED1_PORTn(port), mask, ctrl);
+	if (res)
+		return res;
+
+	ctrl = val & ~(YT921X_LED2_PORT_FORCEn_M(group) |
+		       YT921X_LED2_PORT_FORCE_BLINKn_M(group));
+	ctrl |= YT921X_LED2_PORT_FORCEn_BLINK(group);
+	if (use_cycle)
+		ctrl |= YT921X_LED2_PORT_FORCE_BLINKn_OTHER(group);
+	else
+		ctrl |= YT921X_LED2_PORT_FORCE_BLINKn(group, __fls(cycle) - 9);
+	res = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), ctrl);
+	if (res)
+		return res;
+
+	led->use_cycle = use_cycle;
+	if (use_cycle)
+		pp->led_cycle = cycle;
+	led->use_duty = true;
+	pp->led_duty = duty;
+
+	*onp = DIV_ROUND_CLOSEST(duty * cycle, YT921X_LED_DUTY_DENOM);
+	*offp = cycle - *onp;
+	return 0;
+}
+
+static const u32 yt921x_led_trigger_maps[__TRIGGER_NETDEV_MAX] = {
+	[TRIGGER_NETDEV_LINK]		= YT921X_LEDx_PORT_ACT_DUPLEX_HALF |
+					  YT921X_LEDx_PORT_ACT_DUPLEX_FULL,
+	[TRIGGER_NETDEV_LINK_10]	= YT921X_LEDx_PORT_ACT_10M,
+	[TRIGGER_NETDEV_LINK_100]	= YT921X_LEDx_PORT_ACT_100M,
+	[TRIGGER_NETDEV_LINK_1000]	= YT921X_LEDx_PORT_ACT_1000M,
+	[TRIGGER_NETDEV_HALF_DUPLEX]	= YT921X_LEDx_PORT_ACT_DUPLEX_HALF,
+	[TRIGGER_NETDEV_FULL_DUPLEX]	= YT921X_LEDx_PORT_ACT_DUPLEX_FULL,
+	[TRIGGER_NETDEV_TX]		= YT921X_LEDx_PORT_ACT_TX,
+	[TRIGGER_NETDEV_RX]		= YT921X_LEDx_PORT_ACT_RX,
+};
+
+static bool
+yt921x_led_trigger_is_supported(const struct yt921x_priv *priv, int port,
+				int group, unsigned long flags)
+{
+	unsigned int i;
+
+	for_each_set_bit(i, &flags, __TRIGGER_NETDEV_MAX)
+		if (!yt921x_led_trigger_maps[i])
+			return false;
+
+	return true;
+}
+
+static int
+yt921x_led_trigger_get(struct yt921x_priv *priv, int port, int group,
+		       unsigned long *flagsp)
+{
+	u32 addr;
+	u32 val;
+	int res;
+
+	res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val);
+	if (res)
+		return res;
+
+	if ((val & YT921X_LED2_PORT_FORCEn_M(group)) !=
+	    YT921X_LED2_PORT_FORCEn_DONTCARE(group)) {
+		*flagsp = 0;
+		return 0;
+	}
+
+	if (group != 2) {
+		addr = yt921x_led_regaddr(priv, port, group);
+		res = yt921x_reg_read(priv, addr, &val);
+		if (res)
+			return res;
+	}
+
+	*flagsp = 0;
+	for (unsigned int i = 0; i < __TRIGGER_NETDEV_MAX; i++) {
+		u32 mask = yt921x_led_trigger_maps[i];
+
+		if (mask && (val & mask) == mask)
+			*flagsp |= BIT(i);
+	}
+
+	return 0;
+}
+
+static int
+yt921x_led_trigger_set(struct yt921x_priv *priv, int port, int group,
+		       unsigned long flags)
+{
+	struct yt921x_port *pp = &priv->ports[port];
+	struct yt921x_led *led = pp->leds[group];
+	unsigned int i;
+	u32 addr;
+	u32 ctrl;
+	u32 mask;
+	int res;
+
+	ctrl = 0;
+	for_each_set_bit(i, &flags, __TRIGGER_NETDEV_MAX) {
+		if (!yt921x_led_trigger_maps[i])
+			return -EOPNOTSUPP;
+
+		ctrl |= yt921x_led_trigger_maps[i];
+	}
+
+	led->use_cycle = false;
+	led->use_duty = false;
+
+	mask = !group ? YT921X_LED0_PORT_ACT_M : YT921X_LEDx_PORT_ACT_M;
+	if (group == 2) {
+		mask |= YT921X_LED2_PORT_FORCEn_M(group);
+		ctrl |= YT921X_LED2_PORT_FORCEn_DONTCARE(group);
+	}
+	addr = yt921x_led_regaddr(priv, port, group);
+	res = yt921x_reg_update_bits(priv, addr, mask, ctrl);
+	if (res)
+		return res;
+
+	if (group != 2) {
+		mask = YT921X_LED2_PORT_FORCEn_M(group);
+		ctrl = YT921X_LED2_PORT_FORCEn_DONTCARE(group);
+		res = yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port),
+					     mask, ctrl);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static int
+yt921x_cled_brightness_set_blocking(struct led_classdev *led_cdev,
+				    enum led_brightness brightness)
+{
+	struct yt921x_led *led = to_yt921x_led(led_cdev);
+	struct yt921x_port *pp = to_yt921x_port(led);
+	struct yt921x_priv *priv = to_yt921x_priv(pp);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_led_force_set(priv, pp->index, led->group, brightness);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int
+yt921x_cled_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
+		      unsigned long *delay_off)
+{
+	struct yt921x_led *led = to_yt921x_led(led_cdev);
+	struct yt921x_port *pp = to_yt921x_port(led);
+	struct yt921x_priv *priv = to_yt921x_priv(pp);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_led_blink_set(priv, pp->index, led->group, delay_on,
+				   delay_off);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static struct device * __maybe_unused
+yt921x_cled_hw_control_get_device(struct led_classdev *led_cdev)
+{
+	struct yt921x_led *led = to_yt921x_led(led_cdev);
+	struct yt921x_port *pp = to_yt921x_port(led);
+	struct yt921x_priv *priv = to_yt921x_priv(pp);
+	struct dsa_port *dp;
+
+	dp = dsa_to_port(&priv->ds, pp->index);
+	if (!dp || !dp->user)
+		return NULL;
+	return &dp->user->dev;
+}
+
+static int __maybe_unused
+yt921x_cled_hw_control_is_supported(struct led_classdev *led_cdev,
+				    unsigned long flags)
+{
+	struct yt921x_led *led = to_yt921x_led(led_cdev);
+	struct yt921x_port *pp = to_yt921x_port(led);
+	struct yt921x_priv *priv = to_yt921x_priv(pp);
+
+	if (yt921x_led_trigger_is_supported(priv, pp->index, led->group, flags))
+		return 0;
+	return -EOPNOTSUPP;
+}
+
+static int __maybe_unused
+yt921x_cled_hw_control_get(struct led_classdev *led_cdev, unsigned long *flagsp)
+{
+	struct yt921x_led *led = to_yt921x_led(led_cdev);
+	struct yt921x_port *pp = to_yt921x_port(led);
+	struct yt921x_priv *priv = to_yt921x_priv(pp);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_led_trigger_get(priv, pp->index, led->group, flagsp);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int __maybe_unused
+yt921x_cled_hw_control_set(struct led_classdev *led_cdev, unsigned long flags)
+{
+	struct yt921x_led *led = to_yt921x_led(led_cdev);
+	struct yt921x_port *pp = to_yt921x_port(led);
+	struct yt921x_priv *priv = to_yt921x_priv(pp);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_led_trigger_set(priv, pp->index, led->group, flags);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int
+yt921x_led_setup(struct yt921x_priv *priv, int port,
+		 struct fwnode_handle *fwnode, u32 *inv_maskp, u32 *inv_ctrlp)
+{
+	struct yt921x_port *pp = &priv->ports[port];
+	struct device *dev = to_device(priv);
+	struct led_init_data init_data;
+	struct led_classdev *led_cdev;
+	char name[LED_MAX_NAME_SIZE];
+	enum led_default_state state;
+	struct yt921x_led *led;
+	bool force_high;
+	bool force_low;
+	int polarity;
+	u32 group;
+	bool on;
+	int res;
+
+	if (port == YT921X_PORT_MCU) {
+		dev_err(dev, "No LEDs for port %d\n", port);
+		return -ENODEV;
+	}
+
+	res = fwnode_property_read_u32(fwnode, "reg", &group);
+	if (res)
+		return res;
+	if (group >= YT921X_LED_GROUP_NUM) {
+		dev_err(dev, "Invalid LED reg %u for port %d\n", group, port);
+		return -EINVAL;
+	}
+	if (pp->leds[group])
+		return -EEXIST;
+
+	force_high = fwnode_property_read_bool(fwnode, "active-high");
+	force_low = fwnode_property_read_bool(fwnode, "active-low");
+	if (force_high && force_low) {
+		dev_err(dev, "Duplicate polarities for LED %02d:%02u\n",
+			port, group);
+		return -EINVAL;
+	}
+	polarity = force_high ? 1 : force_low ? -1 : 0;
+
+	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+	led->port = pp;
+	led->group = group;
+
+	state = led_init_default_state_get(fwnode);
+
+	mutex_lock(&priv->reg_lock);
+	pp->leds[group] = led;
+	switch (state) {
+	case LEDS_DEFSTATE_OFF:
+	case LEDS_DEFSTATE_ON:
+	default:
+		on = state == LEDS_DEFSTATE_ON;
+		res = yt921x_led_force_set(priv, port, group, on);
+		break;
+	case LEDS_DEFSTATE_KEEP:
+		res = yt921x_led_force_get(priv, port, group, &on);
+		break;
+	}
+	mutex_unlock(&priv->reg_lock);
+	if (res)
+		goto err;
+
+	led_cdev = &led->cdev;
+	led_cdev->brightness = on;
+	led_cdev->max_brightness = 1;
+	led_cdev->flags = LED_RETAIN_AT_SHUTDOWN;
+	led_cdev->brightness_set_blocking = yt921x_cled_brightness_set_blocking;
+	led_cdev->blink_set = yt921x_cled_blink_set;
+#ifdef CONFIG_LEDS_TRIGGERS
+	led_cdev->hw_control_trigger = "netdev";
+	led_cdev->hw_control_get_device = yt921x_cled_hw_control_get_device;
+	led_cdev->hw_control_is_supported = yt921x_cled_hw_control_is_supported;
+	led_cdev->hw_control_get = yt921x_cled_hw_control_get;
+	led_cdev->hw_control_set = yt921x_cled_hw_control_set;
+#endif
+
+	snprintf(name, sizeof(name), YT921X_NAME "-%u:%02d:%02u",
+		 priv->ds.index, port, group);
+	init_data = (typeof(init_data)){
+		.fwnode = fwnode,
+		.devicename = name,
+		.devname_mandatory = true,
+	};
+	res = devm_led_classdev_register_ext(dev, led_cdev, &init_data);
+	if (res)
+		goto err;
+
+	if (polarity) {
+		u32 bit = YT921X_LED_PAR_INV_INVnm(group, port);
+
+		*inv_maskp |= bit;
+		if (polarity > 0)
+			*inv_ctrlp |= bit;
+	}
+	return 0;
+
+err:
+	mutex_lock(&priv->reg_lock);
+	pp->leds[group] = NULL;
+	mutex_unlock(&priv->reg_lock);
+
+	devm_kfree(dev, led);
+	return res;
+}
+
+void yt921x_leds_remove(struct yt921x_priv *priv)
+{
+	struct device *dev = to_device(priv);
+
+	for (int port = 0; port < YT921X_PORT_NUM; port++) {
+		struct yt921x_port *pp = &priv->ports[port];
+
+		for (int group = 0; group < YT921X_LED_GROUP_NUM; group++) {
+			struct yt921x_led *led = pp->leds[group];
+
+			if (led)
+				devm_led_classdev_unregister(dev, &led->cdev);
+		}
+	}
+
+	for (int port = 0; port < YT921X_PORT_NUM; port++) {
+		struct yt921x_port *pp = &priv->ports[port];
+
+		for (int group = 0; group < YT921X_LED_GROUP_NUM; group++) {
+			struct yt921x_led *led = pp->leds[group];
+
+			if (led) {
+				pp->leds[group] = NULL;
+				devm_kfree(dev, led);
+			}
+		}
+	}
+}
+
+int yt921x_leds_setup(struct yt921x_priv *priv)
+{
+	struct dsa_switch *ds = &priv->ds;
+	struct dsa_port *dp;
+	u32 inv_ctrl = 0;
+	u32 inv_mask = 0;
+	u32 ctrl;
+	u32 mask;
+	int res;
+
+	dsa_switch_for_each_port(dp, ds) {
+		struct device_node *leds_np;
+		int port = dp->index;
+
+		if (!dp->dn)
+			continue;
+
+		leds_np = of_get_child_by_name(dp->dn, "leds");
+		if (!leds_np)
+			continue;
+
+		for_each_child_of_node_scoped(leds_np, led_np) {
+			res = yt921x_led_setup(priv, port,
+					       of_fwnode_handle(led_np),
+					       &inv_mask, &inv_ctrl);
+			if (res) {
+				of_node_put(leds_np);
+				goto err;
+			}
+		}
+
+		of_node_put(leds_np);
+	}
+
+	mutex_lock(&priv->reg_lock);
+
+	mask = YT921X_LED_CTRL_MODE_M | YT921X_LED_CTRL_PORT_NUM_M |
+	       YT921X_LED_CTRL_EN;
+	ctrl = YT921X_LED_CTRL_MODE_PARALLEL |
+	       YT921X_LED_CTRL_PORT_NUM(YT921X_PORT_NUM - 1) |
+	       YT921X_LED_CTRL_EN;
+	res = yt921x_reg_update_bits(priv, YT921X_LED_CTRL, mask, ctrl);
+	if (res)
+		goto end;
+
+	/* Inversion is internal - force on will give low logic.
+	 * In the rest of the file, treat LEDs as if active-low.
+	 */
+	if (inv_mask)
+		res = yt921x_reg_update_bits(priv, YT921X_LED_PAR_INV, inv_mask,
+					     inv_ctrl);
+
+end:
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		goto err;
+
+	return 0;
+
+err:
+	yt921x_leds_remove(priv);
+	return res;
+}
diff --git a/drivers/net/dsa/motorcomm/leds.h b/drivers/net/dsa/motorcomm/leds.h
new file mode 100644
index 00000000000000..67373a190c8f4b
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/leds.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_LEDS_H
+#define _YT_LEDS_H
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/kconfig.h>
+#include <linux/leds.h>
+
+#define YT921X_LED_CTRL			0xd0000
+#define  YT921X_LED_CTRL_EN			BIT(21)
+#define  YT921X_LED_CTRL_LOOPDETECT_BLINK_M	GENMASK(20, 19)	/* cycle = 512 * x ms */
+#define   YT921X_LED_CTRL_LOOPDETECT_BLINK(x)		FIELD_PREP(YT921X_LED_CTRL_LOOPDETECT_BLINK_M, (x))
+#define  YT921X_LED_CTRL_PORT_NUM_M		GENMASK(16, 13)
+#define   YT921X_LED_CTRL_PORT_NUM(x)			FIELD_PREP(YT921X_LED_CTRL_PORT_NUM_M, (x))
+#define  YT921X_LED_CTRL_MODE_M			GENMASK(1, 0)
+#define   YT921X_LED_CTRL_MODE(x)			FIELD_PREP(YT921X_LED_CTRL_MODE_M, (x))
+#define   YT921X_LED_CTRL_MODE_PARALLEL			YT921X_LED_CTRL_MODE(0)
+#define   YT921X_LED_CTRL_MODE_SERIAL			YT921X_LED_CTRL_MODE(2)
+#define YT921X_LED0_PORTn(port)		(0xd0004 + 4 * (port))
+#define  YT921X_LED0_PORT_ACT_M			GENMASK(17, 0)
+#define  YT921X_LED0_PORT_ACT_LINK_TRY_DIS	BIT(17)
+#define  YT921X_LED0_PORT_ACT_COLLISION_BLINK_INDI	BIT(16)
+#define YT921X_LED1_PORTn(port)		(0xd0040 + 4 * (port))
+#define  YT921X_LED1_PORT_OTHER_BLINK_M		GENMASK(31, 30)	/* cycle = 512 >> x ms */
+#define   YT921X_LED1_PORT_OTHER_BLINK(x)		FIELD_PREP(YT921X_LED1_PORT_OTHER_BLINK_M, (x))
+#define  YT921X_LED1_PORT_EEE_BLINK_M		GENMASK(29, 28)	/* cycle = 512 >> x ms */
+#define   YT921X_LED1_PORT_EEE_BLINK(x)			FIELD_PREP(YT921X_LED1_PORT_EEE_BLINK_M, (x))
+#define  YT921X_LED1_PORT_BLINK_DUTY_COMP	BIT(27)
+#define  YT921X_LED1_PORT_BLINK_DUTY_M		GENMASK(26, 25)
+#define   YT921X_LED1_PORT_BLINK_DUTY(x)		FIELD_PREP(YT921X_LED1_PORT_BLINK_DUTY_M, (x))
+#define   YT921X_LED1_PORT_BLINK_DUTY_1_2		YT921X_LED1_PORT_BLINK_DUTY(0)
+#define   YT921X_LED1_PORT_BLINK_DUTY_2_3		YT921X_LED1_PORT_BLINK_DUTY(1)
+#define   YT921X_LED1_PORT_BLINK_DUTY_3_4		YT921X_LED1_PORT_BLINK_DUTY(2)
+#define   YT921X_LED1_PORT_BLINK_DUTY_5_6		YT921X_LED1_PORT_BLINK_DUTY(3)
+#define YT921X_LED2_PORTn(port)		(0xd0080 + 4 * (port))
+#define  YT921X_LED2_PORT_FORCEn_M(grp)		GENMASK(4 * (grp) + 19, 4 * (grp) + 18)
+#define   YT921X_LED2_PORT_FORCEn(grp, x)		((x) << (4 * (grp) + 18))
+#define   YT921X_LED2_PORT_FORCEn_DONTCARE(grp)		YT921X_LED2_PORT_FORCEn(grp, 0)
+#define   YT921X_LED2_PORT_FORCEn_BLINK(grp)		YT921X_LED2_PORT_FORCEn(grp, 1)
+#define   YT921X_LED2_PORT_FORCEn_ON(grp)		YT921X_LED2_PORT_FORCEn(grp, 2)
+#define   YT921X_LED2_PORT_FORCEn_OFF(grp)		YT921X_LED2_PORT_FORCEn(grp, 3)
+#define  YT921X_LED2_PORT_FORCE_BLINKn_M(grp)	GENMASK(4 * (grp) + 17, 4 * (grp) + 16)	/* cycle = 512 << x ms */
+#define   YT921X_LED2_PORT_FORCE_BLINKn(grp, x)		((x) << (4 * (grp) + 16))
+#define   YT921X_LED2_PORT_FORCE_BLINKn_OTHER(grp)	YT921X_LED2_PORT_FORCE_BLINKn(grp, 3)
+#define  YT921X_LEDx_PORT_ACT_M			GENMASK(15, 0)
+#define  YT921X_LEDx_PORT_ACT_EEE_BLINK		BIT(15)
+#define  YT921X_LEDx_PORT_ACT_LOOPDETECT_BLINK	BIT(14)
+#define  YT921X_LEDx_PORT_ACT_ACTIVE_BLINK	BIT(13)
+#define  YT921X_LEDx_PORT_ACT_DUPLEX_FULL	BIT(12)
+#define  YT921X_LEDx_PORT_ACT_DUPLEX_HALF	BIT(11)
+#define  YT921X_LEDx_PORT_ACT_TX_BLINK		BIT(10)
+#define  YT921X_LEDx_PORT_ACT_RX_BLINK		BIT(9)
+#define  YT921X_LEDx_PORT_ACT_TX		BIT(8)
+#define  YT921X_LEDx_PORT_ACT_RX		BIT(7)
+#define  YT921X_LEDx_PORT_ACT_1000M		BIT(6)
+#define  YT921X_LEDx_PORT_ACT_100M		BIT(5)
+#define  YT921X_LEDx_PORT_ACT_10M		BIT(4)
+#define  YT921X_LEDx_PORT_ACT_COLLISION_BLINK	BIT(3)
+#define  YT921X_LEDx_PORT_ACT_1000M_BLINK	BIT(2)
+#define  YT921X_LEDx_PORT_ACT_100M_BLINK	BIT(1)
+#define  YT921X_LEDx_PORT_ACT_10M_BLINK		BIT(0)
+#define YT921X_LED_SER_CTRL		0xd0100
+#define  YT921X_LED_SER_CTRL_UNK		GENMASK(25, 24)	/* delay? */
+#define  YT921X_LED_SER_CTRL_ACTIVE_LOW		BIT(4)
+#define  YT921X_LED_SER_CTRL_GRP_NUM_M		GENMASK(1, 0)	/* #grp - 1 */
+#define   YT921X_LED_SER_CTRL_GRP_NUM(x)		FIELD_PREP(YT921X_LED_SER_CTRL_GRP_NUM_M, (x))
+#define YT921X_LED_SER_MAPnm(grp, port)	(0xd0104 + 8 * (2 - (grp)) + 4 * ((port) / 5))
+#define  YT921X_LED_SER_MAP_DSTn_PORT_M(port)	GENMASK(6 * ((port) % 5) + 5, 6 * ((port) % 5) + 2)
+#define   YT921X_LED_SER_MAP_DSTn_PORT(port, x)		((x) << (6 * ((port) % 5) + 2))
+#define  YT921X_LED_SER_MAP_DSTn_LED_M(port)	GENMASK(6 * ((port) % 5) + 1, 6 * ((port) % 5))
+#define   YT921X_LED_SER_MAP_DSTn_LED(port, x)		((x) << (6 * ((port) % 5)))
+#define YT921X_LED_PAR_PORTS		0xd01c4
+#define YT921X_LED_PAR_INV		0xd01c8
+#define  YT921X_LED_PAR_INV_INVnm(grp, port)	BIT(10 * (grp) + (port))
+#define YT921X_LED_PAR_MAPn(port)	(0xd01d0 + 4 * (port))
+#define  YT921X_LED_PAR_MAP_DSTn_PORT_M(grp)	GENMASK(6 * (grp) + 5, 6 * (grp) + 2)
+#define   YT921X_LED_PAR_MAP_DSTn_PORT(grp, x)		((x) << (6 * (grp) + 2))
+#define  YT921X_LED_PAR_MAP_DSTn_LED_M(grp)	GENMASK(6 * (grp) + 1, 6 * (grp))
+#define   YT921X_LED_PAR_MAP_DSTn_LED(grp, x)		((x) << (6 * (grp)))
+
+#define YT921X_LED_BLINK_MIN	64
+#define YT921X_LED_BLINK_DEF	512
+#define YT921X_LED_BLINK_MAX	2048
+
+/* 2 * lcm(2, 3, 4, 6) */
+#define YT921X_LED_DUTY_DENOM		24
+#define YT921X_LED_DUTY(nom, denom)	(YT921X_LED_DUTY_DENOM * (nom) / (denom))
+
+struct yt921x_priv;
+
+struct yt921x_led {
+	struct led_classdev cdev;
+	struct yt921x_port *port;
+	unsigned char group;
+
+	bool use_cycle:1;
+	bool use_duty:1;
+};
+
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+
+void yt921x_leds_remove(struct yt921x_priv *priv);
+int yt921x_leds_setup(struct yt921x_priv *priv);
+
+#else
+
+static inline void yt921x_leds_remove(struct yt921x_priv *priv) {}
+
+static inline int yt921x_leds_setup(struct yt921x_priv *priv)
+{
+	return 0;
+}
+
+#endif
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c
new file mode 100644
index 00000000000000..9054896e4cd12b
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/smi.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include <linux/iopoll.h>
+
+#include "chip.h"
+#include "smi.h"
+
+#define YT921X_POLL_SLEEP_US	10000
+#define YT921X_POLL_TIMEOUT_US	100000
+
+int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
+{
+	lockdep_assert_held_once(&priv->reg_lock);
+
+	return priv->reg_ops->read(priv->reg_ctx, reg, valp);
+}
+
+int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
+{
+	lockdep_assert_held_once(&priv->reg_lock);
+
+	return priv->reg_ops->write(priv->reg_ctx, reg, val);
+}
+
+int yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
+{
+	u32 val;
+	int res;
+	int ret;
+
+	ret = read_poll_timeout(yt921x_reg_read, res,
+				res || (val & mask) == *valp,
+				YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
+				false, priv, reg, &val);
+	if (ret)
+		return ret;
+	if (res)
+		return res;
+
+	*valp = val;
+	return 0;
+}
+
+int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
+{
+	int res;
+	u32 v;
+	u32 u;
+
+	res = yt921x_reg_read(priv, reg, &v);
+	if (res)
+		return res;
+
+	u = v;
+	u &= ~mask;
+	u |= val;
+	if (u == v)
+		return 0;
+
+	return yt921x_reg_write(priv, reg, u);
+}
+
+int
+yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
+		 unsigned int num_regs)
+{
+	int res;
+
+	for (unsigned int i = 0; i < num_regs; i++) {
+		res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+int
+yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
+		  unsigned int num_regs)
+{
+	int res;
+
+	for (unsigned int i = 0; i < num_regs; i++) {
+		res = yt921x_reg_write(priv, reg + 4 * i, vals[i]);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+int
+yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+			const u32 *vals, unsigned int num_regs)
+{
+	bool changed = false;
+	u32 vs[4];
+	int res;
+
+	if (WARN_ON_ONCE(num_regs > ARRAY_SIZE(vs)))
+		return -EINVAL;
+
+	res = yt921x_regs_read(priv, reg, vs, num_regs);
+	if (res)
+		return res;
+
+	for (unsigned int i = 0; i < num_regs; i++) {
+		u32 u = vs[i];
+
+		u &= ~masks[i];
+		u |= vals[i];
+		if (u != vs[i])
+			changed = true;
+
+		vs[i] = u;
+	}
+
+	if (!changed)
+		return 0;
+
+	return yt921x_regs_write(priv, reg, vs, num_regs);
+}
+
+int
+yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+		       unsigned int num_regs)
+{
+	bool changed = false;
+	u32 vs[4];
+	int res;
+
+	if (WARN_ON_ONCE(num_regs > ARRAY_SIZE(vs)))
+		return -EINVAL;
+
+	res = yt921x_regs_read(priv, reg, vs, num_regs);
+	if (res)
+		return res;
+
+	for (unsigned int i = 0; i < num_regs; i++) {
+		u32 u = vs[i];
+
+		u &= ~masks[i];
+		if (u != vs[i])
+			changed = true;
+
+		vs[i] = u;
+	}
+
+	if (!changed)
+		return 0;
+
+	return yt921x_regs_write(priv, reg, vs, num_regs);
+}
diff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h
new file mode 100644
index 00000000000000..2e956065eb90ee
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/smi.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_SMI_H
+#define _YT_SMI_H
+
+#include <linux/types.h>
+#include <linux/wordpart.h>
+
+struct yt921x_priv;
+
+int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp);
+int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val);
+int yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp);
+int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask,
+			   u32 val);
+
+static inline int
+yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
+{
+	return yt921x_reg_update_bits(priv, reg, 0, mask);
+}
+
+static inline int
+yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
+{
+	return yt921x_reg_update_bits(priv, reg, mask, 0);
+}
+
+static inline int
+yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
+{
+	return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
+}
+
+/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
+ * long register. More specifically, writes to parts of its words won't become
+ * visible, until the last word is written.
+ *
+ * Here we require full read and write operations over these registers to
+ * eliminate potential issues, although partial reads/writes are also possible.
+ */
+
+static inline void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)
+{
+	*lo &= ~lower_32_bits(mask);
+	*hi &= ~upper_32_bits(mask);
+	*lo |= lower_32_bits(val);
+	*hi |= upper_32_bits(val);
+}
+
+int yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
+		     unsigned int num_regs);
+int yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
+		      unsigned int num_regs);
+int yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+			    const u32 *vals, unsigned int num_regs);
+int yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+			   unsigned int num_regs);
+
+static inline int
+yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
+{
+	return yt921x_regs_write(priv, reg, vals, 2);
+}
+
+static inline int
+yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
+			 const u32 *vals)
+{
+	return yt921x_regs_update_bits(priv, reg, masks, vals, 2);
+}
+
+static inline int
+yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)
+{
+	return yt921x_regs_clear_bits(priv, reg, masks, 2);
+}
+
+static inline int
+yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
+{
+	return yt921x_regs_write(priv, reg, vals, 3);
+}
+
+#endif

3/2 2026/07/07 14:42 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20392
							output: 21
							thoughts: 545
						

Thoughts:
**Analyzing the Motorcomm YT9215 LED Support Patch**

Okay, so I'm looking at this patch that adds LED support for the Motorcomm YT9215 ethernet switch chip. It's moving the existing driver code, `yt921x.c`, and splitting it into `chip.c`, `leds.c`, and `smi.c` within a `motorcomm` subdirectory under `drivers/net/dsa/`. A new config option, `NET_DSA_YT921X_LEDS`, is being introduced. My immediate thought is, can this even be tested in a virtualized environment? This is a hardware driver for a specific Ethernet switch chip, and those are usually very hardware-dependent, especially DSA switches.

I know QEMU doesn't typically have hardware drivers unless they're emulated. So, is there any emulation for the YT9215 in QEMU? Or is this purely a hardware thing? DSA drivers, of course, are usually for embedded switches, so I need to think about how these things are instantiated. Can they be instantiated through the device tree, or maybe through I2C or MDIO in a virtual environment? Wait, syzkaller *can* instantiate I2C/MDIO devices, if it has the right descriptions, or even through dummy devices. If it's a hardware driver though, reaching it is going to come down to mocking the MDIO bus. Hang on, syzkaller *does* have some MDIO/I2C fuzzing support. I should check if `NET_DSA_YT921X` is something we fuzz.

This patch is modifying `leds.c`, adding functions like `yt921x_led_setup`, `yt921x_led_force_set`, `yt921x_led_blink_set`, and `yt921x_led_trigger_set`. These are LED classdev operations. Can these be reached? Yes, definitely via sysfs! But the catch is, the device needs to be probed first. Probing a DSA switch… that’s where things get tricky. We'll need an MDIO bus and a device tree or something similar setup. Does syzkaller support DSA switch probing?

Typically, hardware-specific drivers, especially those that need to read ID registers, will fail to probe unless the actual hardware is present or emulated. I need to check if `yt921x_mdio_read` or anything similar reads a hardware ID. Let's do a kernel search for `yt921x` to see exactly how it probes. I need to understand the probe path to see if it's possible to even get this thing off the ground in a virtual environment.



4/2 2026/07/07 14:42 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "yt921x_probe"
}

Results:
{
  "error": "no matches"
}

5/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21565
							output: 21
							thoughts: 20
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Investigating a Potential MDIO Probe Issue**

Alright, my first instinct here is to zero in on a potential problem related to an MDIO interface probe. I'm thinking the function `yt921x_mdio_probe` seems like a likely candidate, or perhaps something with a similar naming convention related to that particular chipset. Given the context, I'm probably looking at some kind of initialization or device discovery issue within the MDIO framework. My experience tells me this is often where things go sideways – timing problems, incorrect register accesses, or potentially a driver-specific bug in the probe routine itself. Time to start digging through the relevant code and logs, and hopefully find the root cause, and begin testing for errors.



6/2 2026/07/07 14:43 tool grepper
0m Args:
{
  "Expression": "probe.*yt921x"
}

Results:
{
  "Output": "drivers/net/dsa/motorcomm/chip.c=4772=static struct mdio_driver yt921x_mdio_driver = {\ndrivers/net/dsa/motorcomm/chip.c:4773:\t.probe = yt921x_mdio_probe,\ndrivers/net/dsa/motorcomm/chip.c-4774-\t.remove = yt921x_mdio_remove,\n"
}

7/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21896
							output: 43
							thoughts: 19
						

Thoughts:
Okay, let's dive into this. Here's my take on `yt921x_mdio_probe`:

**Analyzing `yt921x_mdio_probe`**

Alright, I'm digging into `yt921x_mdio_probe`. My primary concern is what this function is designed to *do*. What's its purpose? Given the name, it's pretty clear we're dealing with an MDIO interface, and specifically, one associated with a YT921x device. "Probe" is a common term, so I'm assuming it's part of the device driver's initialization. I want to understand how it discovers and sets up the MDIO interface associated with the YT921x chipset. Specifically, I'm interested in how it interacts with the underlying hardware, how it configures the MDIO bus, and what specific resources it's likely to allocate or initialize. I'm looking for clues about the driver's overall strategy for managing this MDIO component. Are there specific register reads, writes, or delays involved? Are there any potential error conditions that are addressed? And what's the broader context? What other devices will it interact with? I'll probably start by looking for any hints about a platform device or resource allocation, which would suggest a dependency on the device tree or similar configuration. Let's get to work.



8/2 2026/07/07 14:43 tool read-file
0m Args:
{
  "File": "drivers/net/dsa/motorcomm/chip.c",
  "FirstLine": 4600,
  "LineCount": 100
}

Results:
{
  "Contents": "4600:\t\t.get_strings\t\t= yt921x_dsa_get_strings,\n4601:\t\t.get_ethtool_stats\t= yt921x_dsa_get_ethtool_stats,\n4602:\t\t.get_sset_count\t\t= yt921x_dsa_get_sset_count,\n4603:\t\t.get_eth_mac_stats\t= yt921x_dsa_get_eth_mac_stats,\n4604:\t\t.get_eth_ctrl_stats\t= yt921x_dsa_get_eth_ctrl_stats,\n4605:\t\t.get_rmon_stats\t\t= yt921x_dsa_get_rmon_stats,\n4606:\t\t.get_stats64\t\t= yt921x_dsa_get_stats64,\n4607:\t\t.get_pause_stats\t= yt921x_dsa_get_pause_stats,\n4608:\t\t/* eee */\n4609:\t\t.support_eee\t\t= dsa_supports_eee,\n4610:\t\t.set_mac_eee\t\t= yt921x_dsa_set_mac_eee,\n4611:\t\t/* mtu */\n4612:\t\t.port_change_mtu\t= yt921x_dsa_port_change_mtu,\n4613:\t\t.port_max_mtu\t\t= yt921x_dsa_port_max_mtu,\n4614:\t\t/* rate */\n4615:\t\t.port_policer_del\t= yt921x_dsa_port_policer_del,\n4616:\t\t.port_policer_add\t= yt921x_dsa_port_policer_add,\n4617:\t\t.port_setup_tc\t\t= yt921x_dsa_port_setup_tc,\n4618:\t\t/* acl */\n4619:\t\t.cls_flower_del\t\t= yt921x_dsa_cls_flower_del,\n4620:\t\t.cls_flower_add\t\t= yt921x_dsa_cls_flower_add,\n4621:\t\t/* hsr */\n4622:\t\t.port_hsr_leave\t\t= dsa_port_simple_hsr_leave,\n4623:\t\t.port_hsr_join\t\t= dsa_port_simple_hsr_join,\n4624:\t\t/* mirror */\n4625:\t\t.port_mirror_del\t= yt921x_dsa_port_mirror_del,\n4626:\t\t.port_mirror_add\t= yt921x_dsa_port_mirror_add,\n4627:\t\t/* lag */\n4628:\t\t.port_lag_leave\t\t= yt921x_dsa_port_lag_leave,\n4629:\t\t.port_lag_join\t\t= yt921x_dsa_port_lag_join,\n4630:\t\t/* fdb */\n4631:\t\t.port_fdb_dump\t\t= yt921x_dsa_port_fdb_dump,\n4632:\t\t.port_fast_age\t\t= yt921x_dsa_port_fast_age,\n4633:\t\t.set_ageing_time\t= yt921x_dsa_set_ageing_time,\n4634:\t\t.port_fdb_del\t\t= yt921x_dsa_port_fdb_del,\n4635:\t\t.port_fdb_add\t\t= yt921x_dsa_port_fdb_add,\n4636:\t\t.port_mdb_del\t\t= yt921x_dsa_port_mdb_del,\n4637:\t\t.port_mdb_add\t\t= yt921x_dsa_port_mdb_add,\n4638:\t\t/* vlan */\n4639:\t\t.port_vlan_filtering\t= yt921x_dsa_port_vlan_filtering,\n4640:\t\t.port_vlan_del\t\t= yt921x_dsa_port_vlan_del,\n4641:\t\t.port_vlan_add\t\t= yt921x_dsa_port_vlan_add,\n4642:\t\t/* bridge */\n4643:\t\t.port_pre_bridge_flags\t= yt921x_dsa_port_pre_bridge_flags,\n4644:\t\t.port_bridge_flags\t= yt921x_dsa_port_bridge_flags,\n4645:\t\t.port_bridge_leave\t= yt921x_dsa_port_bridge_leave,\n4646:\t\t.port_bridge_join\t= yt921x_dsa_port_bridge_join,\n4647:\t\t/* mst */\n4648:\t\t.port_mst_state_set\t= yt921x_dsa_port_mst_state_set,\n4649:\t\t.vlan_msti_set\t\t= yt921x_dsa_vlan_msti_set,\n4650:\t\t.port_stp_state_set\t= yt921x_dsa_port_stp_state_set,\n4651:\t#if IS_ENABLED(CONFIG_DCB)\n4652:\t\t/* dcb */\n4653:\t\t.port_get_default_prio\t= yt921x_dsa_port_get_default_prio,\n4654:\t\t.port_set_default_prio\t= yt921x_dsa_port_set_default_prio,\n4655:\t\t.port_get_apptrust\t= yt921x_dsa_port_get_apptrust,\n4656:\t\t.port_set_apptrust\t= yt921x_dsa_port_set_apptrust,\n4657:\t#endif\n4658:\t\t/* port */\n4659:\t\t.get_tag_protocol\t= yt921x_dsa_get_tag_protocol,\n4660:\t\t.phylink_get_caps\t= yt921x_dsa_phylink_get_caps,\n4661:\t\t.port_setup\t\t= yt921x_dsa_port_setup,\n4662:\t#if IS_ENABLED(CONFIG_DCB)\n4663:\t\t/* dscp */\n4664:\t\t.port_get_dscp_prio\t= yt921x_dsa_port_get_dscp_prio,\n4665:\t\t.port_del_dscp_prio\t= yt921x_dsa_port_del_dscp_prio,\n4666:\t\t.port_add_dscp_prio\t= yt921x_dsa_port_add_dscp_prio,\n4667:\t#endif\n4668:\t\t/* chip */\n4669:\t\t.setup\t\t\t= yt921x_dsa_setup,\n4670:\t};\n4671:\t\n4672:\tstatic void yt921x_mdio_shutdown(struct mdio_device *mdiodev)\n4673:\t{\n4674:\t\tstruct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);\n4675:\t\n4676:\t\tif (!priv)\n4677:\t\t\treturn;\n4678:\t\n4679:\t\tdsa_switch_shutdown(\u0026priv-\u003eds);\n4680:\t}\n4681:\t\n4682:\tstatic void yt921x_mdio_remove(struct mdio_device *mdiodev)\n4683:\t{\n4684:\t\tstruct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);\n4685:\t\n4686:\t\tif (!priv)\n4687:\t\t\treturn;\n4688:\t\n4689:\t#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n4690:\t\tyt921x_leds_remove(priv);\n4691:\t#endif\n4692:\t\n4693:\t\tfor (size_t i = ARRAY_SIZE(priv-\u003eports); i-- \u003e 0; ) {\n4694:\t\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n4695:\t\n4696:\t\t\tdisable_delayed_work_sync(\u0026pp-\u003emib_read);\n4697:\t\t}\n4698:\t\n4699:\t\tdsa_unregister_switch(\u0026priv-\u003eds);\n"
}

9/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24508
							output: 43
							thoughts: 16
						

Thoughts:
Okay, let's dive into `yt921x_mdio_probe`. I'm going to approach this with the assumption that I understand the general principles of MDIO and driver probing, so I'll be looking for specifics related to the YT921x chipset and anything unusual or noteworthy in its implementation. I'll be paying close attention to:

**Initial Assessment: yt921x_mdio_probe**

*   **Entry Point and Purpose:** First, I need to confirm this is indeed the probe function for the MDIO interface. I expect to see it as part of the driver's registration. I need to understand what triggered the probe; likely, the system detected a device matching a known ID, and now this function is being called.
*   **Resource Allocation:** I'll be looking at what resources are allocated, especially memory for data structures. This includes any device-specific information needed for the MDIO communication or PHY management. Are there any unusual memory mapping strategies being used? Are there potential resource conflicts to look out for?
*   **MDIO Initialization:** The critical part. How is the MDIO interface initialized? Are there specific sequences, register writes, or clock configurations necessary for the YT921x? I'm expecting standard MDIO initialization steps, potentially with a vendor-specific twist. I'll need to understand how the driver interacts with the underlying hardware MDIO controller.
*   **PHY Detection and Configuration:** Is there PHY detection involved here, or is it assumed a specific PHY is connected? How does it handle various PHY types? I'll need to figure out how it sets up and configures the PHY, including any power-up or reset sequences. What about the MDIO bus speed, are there any speed adjustments?
*   **Error Handling and Robustness:** How robust is the error handling? What does it do if the device isn't responding, if there are MDIO errors, or if the PHY configuration fails? Are there retry mechanisms? Any potential race conditions? I'll look for `printk` calls and other debugging statements to understand the error reporting.
*   **Driver Model Integration:** How does this function integrate into the broader kernel driver model? Are there bindings to other parts of the system, like network interfaces or the device tree? I'll check for the use of the device structure, device private data, and of course any network initialization and registration.
*   **Power Management:** How does it handle power management? Are there any power-up or power-down sequences or callbacks defined?
*   **Concurrency:** Are there any concurrency issues I need to be concerned with? Are there any locks being used to protect shared data or hardware registers?

Essentially, I want to understand how this driver brings up the MDIO interface and PHY. This includes how it manages hardware resources, detects and configures the PHY, and handles potential errors in the process. The core of my interest is how this driver brings up and configures the YT921x MDIO interface. I will carefully review the code for any subtle areas, that can lead to subtle bugs.



10/2 2026/07/07 14:43 tool read-file
0m Args:
{
  "File": "drivers/net/dsa/motorcomm/chip.c",
  "FirstLine": 4700,
  "LineCount": 100
}

Results:
{
  "Contents": "4700:\t\n4701:\t\tfor (unsigned int i = 0; i \u003c ARRAY_SIZE(priv-\u003eacl_blks); i++) {\n4702:\t\t\tstruct yt921x_acl_blk *aclblk = priv-\u003eacl_blks[i];\n4703:\t\n4704:\t\t\tif (!aclblk)\n4705:\t\t\t\tcontinue;\n4706:\t\t\tfor (unsigned int j = 0; j \u003c ARRAY_SIZE(aclblk-\u003erules); j++) {\n4707:\t\t\t\tstruct yt921x_acl_rule *aclrule = aclblk-\u003erules[j];\n4708:\t\n4709:\t\t\t\tif (!aclrule)\n4710:\t\t\t\t\tcontinue;\n4711:\t\t\t\tkvfree(aclrule);\n4712:\t\t\t}\n4713:\t\t\tkvfree(aclblk);\n4714:\t\t}\n4715:\t\tmutex_destroy(\u0026priv-\u003ereg_lock);\n4716:\t}\n4717:\t\n4718:\tstatic int yt921x_mdio_probe(struct mdio_device *mdiodev)\n4719:\t{\n4720:\t\tstruct device *dev = \u0026mdiodev-\u003edev;\n4721:\t\tstruct yt921x_reg_mdio *mdio;\n4722:\t\tstruct yt921x_priv *priv;\n4723:\t\tstruct dsa_switch *ds;\n4724:\t\n4725:\t\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n4726:\t\tif (!priv)\n4727:\t\t\treturn -ENOMEM;\n4728:\t\n4729:\t\tmdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);\n4730:\t\tif (!mdio)\n4731:\t\t\treturn -ENOMEM;\n4732:\t\n4733:\t\tmdio-\u003ebus = mdiodev-\u003ebus;\n4734:\t\tmdio-\u003eaddr = mdiodev-\u003eaddr;\n4735:\t\tmdio-\u003eswitchid = 0;\n4736:\t\n4737:\t\tmutex_init(\u0026priv-\u003ereg_lock);\n4738:\t\n4739:\t\tpriv-\u003ereg_ops = \u0026yt921x_reg_ops_mdio;\n4740:\t\tpriv-\u003ereg_ctx = mdio;\n4741:\t\n4742:\t\tfor (size_t i = 0; i \u003c ARRAY_SIZE(priv-\u003eports); i++) {\n4743:\t\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n4744:\t\n4745:\t\t\tpp-\u003eindex = i;\n4746:\t\t\tINIT_DELAYED_WORK(\u0026pp-\u003emib_read, yt921x_poll_mib);\n4747:\t\t}\n4748:\t\n4749:\t\tds = \u0026priv-\u003eds;\n4750:\t\tds-\u003edev = dev;\n4751:\t\tds-\u003eassisted_learning_on_cpu_port = true;\n4752:\t\tds-\u003edscp_prio_mapping_is_global = true;\n4753:\t\tds-\u003epriv = priv;\n4754:\t\tds-\u003eops = \u0026yt921x_dsa_switch_ops;\n4755:\t\tds-\u003eageing_time_min = 1 * 5000;\n4756:\t\tds-\u003eageing_time_max = U16_MAX * 5000;\n4757:\t\tds-\u003ephylink_mac_ops = \u0026yt921x_phylink_mac_ops;\n4758:\t\tds-\u003enum_lag_ids = YT921X_LAG_NUM;\n4759:\t\tds-\u003enum_ports = YT921X_PORT_NUM;\n4760:\t\n4761:\t\tmdiodev_set_drvdata(mdiodev, priv);\n4762:\t\n4763:\t\treturn dsa_register_switch(ds);\n4764:\t}\n4765:\t\n4766:\tstatic const struct of_device_id yt921x_of_match[] = {\n4767:\t\t{ .compatible = \"motorcomm,yt9215\" },\n4768:\t\t{}\n4769:\t};\n4770:\tMODULE_DEVICE_TABLE(of, yt921x_of_match);\n4771:\t\n4772:\tstatic struct mdio_driver yt921x_mdio_driver = {\n4773:\t\t.probe = yt921x_mdio_probe,\n4774:\t\t.remove = yt921x_mdio_remove,\n4775:\t\t.shutdown = yt921x_mdio_shutdown,\n4776:\t\t.mdiodrv.driver = {\n4777:\t\t\t.name = YT921X_NAME,\n4778:\t\t\t.of_match_table = yt921x_of_match,\n4779:\t\t},\n4780:\t};\n4781:\t\n4782:\tmdio_module_driver(yt921x_mdio_driver);\n4783:\t\n4784:\tMODULE_AUTHOR(\"David Yang \u003cmmyangfl@gmail.com\u003e\");\n4785:\tMODULE_DESCRIPTION(\"Driver for Motorcomm YT921x Switch\");\n4786:\tMODULE_LICENSE(\"GPL\");\n"
}

11/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26649
							output: 43
							thoughts: 78
						

Thoughts:
Here's my summary, as I delve into this code:

**Analyzing the yt921x_mdio_probe Function**

Okay, so I'm digging into `yt921x_mdio_probe`. Hmm, the code doesn't seem to be touching any hardware ID registers, which is a bit unusual for a probe function. It's skipping straight to registering the DSA switch. That's a key observation.

Now, hold on... `dsa_register_switch` is the call that actually sets things in motion. That, in turn, is going to trigger the `setup` callback, which I see is `yt921x_dsa_setup`. Right. That makes more sense. The probe likely just handles the initial registration, and the real hardware configuration is happening *after* that. So, let's turn our attention to `yt921x_dsa_setup`. That's where the meat of the initialization will be. I need to understand what's happening there to get the full picture.



12/2 2026/07/07 14:43 tool read-file
0m Args:
{
  "File": "drivers/net/dsa/motorcomm/chip.c",
  "FirstLine": 4540,
  "LineCount": 100
}

Results:
{
  "Contents": "4540:\t\tstruct device *dev = to_device(priv);\n4541:\t\tstruct device_node *np = dev-\u003eof_node;\n4542:\t\tstruct device_node *child;\n4543:\t\tint res;\n4544:\t\n4545:\t\tmutex_lock(\u0026priv-\u003ereg_lock);\n4546:\t\tres = yt921x_chip_reset(priv);\n4547:\t\tmutex_unlock(\u0026priv-\u003ereg_lock);\n4548:\t\n4549:\t\tif (res)\n4550:\t\t\treturn res;\n4551:\t\n4552:\t\t/* Register the internal mdio bus. Nodes for internal ports should have\n4553:\t\t * proper phy-handle pointing to their PHYs. Not enabling the internal\n4554:\t\t * bus is possible, though pretty wired, if internal ports are not used.\n4555:\t\t */\n4556:\t\tchild = of_get_child_by_name(np, \"mdio\");\n4557:\t\tif (child) {\n4558:\t\t\tres = yt921x_mbus_int_init(priv, child);\n4559:\t\t\tof_node_put(child);\n4560:\t\t\tif (res)\n4561:\t\t\t\treturn res;\n4562:\t\t}\n4563:\t\n4564:\t\t/* External mdio bus is optional */\n4565:\t\tchild = of_get_child_by_name(np, \"mdio-external\");\n4566:\t\tif (child) {\n4567:\t\t\tres = yt921x_mbus_ext_init(priv, child);\n4568:\t\t\tof_node_put(child);\n4569:\t\t\tif (res)\n4570:\t\t\t\treturn res;\n4571:\t\n4572:\t\t\tdev_err(dev, \"Untested external mdio bus\\n\");\n4573:\t\t\treturn -ENODEV;\n4574:\t\t}\n4575:\t\n4576:\t\tmutex_lock(\u0026priv-\u003ereg_lock);\n4577:\t\tres = yt921x_chip_setup(priv);\n4578:\t\tmutex_unlock(\u0026priv-\u003ereg_lock);\n4579:\t\n4580:\t\tif (res)\n4581:\t\t\treturn res;\n4582:\t\n4583:\t#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n4584:\t\tres = yt921x_leds_setup(priv);\n4585:\t\tif (res)\n4586:\t\t\tdev_warn(dev, \"Failed to setup LEDs: %d\\n\", res);\n4587:\t#endif\n4588:\t\n4589:\t\treturn 0;\n4590:\t}\n4591:\t\n4592:\tstatic const struct phylink_mac_ops yt921x_phylink_mac_ops = {\n4593:\t\t.mac_link_down\t= yt921x_phylink_mac_link_down,\n4594:\t\t.mac_link_up\t= yt921x_phylink_mac_link_up,\n4595:\t\t.mac_config\t= yt921x_phylink_mac_config,\n4596:\t};\n4597:\t\n4598:\tstatic const struct dsa_switch_ops yt921x_dsa_switch_ops = {\n4599:\t\t/* mib */\n4600:\t\t.get_strings\t\t= yt921x_dsa_get_strings,\n4601:\t\t.get_ethtool_stats\t= yt921x_dsa_get_ethtool_stats,\n4602:\t\t.get_sset_count\t\t= yt921x_dsa_get_sset_count,\n4603:\t\t.get_eth_mac_stats\t= yt921x_dsa_get_eth_mac_stats,\n4604:\t\t.get_eth_ctrl_stats\t= yt921x_dsa_get_eth_ctrl_stats,\n4605:\t\t.get_rmon_stats\t\t= yt921x_dsa_get_rmon_stats,\n4606:\t\t.get_stats64\t\t= yt921x_dsa_get_stats64,\n4607:\t\t.get_pause_stats\t= yt921x_dsa_get_pause_stats,\n4608:\t\t/* eee */\n4609:\t\t.support_eee\t\t= dsa_supports_eee,\n4610:\t\t.set_mac_eee\t\t= yt921x_dsa_set_mac_eee,\n4611:\t\t/* mtu */\n4612:\t\t.port_change_mtu\t= yt921x_dsa_port_change_mtu,\n4613:\t\t.port_max_mtu\t\t= yt921x_dsa_port_max_mtu,\n4614:\t\t/* rate */\n4615:\t\t.port_policer_del\t= yt921x_dsa_port_policer_del,\n4616:\t\t.port_policer_add\t= yt921x_dsa_port_policer_add,\n4617:\t\t.port_setup_tc\t\t= yt921x_dsa_port_setup_tc,\n4618:\t\t/* acl */\n4619:\t\t.cls_flower_del\t\t= yt921x_dsa_cls_flower_del,\n4620:\t\t.cls_flower_add\t\t= yt921x_dsa_cls_flower_add,\n4621:\t\t/* hsr */\n4622:\t\t.port_hsr_leave\t\t= dsa_port_simple_hsr_leave,\n4623:\t\t.port_hsr_join\t\t= dsa_port_simple_hsr_join,\n4624:\t\t/* mirror */\n4625:\t\t.port_mirror_del\t= yt921x_dsa_port_mirror_del,\n4626:\t\t.port_mirror_add\t= yt921x_dsa_port_mirror_add,\n4627:\t\t/* lag */\n4628:\t\t.port_lag_leave\t\t= yt921x_dsa_port_lag_leave,\n4629:\t\t.port_lag_join\t\t= yt921x_dsa_port_lag_join,\n4630:\t\t/* fdb */\n4631:\t\t.port_fdb_dump\t\t= yt921x_dsa_port_fdb_dump,\n4632:\t\t.port_fast_age\t\t= yt921x_dsa_port_fast_age,\n4633:\t\t.set_ageing_time\t= yt921x_dsa_set_ageing_time,\n4634:\t\t.port_fdb_del\t\t= yt921x_dsa_port_fdb_del,\n4635:\t\t.port_fdb_add\t\t= yt921x_dsa_port_fdb_add,\n4636:\t\t.port_mdb_del\t\t= yt921x_dsa_port_mdb_del,\n4637:\t\t.port_mdb_add\t\t= yt921x_dsa_port_mdb_add,\n4638:\t\t/* vlan */\n4639:\t\t.port_vlan_filtering\t= yt921x_dsa_port_vlan_filtering,\n"
}

13/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28958
							output: 23
							thoughts: 39
						

Thoughts:
Okay, let's see...

**Analyzing `yt921x_chip_reset` Functionality**

Hmm, based on its name, `yt921x_chip_reset` *could* be interacting with hardware registers. That's a reasonable assumption, and I need to confirm that. Let's dig into the code for `yt921x_chip_reset` and see exactly what it's doing. Understanding its inner workings is crucial before I can make any assumptions about its side effects or potential issues.



14/2 2026/07/07 14:43 tool grepper
0m Args:
{
  "Expression": "yt921x_chip_reset"
}

Results:
{
  "Output": "drivers/net/dsa/motorcomm/chip.c=4227=static int yt921x_chip_detect(struct yt921x_priv *priv)\n--\ndrivers/net/dsa/motorcomm/chip.c-4293-\ndrivers/net/dsa/motorcomm/chip.c:4294:static int yt921x_chip_reset(struct yt921x_priv *priv)\ndrivers/net/dsa/motorcomm/chip.c-4295-{\n--\ndrivers/net/dsa/motorcomm/chip.c=4537=static int yt921x_dsa_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-4545-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:4546:\tres = yt921x_chip_reset(priv);\ndrivers/net/dsa/motorcomm/chip.c-4547-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n"
}

15/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29417
							output: 43
							thoughts: 27
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Analyzing `yt921x_chip_reset` and `yt921x_chip_detect`**

Alright, my brain just jumped to those two functions: `yt921x_chip_reset` and `yt921x_chip_detect`. Let's break this down. My gut tells me these are fundamental to initialization and potentially, ongoing health monitoring of a `yt921x` chipset, which is probably a critical piece of the embedded system I'm working on. The reset function is straightforward; it's the gatekeeper for bringing the chip into a defined, known state. The method used would likely depend on whether this is a full-power reset, a soft reset, or maybe even a selective reset of specific sub-modules. The implementation details – the sequence of registers written, any specific timing considerations for signal assertion/de-assertion – are where the devil is. I'll need to dig into the datasheet or the driver code to see if there are any subtle nuances.

`yt921x_chip_detect`... this one's immediately more interesting. What kind of detection are we talking about? Is it a simple ID read, a more complex register signature check, or perhaps some kind of hardware probing sequence? The approach selected would really speak volumes about the robustness of the system's error handling. Ideally, it's not just a blind ID read; it incorporates some redundancy, maybe a checksum verification or a sanity check on reported capabilities.

I'm thinking about edge cases here. What happens if the chip fails to detect at power-up? What's the fallback mechanism? Retry logic? Error reporting? Is there a timeout, and how is that handled? I need to analyze both functions to gain a clear picture. The goal is to fully understand how these functions are implemented to determine the system's resilience and potential vulnerabilities.



16/2 2026/07/07 14:43 tool read-file
0m Args:
{
  "File": "drivers/net/dsa/motorcomm/chip.c",
  "FirstLine": 4227,
  "LineCount": 100
}

Results:
{
  "Contents": "4227:\tstatic int yt921x_chip_detect(struct yt921x_priv *priv)\n4228:\t{\n4229:\t\tstruct device *dev = to_device(priv);\n4230:\t\tconst struct yt921x_info *info;\n4231:\t\tu8 extmode;\n4232:\t\tu32 chipid;\n4233:\t\tu32 major;\n4234:\t\tu32 mode;\n4235:\t\tu32 val;\n4236:\t\tint res;\n4237:\t\n4238:\t\tres = yt921x_reg_read(priv, YT921X_CHIP_ID, \u0026chipid);\n4239:\t\tif (res)\n4240:\t\t\treturn res;\n4241:\t\n4242:\t\tmajor = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);\n4243:\t\n4244:\t\tfor (info = yt921x_infos; info-\u003ename; info++)\n4245:\t\t\tif (info-\u003emajor == major)\n4246:\t\t\t\tbreak;\n4247:\t\tif (!info-\u003ename) {\n4248:\t\t\tdev_err(dev, \"Unexpected chipid 0x%x\\n\", chipid);\n4249:\t\t\treturn -ENODEV;\n4250:\t\t}\n4251:\t\n4252:\t\tres = yt921x_reg_read(priv, YT921X_CHIP_MODE, \u0026mode);\n4253:\t\tif (res)\n4254:\t\t\treturn res;\n4255:\t\tres = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, \u0026extmode);\n4256:\t\tif (res)\n4257:\t\t\treturn res;\n4258:\t\n4259:\t\tfor (; info-\u003ename; info++)\n4260:\t\t\tif (info-\u003emajor == major \u0026\u0026 info-\u003emode == mode \u0026\u0026\n4261:\t\t\t    info-\u003eextmode == extmode)\n4262:\t\t\t\tbreak;\n4263:\t\tif (!info-\u003ename) {\n4264:\t\t\tdev_err(dev,\n4265:\t\t\t\t\"Unsupported chipid 0x%x with chipmode 0x%x 0x%x\\n\",\n4266:\t\t\t\tchipid, mode, extmode);\n4267:\t\t\treturn -ENODEV;\n4268:\t\t}\n4269:\t\n4270:\t\tres = yt921x_reg_read(priv, YT921X_SYS_CLK, \u0026val);\n4271:\t\tif (res)\n4272:\t\t\treturn res;\n4273:\t\tswitch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) {\n4274:\t\tcase 0:\n4275:\t\t\tpriv-\u003ecycle_ns = info-\u003emajor == YT9215_MAJOR ? 8 : 6;\n4276:\t\t\tbreak;\n4277:\t\tcase YT921X_SYS_CLK_143M:\n4278:\t\t\tpriv-\u003ecycle_ns = 7;\n4279:\t\t\tbreak;\n4280:\t\tdefault:\n4281:\t\t\tpriv-\u003ecycle_ns = 8;\n4282:\t\t}\n4283:\t\n4284:\t\t/* Print chipid here since we are interested in lower 16 bits */\n4285:\t\tdev_info(dev,\n4286:\t\t\t \"Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\\n\",\n4287:\t\t\t info-\u003ename, chipid, mode, extmode);\n4288:\t\n4289:\t\tpriv-\u003einfo = info;\n4290:\t\n4291:\t\treturn 0;\n4292:\t}\n4293:\t\n4294:\tstatic int yt921x_chip_reset(struct yt921x_priv *priv)\n4295:\t{\n4296:\t\tstruct device *dev = to_device(priv);\n4297:\t\tu16 eth_p_tag;\n4298:\t\tu32 val;\n4299:\t\tint res;\n4300:\t\n4301:\t\tres = yt921x_chip_detect(priv);\n4302:\t\tif (res)\n4303:\t\t\treturn res;\n4304:\t\n4305:\t\t/* Reset */\n4306:\t\tres = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW);\n4307:\t\tif (res)\n4308:\t\t\treturn res;\n4309:\t\n4310:\t\t/* RST_HW is almost same as GPIO hard reset, so we need this delay. */\n4311:\t\tfsleep(YT921X_RST_DELAY_US);\n4312:\t\n4313:\t\tval = 0;\n4314:\t\tres = yt921x_reg_wait(priv, YT921X_RST, ~0, \u0026val);\n4315:\t\tif (res)\n4316:\t\t\treturn res;\n4317:\t\n4318:\t\t/* Check for tag EtherType; do it after reset in case you messed it up\n4319:\t\t * before.\n4320:\t\t */\n4321:\t\tres = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, \u0026val);\n4322:\t\tif (res)\n4323:\t\t\treturn res;\n4324:\t\teth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);\n4325:\t\tif (eth_p_tag != ETH_P_YT921X) {\n4326:\t\t\tdev_err(dev, \"Tag type 0x%x != 0x%x\\n\", eth_p_tag,\n"
}

17/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31510
							output: 26
							thoughts: 363
						

Thoughts:
**Analyzing the YT921X Chip Detection Logic for Fuzzing**

Okay, so I'm looking at how `yt921x_chip_detect` handles the `YT921X_CHIP_ID`. It expects a specific major version, and if it doesn't get it, it throws a `-ENODEV` error. Since this is an MDIO device, my mind immediately jumps to syzkaller and its ability to emulate reads and writes via MDIO, either through a mock device or by injecting faults.

But here's the snag. Syzkaller doesn't have a *general* MDIO mock device that can just spit out arbitrary values for `mdiobus_read` unless it's specifically coded for. Then I thought, okay, it has specific mocks like `mac80211_hwsim` for wifi, `vkms` for DRM, `vhci` for USB, etc. But does it have *anything* for MDIO? I found `mdio-mock` and `mdio-bcm-unimac`, which are specific implementations, but nothing generic. This isn't going to be a simple solution.

Maybe I could leverage `bpf` to intercept the operations, or perhaps there's a dummy MDIO bus I'm missing? I have to go through the syzkaller documentation and code again. Wait, did syzkaller ever implement `syz_mdio_read` or `syz_mdio_write`? No, I don't see those there.

Alright, what if the device is created through a device tree? Syzkaller *can* inject device tree nodes, but that wouldn't help me emulate the hardware's response, especially the crucial `YT921X_CHIP_ID` value. That's a dead end.

Okay, let's back up. The code reveals that `yt921x_reg_read` uses `priv->reg_ops->read`. So the next step is to examine what `yt921x_reg_ops_mdio` actually *is*. That will be key. Time to dig in there.



18/2 2026/07/07 14:43 tool grepper
0m Args:
{
  "Expression": "yt921x_reg_ops_mdio"
}

Results:
{
  "Output": "drivers/net/dsa/motorcomm/chip.c=236=static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)\n--\ndrivers/net/dsa/motorcomm/chip.c-271-\ndrivers/net/dsa/motorcomm/chip.c:272:static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {\ndrivers/net/dsa/motorcomm/chip.c-273-\t.read = yt921x_reg_mdio_read,\n--\ndrivers/net/dsa/motorcomm/chip.c=4718=static int yt921x_mdio_probe(struct mdio_device *mdiodev)\n--\ndrivers/net/dsa/motorcomm/chip.c-4738-\ndrivers/net/dsa/motorcomm/chip.c:4739:\tpriv-\u003ereg_ops = \u0026yt921x_reg_ops_mdio;\ndrivers/net/dsa/motorcomm/chip.c-4740-\tpriv-\u003ereg_ctx = mdio;\n"
}

19/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 32668
							output: 21
							thoughts: 96
						

Thoughts:
**Analyzing the MDIO Bus Read**

Okay, so I'm looking at this MDIO bus read. The function `mdiobus_read` is the key here. I need to figure out what happens if the hardware isn't responding. The documentation says it'll return either `0xffff` or `-EIO`. If it's `0xffff`, then `chipid` is going to be `0xffffffff`. Consequently, `major` is also going to be `0xffff`.

Now, the important question is: does `yt921x_infos` handle a `0xffff` value? I highly doubt it's expecting that, so I need to verify what happens when `major` takes that value and it's compared against the expected values within `yt921x_infos`. Let me double-check that part of the code and see how `yt921x_infos` is handled.



20/2 2026/07/07 14:43 tool grepper
0m Args:
{
  "Expression": "yt921x_infos"
}

Results:
{
  "Output": "drivers/net/dsa/motorcomm/chip.c=99=struct yt921x_info {\n--\ndrivers/net/dsa/motorcomm/chip.c-115-\ndrivers/net/dsa/motorcomm/chip.c:116:static const struct yt921x_info yt921x_infos[] = {\ndrivers/net/dsa/motorcomm/chip.c-117-\t{\n--\ndrivers/net/dsa/motorcomm/chip.c=4227=static int yt921x_chip_detect(struct yt921x_priv *priv)\n--\ndrivers/net/dsa/motorcomm/chip.c-4243-\ndrivers/net/dsa/motorcomm/chip.c:4244:\tfor (info = yt921x_infos; info-\u003ename; info++)\ndrivers/net/dsa/motorcomm/chip.c-4245-\t\tif (info-\u003emajor == major)\n"
}

21/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 33239
							output: 41
							thoughts: 13
						

Thoughts:
Okay, here's my interpretation of that thought process, assuming I'm deeply familiar with relevant technical contexts.

**Analyzing `yt921x_infos`**

Right, let's dive into this. I'm seeing a reference to `yt921x_infos`. My immediate assumption, given the naming convention, is that this likely represents some sort of structured data or configuration file related to the `yt921x` project or component. It could contain any number of things – settings, metadata, parameter definitions, perhaps even performance metrics, depending on the context of what that `yt921x` is used for. I'm immediately running through a mental checklist: What kind of project is this? What's the typical structure of these files within this ecosystem? Is it likely YAML, JSON, a custom format, or something more specialized? The name 'infos' implies something informational – details, if you will, but I need a whole lot more information to determine its relevance. My next step will be to confirm my assumptions and get some more information on this data. Time to get to work.



22/2 2026/07/07 14:43 tool read-file
0m Args:
{
  "File": "drivers/net/dsa/motorcomm/chip.c",
  "FirstLine": 116,
  "LineCount": 50
}

Results:
{
  "Contents": " 116:\tstatic const struct yt921x_info yt921x_infos[] = {\n 117:\t\t{\n 118:\t\t\t\"YT9215SC\", YT9215_MAJOR, 1, 0,\n 119:\t\t\tYT921X_PORT_MASK_INT0_n(5),\n 120:\t\t\tYT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,\n 121:\t\t},\n 122:\t\t{\n 123:\t\t\t\"YT9215S\", YT9215_MAJOR, 2, 0,\n 124:\t\t\tYT921X_PORT_MASK_INT0_n(5),\n 125:\t\t\tYT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,\n 126:\t\t},\n 127:\t\t{\n 128:\t\t\t\"YT9215RB\", YT9215_MAJOR, 3, 0,\n 129:\t\t\tYT921X_PORT_MASK_INT0_n(5),\n 130:\t\t\tYT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,\n 131:\t\t},\n 132:\t\t{\n 133:\t\t\t\"YT9214NB\", YT9215_MAJOR, 3, 2,\n 134:\t\t\tYT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),\n 135:\t\t\tYT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,\n 136:\t\t},\n 137:\t\t{\n 138:\t\t\t\"YT9213NB\", YT9215_MAJOR, 3, 3,\n 139:\t\t\tYT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),\n 140:\t\t\tYT921X_PORT_MASK_EXT1,\n 141:\t\t},\n 142:\t\t{\n 143:\t\t\t\"YT9218N\", YT9218_MAJOR, 0, 0,\n 144:\t\t\tYT921X_PORT_MASK_INT0_n(8),\n 145:\t\t\t0,\n 146:\t\t},\n 147:\t\t{\n 148:\t\t\t\"YT9218MB\", YT9218_MAJOR, 1, 0,\n 149:\t\t\tYT921X_PORT_MASK_INT0_n(8),\n 150:\t\t\tYT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,\n 151:\t\t},\n 152:\t\t{}\n 153:\t};\n 154:\t\n 155:\t#define YT921X_VID_UNWARE\t4095\n 156:\t\n 157:\t/* The interval should be small enough to avoid overflow of 32bit MIBs.\n 158:\t *\n 159:\t * Until we can read MIBs from stats64 call directly (i.e. sleep\n 160:\t * there), we have to poll stats more frequently then it is actually needed.\n 161:\t * For overflow protection, normally, 100 sec interval should have been OK.\n 162:\t */\n 163:\t#define YT921X_STATS_INTERVAL_JIFFIES\t(3 * HZ)\n 164:\t\n 165:\tstruct yt921x_reg_mdio {\n"
}

23/2 2026/07/07 14:43 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 34499
							output: 18
							thoughts: 664
						

Thoughts:
**Unreachable Code Analysis**

Okay, so I'm diving into this YT9215/YT9218 situation. First, the `major` version needs to be either `YT9215_MAJOR` or `YT9218_MAJOR`. Now, without actual hardware, `yt921x_chip_detect` will inevitably fail. And if that fails, `yt921x_chip_reset` fails too. This failure cascades – `yt921x_dsa_setup` goes down with it, leading to a `dsa_register_switch` failure, and ultimately, the dreaded `yt921x_mdio_probe` failure. It's a chain reaction.

The problem? In any virtualized environment, like the ones Syzkaller uses, or even just in QEMU, the device will never successfully probe because it's expecting a physical hardware response that isn't there.

Hold on, I need to confirm something. Syzkaller doesn't have a built-in MDIO emulator that can return *specific* `YT921X_CHIP_ID` values. That's a key piece missing. I was hoping I could somehow get to `yt921x_leds_setup`, but it's called within `yt921x_dsa_setup`, *after* `yt921x_chip_reset` and `yt921x_chip_setup`. So, with the failure from the initial hardware detection, there's no way to reach the LED setup code.

Let's double check. Can `dsa_register_switch` be called anywhere else? Nope. Only `yt921x_mdio_probe` calls it. Okay, is there *any* other entry point to trigger the LED-related code? The code itself is located in `drivers/net/dsa/motorcomm/leds.c`, which uses `devm_led_classdev_register_ext` to register the LEDs. But it's only called from within `yt921x_leds_setup`. And `yt921x_leds_setup` is *only* called from `yt921x_dsa_setup`.

So, the conclusion is this: Since `yt921x_chip_detect` fails early, the whole `yt921x_dsa_setup` function fails too, and the LED code is completely unreachable in a virtual environment. I need to be *sure* about this.

I was hoping there was some kind of mock MDIO bus implementation in the kernel that Syzkaller *could* leverage to inject arbitrary responses. There's `drivers/net/phy/mdio-mock.c`? No such luck. There are various MDIO drivers like `mdio-gpio.c`, `mdio-mux.c`, and so on.

Syzkaller *could* potentially use something like `mdio-gpio` to construct an MDIO bus. However, it *cannot* mock the responses of a specific PHY/switch chip unless it's emulated in QEMU. And QEMU, as far as I know, doesn't emulate the Motorcomm YT9215. This confirms that the code is utterly unreachable in these standard virtualized environments. I'm going to lock that down.



24/2 2026/07/07 14:44 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "mdio.*mock"
}

Results:
{
  "error": "no matches"
}

25/2 2026/07/07 14:44 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 35932
							output: 26
							thoughts: 522
						

Thoughts:
**Unreachable Code in the DSA Driver: A Deep Dive**

Okay, let's break this down. My initial thought was to see if QEMU provides any MDIO emulation that could help. I know QEMU does have some MDIO emulation, but it's specific to certain MACs and PHYs, and critically, doesn't seem to touch DSA switches on the MDIO bus. So, the `yt921x_mdio_probe` function is going to fail – no dice there. And the syzkaller environment, at least as I understand it, isn't set up to emulate this switch either.

The next question was whether syzkaller could leverage eBPF to mock the `mdiobus_read` calls, effectively bypassing the MDIO setup. But, and this is important, syzkaller doesn't use eBPF for that kind of hardware register mocking. It relies on pre-existing kernel interfaces or QEMU's built-in emulation, and since there's no `mdiosim` or similar here, that path is blocked. That seals it: `WorthFuzzing` should be false.

Just to be absolutely certain, I went back and examined other potential entry points. The patch impacts `leds.c` and `smi.c` within the DSA driver. `smi.c` has register read/write wrappers, and `leds.c` contains LED-related operations. However, both of these require the `yt921x_priv` structure. This structure, critically, is allocated within `yt921x_mdio_probe`. If that function fails, the `priv` structure is freed, and no LEDs are registered. Therefore, functions like `yt921x_cled_brightness_set_blocking` and `yt921x_cled_blink_set`, are never going to be called. It's unreachable code.

Now, a final, quick check. What if `yt921x_reg_read` returns 0? Could `yt921x_chip_detect` succeed under those circumstances? If `yt921x_reg_read` is 0, then the `chipid` value will be 0. So `major` would be set to whatever `FIELD_GET(YT921X_CHIP_ID_MAJOR, 0)` evaluates to. Let me quickly double-check the definition of `YT921X_CHIP_ID_MAJOR` just to be completely sure.



26/2 2026/07/07 14:44 tool grepper
0m Args:
{
  "Expression": "YT921X_CHIP_ID_MAJOR"
}

Results:
{
  "Output": "drivers/net/dsa/motorcomm/chip.c=4227=static int yt921x_chip_detect(struct yt921x_priv *priv)\n--\ndrivers/net/dsa/motorcomm/chip.c-4241-\ndrivers/net/dsa/motorcomm/chip.c:4242:\tmajor = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);\ndrivers/net/dsa/motorcomm/chip.c-4243-\n--\ndrivers/net/dsa/motorcomm/chip.h-29-#define YT921X_CHIP_ID\t\t\t0x80008\ndrivers/net/dsa/motorcomm/chip.h:30:#define  YT921X_CHIP_ID_MAJOR\t\t\tGENMASK(31, 16)\ndrivers/net/dsa/motorcomm/chip.h-31-#define YT921X_EXT_CPU_PORT\t\t0x8000c\n"
}

27/2 2026/07/07 14:44 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 37286
							output: 21
							thoughts: 52
						

Thoughts:
**Analyzing Chip Identification and Major Version Numbers**

Okay, so if the `chipid` value is 0, the `major` version number should also be 0. That's a fundamental assumption I need to keep in mind as I process this data. I need to make sure I haven't missed anything obvious. I'll double-check the `yt921x_infos` data structure again. I need to pay particular attention to the definitions of `YT9215_MAJOR` and `YT9218_MAJOR` because these are key. The relationship between the `chipid`, the `major` version, and these constants is probably critical for proper device identification and functionality.



28/2 2026/07/07 14:44 tool grepper
0m Args:
{
  "Expression": "YT9215_MAJOR"
}

Results:
{
  "Output": "drivers/net/dsa/motorcomm/chip.c=116=static const struct yt921x_info yt921x_infos[] = {\ndrivers/net/dsa/motorcomm/chip.c-117-\t{\ndrivers/net/dsa/motorcomm/chip.c:118:\t\t\"YT9215SC\", YT9215_MAJOR, 1, 0,\ndrivers/net/dsa/motorcomm/chip.c-119-\t\tYT921X_PORT_MASK_INT0_n(5),\n--\ndrivers/net/dsa/motorcomm/chip.c-122-\t{\ndrivers/net/dsa/motorcomm/chip.c:123:\t\t\"YT9215S\", YT9215_MAJOR, 2, 0,\ndrivers/net/dsa/motorcomm/chip.c-124-\t\tYT921X_PORT_MASK_INT0_n(5),\n--\ndrivers/net/dsa/motorcomm/chip.c-127-\t{\ndrivers/net/dsa/motorcomm/chip.c:128:\t\t\"YT9215RB\", YT9215_MAJOR, 3, 0,\ndrivers/net/dsa/motorcomm/chip.c-129-\t\tYT921X_PORT_MASK_INT0_n(5),\n--\ndrivers/net/dsa/motorcomm/chip.c-132-\t{\ndrivers/net/dsa/motorcomm/chip.c:133:\t\t\"YT9214NB\", YT9215_MAJOR, 3, 2,\ndrivers/net/dsa/motorcomm/chip.c-134-\t\tYT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),\n--\ndrivers/net/dsa/motorcomm/chip.c-137-\t{\ndrivers/net/dsa/motorcomm/chip.c:138:\t\t\"YT9213NB\", YT9215_MAJOR, 3, 3,\ndrivers/net/dsa/motorcomm/chip.c-139-\t\tYT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),\n--\ndrivers/net/dsa/motorcomm/chip.c=4227=static int yt921x_chip_detect(struct yt921x_priv *priv)\n--\ndrivers/net/dsa/motorcomm/chip.c-4274-\tcase 0:\ndrivers/net/dsa/motorcomm/chip.c:4275:\t\tpriv-\u003ecycle_ns = info-\u003emajor == YT9215_MAJOR ? 8 : 6;\ndrivers/net/dsa/motorcomm/chip.c-4276-\t\tbreak;\n--\ndrivers/net/dsa/motorcomm/chip.h=808=enum yt921x_fdb_entry_status {\n--\ndrivers/net/dsa/motorcomm/chip.h-837-\ndrivers/net/dsa/motorcomm/chip.h:838:#define YT9215_MAJOR\t0x9002\ndrivers/net/dsa/motorcomm/chip.h-839-#define YT9218_MAJOR\t0x9001\n"
}

29/2 2026/07/07 14:44 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 38359
							output: 159
							thoughts: 180
						

Thoughts:
**Unreachable Code in a Virtualized Environment**

Okay, let me break this down. From what I see, `YT9215_MAJOR` is defined as `0x9002`, and `YT9218_MAJOR` is defined as `0x9001`. Now, if the `chipid` variable happens to be zero or `0xffffffff`, then the corresponding `major` value will be either zero or `0xffff`. Neither of those major values will match either of the expected values (`0x9002` or `0x9001`). This means the `yt921x_chip_detect` function is guaranteed to fail in this scenario.

Consequently, since the chip detection fails, the driver's probe function will never execute. This is a critical point. If the probe function never runs, then the subsequent LED code, which I'm assuming relies on a successful probe, is completely unreachable. It’s essentially dead code in this setup.

Therefore, for this fuzzing effort, I'm going to return `WorthFuzzing: false`. My reasoning is that this specific portion of the code is unreachable in any typical virtualized environment because it depends on the presence of physical hardware – specifically the Motorcomm YT9215/YT9218 switch chip – for successful probing. QEMU, or any virtualization setup I'm familiar with, doesn't emulate that particular piece of hardware. So, any attempt to fuzz this code in the current environment is going to hit a dead end, making it a waste of resources.



30/2 2026/07/07 14:44 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch adds LED support for the Motorcomm YT9215/YT9218 ethernet switch chips. The driver requires specific hardware to be present on the MDIO bus to probe successfully (it reads the chip ID register and expects specific values like 0x9002 or 0x9001). Since there is no emulation for this specific hardware in QEMU or a generic MDIO mock device in the kernel, the driver will fail to probe in virtualized environments. Consequently, the newly added LED classdev operations and SMI read/write wrappers are completely unreachable and cannot be fuzzed.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds LED support for the Motorcomm YT9215/YT9218 ethernet switch chips. The driver requires specific hardware to be present on the MDIO bus to probe successfully (it reads the chip ID register and expects specific values like 0x9002 or 0x9001). Since there is no emulation for this specific hardware in QEMU or a generic MDIO mock device in the kernel, the driver will fail to probe in virtualized environments. Consequently, the newly added LED classdev operations and SMI read/write wrappers are completely unreachable and cannot be fuzzed.",
  "WorthFuzzing": false
}

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