| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/07 14:42 | flow | patch-triage |
1mArgs: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 |
0mArgs: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 |
1mModel: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 |
0mModel: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 |
0mError:no matches Args: {
"Expression": "yt921x_probe"
}Results: {
"error": "no matches"
} |
| 5/2 | 2026/07/07 14:43 | llm | patch-evaluator |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mError:no matches Args: {
"Expression": "mdio.*mock"
}Results: {
"error": "no matches"
} |
| 25/2 | 2026/07/07 14:44 | llm | patch-evaluator |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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 |
0mModel: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 |
0mArgs:{
"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
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|