| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/03 17:16 | flow | patch-triage |
3mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch refactors the Motorcomm YT9215 DSA switch driver and adds LED support for it. This is a hardware-specific driver that requires the physical Motorcomm switch chip to be present on an MDIO bus to probe successfully (it reads the chip ID via MDIO in `yt921x_chip_detect`). Since there is no software emulation for this hardware in QEMU/syzkaller, the modified code paths are impossible to reach in a virtualized fuzzing environment.",
"WorthFuzzing": false
} |
| 1/1 | 2026/07/03 17:16 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 907e79cf928cfb461185f3cdefd14c5e9cb35a04\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Jul 3 17:16:30 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..a3512136383b36 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 \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..b066a7e84fe6bc\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/leds.c\n@@ -0,0 +1,607 @@\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+\taddr = yt921x_led_regaddr(priv, port, group);\n+\tres = yt921x_reg_read(priv, addr, \u0026val);\n+\tif (res)\n+\t\treturn res;\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, int *polarityp)\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+\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\tgroup, port);\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+\tpp-\u003eleds[group] = led;\n+\n+\tled-\u003eport = pp;\n+\tled-\u003egroup = group;\n+\n+\tstate = led_init_default_state_get(fwnode);\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tswitch (state) {\n+\tcase LEDS_DEFSTATE_OFF:\n+\tcase LEDS_DEFSTATE_ON:\n+\t\ton = state != LEDS_DEFSTATE_OFF;\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+\t*polarityp = polarity;\n+\treturn 0;\n+\n+err:\n+\tpp-\u003eleds[group] = NULL;\n+\tdevm_kfree(dev, led);\n+\treturn res;\n+}\n+\n+static void yt921x_leds_teardown(struct yt921x_priv *priv)\n+{\n+\tstruct device *dev = to_device(priv);\n+\tu32 mask;\n+\tint res;\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\tcontinue;\n+\n+\t\t\tdevm_led_classdev_unregister(dev, \u0026led-\u003ecdev);\n+\t\t\tpp-\u003eleds[group] = NULL;\n+\t\t\tdevm_kfree(dev, led);\n+\t\t}\n+\t}\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tres = yt921x_reg_write(priv, YT921X_LED_PAR_PORTS, 0);\n+\tif (res)\n+\t\tgoto end;\n+\n+\tmask = YT921X_LED_CTRL_PORT_NUM_M | YT921X_LED_CTRL_EN;\n+\tres = yt921x_reg_clear_bits(priv, YT921X_LED_CTRL, mask);\n+\n+end:\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\n+\tif (res)\n+\t\tdev_err(dev, \"Failed to teardown LEDs: %d\\n\", res);\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\tint polarity;\n+\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 \u0026polarity);\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+\n+\t\t\tif (polarity) {\n+\t\t\t\tinv_mask |= BIT(port);\n+\t\t\t\tif (polarity \u003e 0)\n+\t\t\t\t\tinv_ctrl |= BIT(port);\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_teardown(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..bbd207e09a8df3\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/leds.h\n@@ -0,0 +1,118 @@\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+int yt921x_leds_setup(struct yt921x_priv *priv);\n+\n+#else\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/03 17:16 | agent | patch-evaluator |
3mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch refactors the Motorcomm YT9215 DSA switch driver and adds LED support for it. This is a hardware-specific driver that requires the physical Motorcomm switch chip to be present on an MDIO bus to probe successfully (it reads the chip ID via MDIO in `yt921x_chip_detect`). Since there is no software emulation for this hardware in QEMU/syzkaller, the modified code paths are impossible to reach in a virtualized fuzzing environment.",
"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 907e79cf928cfb461185f3cdefd14c5e9cb35a04
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Jul 3 17:16:30 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..a3512136383b36 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;
}
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..b066a7e84fe6bc
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/leds.c
@@ -0,0 +1,607 @@
+// 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;
+
+ 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, int *polarityp)
+{
+ 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;
+ }
+
+ 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",
+ group, port);
+ return -EINVAL;
+ }
+ polarity = force_high ? 1 : force_low ? -1 : 0;
+
+ led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+ pp->leds[group] = led;
+
+ led->port = pp;
+ led->group = group;
+
+ state = led_init_default_state_get(fwnode);
+ mutex_lock(&priv->reg_lock);
+ switch (state) {
+ case LEDS_DEFSTATE_OFF:
+ case LEDS_DEFSTATE_ON:
+ on = state != LEDS_DEFSTATE_OFF;
+ 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;
+
+ *polarityp = polarity;
+ return 0;
+
+err:
+ pp->leds[group] = NULL;
+ devm_kfree(dev, led);
+ return res;
+}
+
+static void yt921x_leds_teardown(struct yt921x_priv *priv)
+{
+ struct device *dev = to_device(priv);
+ u32 mask;
+ int res;
+
+ 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)
+ continue;
+
+ devm_led_classdev_unregister(dev, &led->cdev);
+ pp->leds[group] = NULL;
+ devm_kfree(dev, led);
+ }
+ }
+
+ mutex_lock(&priv->reg_lock);
+
+ res = yt921x_reg_write(priv, YT921X_LED_PAR_PORTS, 0);
+ if (res)
+ goto end;
+
+ mask = YT921X_LED_CTRL_PORT_NUM_M | YT921X_LED_CTRL_EN;
+ res = yt921x_reg_clear_bits(priv, YT921X_LED_CTRL, mask);
+
+end:
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to teardown LEDs: %d\n", res);
+}
+
+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) {
+ int polarity;
+
+ res = yt921x_led_setup(priv, port,
+ of_fwnode_handle(led_np),
+ &polarity);
+ if (res) {
+ of_node_put(leds_np);
+ goto err;
+ }
+
+ if (polarity) {
+ inv_mask |= BIT(port);
+ if (polarity > 0)
+ inv_ctrl |= BIT(port);
+ }
+ }
+
+ 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_teardown(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..bbd207e09a8df3
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/leds.h
@@ -0,0 +1,118 @@
+/* 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)
+
+int yt921x_leds_setup(struct yt921x_priv *priv);
+
+#else
+
+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/03 17:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20048 output: 19 thoughts: 366 Thoughts: **Analyzing the Reachability of the YT9215 LED Driver** Okay, so there's a patch introducing LED support for the Motorcomm YT9215 ethernet switch chip driver. The patch touches `drivers/net/dsa/motorcomm/leds.c` and related files, and it seems to introduce `CONFIG_NET_DSA_YT921X_LEDS` and move the driver. This driver is for a DSA (Distributed Switch Architecture) ethernet switch chip. My immediate concern is reachability, especially in a virtualized or simulated environment. DSA drivers, by their nature, generally rely on specific hardware – the switch chip itself connected via MDIO, SPI, or a similar interface. Unless there's some obscure software emulator specifically for this YT9215 chip (which is highly improbable), the driver's `probe` function is going to fail to find the required hardware. It'll be looking for something on a bus that simply won't exist in a virtual machine or most testing environments. Even if we somehow force the driver to load, perhaps by adding a device tree node, it'll attempt to read from the MDIO or SPI bus, and those reads will fail or timeout, ultimately causing the probe to fail. The question then becomes, can we somehow *fuzz* this code? Could we perhaps mock an MDIO bus to simulate communication? The issue is that standard fuzzing tools, like syzkaller, generally don't offer generic MDIO or SPI mocks that would allow us to probe an arbitrary DSA switch without explicit support. Hardware-specific drivers that lack proper emulation capabilities are usually considered unreachable and therefore not worth fuzzing. My next step is to verify whether `yt921x` is even reachable at all. I will search the kernel source for any mentions of `yt921x` to get a clearer picture of its potential reach. |
| 4/2 | 2026/07/03 17:16 | tool | grepper |
0mArgs:{
"Expression": "yt921x"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 2590 lines.\nUse more precise expression if possible.\n\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml-3----\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml:4:$id: http://devicetree.org/schemas/net/dsa/motorcomm,yt921x.yaml#\nDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml-5-$schema: http://devicetree.org/meta-schemas/core.yaml#\n--\nMAINTAINERS=18231=S:\tMaintained\nMAINTAINERS:18232:F:\tDocumentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml\nMAINTAINERS-18233-F:\tdrivers/net/dsa/motorcomm/\nMAINTAINERS:18234:F:\tnet/dsa/tag_yt921x.c\nMAINTAINERS-18235-\n--\ndrivers/net/dsa/motorcomm/Makefile-1-# SPDX-License-Identifier: ISC\ndrivers/net/dsa/motorcomm/Makefile:2:obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o\ndrivers/net/dsa/motorcomm/Makefile:3:yt921x-objs := chip.o\ndrivers/net/dsa/motorcomm/Makefile:4:yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o\ndrivers/net/dsa/motorcomm/Makefile:5:yt921x-objs += smi.o\n--\ndrivers/net/dsa/motorcomm/chip.c-31-\ndrivers/net/dsa/motorcomm/chip.c:32:struct yt921x_mib_desc {\ndrivers/net/dsa/motorcomm/chip.c-33-\tunsigned int size;\n--\ndrivers/net/dsa/motorcomm/chip.c-40-\ndrivers/net/dsa/motorcomm/chip.c:41:/* Must agree with yt921x_mib\ndrivers/net/dsa/motorcomm/chip.c-42- *\n--\ndrivers/net/dsa/motorcomm/chip.c-46- */\ndrivers/net/dsa/motorcomm/chip.c:47:static const struct yt921x_mib_desc yt921x_mib_descs[] = {\ndrivers/net/dsa/motorcomm/chip.c-48-\tMIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),\n--\ndrivers/net/dsa/motorcomm/chip.c-98-\ndrivers/net/dsa/motorcomm/chip.c:99:struct yt921x_info {\ndrivers/net/dsa/motorcomm/chip.c-100-\tconst char *name;\n--\ndrivers/net/dsa/motorcomm/chip.c-106-\tu16 internal_mask;\ndrivers/net/dsa/motorcomm/chip.c:107:\t/* TODO: see comments in yt921x_dsa_phylink_get_caps() */\ndrivers/net/dsa/motorcomm/chip.c-108-\tu16 external_mask;\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-164-\ndrivers/net/dsa/motorcomm/chip.c:165:struct yt921x_reg_mdio {\ndrivers/net/dsa/motorcomm/chip.c-166-\tstruct mii_bus *bus;\n--\ndrivers/net/dsa/motorcomm/chip.c-181-\ndrivers/net/dsa/motorcomm/chip.c:182:#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)\ndrivers/net/dsa/motorcomm/chip.c-183-#define to_device(priv) ((priv)-\u003eds.dev)\n--\ndrivers/net/dsa/motorcomm/chip.c=190=static u32 ethaddr_lo2_to_u32(const unsigned char *addr)\n--\ndrivers/net/dsa/motorcomm/chip.c-194-\ndrivers/net/dsa/motorcomm/chip.c:195:static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)\ndrivers/net/dsa/motorcomm/chip.c-196-{\ndrivers/net/dsa/motorcomm/chip.c:197:\tstruct yt921x_reg_mdio *mdio = context;\ndrivers/net/dsa/motorcomm/chip.c-198-\tstruct mii_bus *bus = mdio-\u003ebus;\n--\ndrivers/net/dsa/motorcomm/chip.c-235-\ndrivers/net/dsa/motorcomm/chip.c:236:static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)\ndrivers/net/dsa/motorcomm/chip.c-237-{\ndrivers/net/dsa/motorcomm/chip.c:238:\tstruct yt921x_reg_mdio *mdio = context;\ndrivers/net/dsa/motorcomm/chip.c-239-\tstruct mii_bus *bus = mdio-\u003ebus;\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,\ndrivers/net/dsa/motorcomm/chip.c:274:\t.write = yt921x_reg_mdio_write,\ndrivers/net/dsa/motorcomm/chip.c-275-};\n--\ndrivers/net/dsa/motorcomm/chip.c-278-\ndrivers/net/dsa/motorcomm/chip.c:279:static int yt921x_intif_wait(struct yt921x_priv *priv)\ndrivers/net/dsa/motorcomm/chip.c-280-{\n--\ndrivers/net/dsa/motorcomm/chip.c-282-\ndrivers/net/dsa/motorcomm/chip.c:283:\treturn yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START,\ndrivers/net/dsa/motorcomm/chip.c-284-\t\t\t \u0026val);\n--\ndrivers/net/dsa/motorcomm/chip.c=287=static int\ndrivers/net/dsa/motorcomm/chip.c:288:yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)\ndrivers/net/dsa/motorcomm/chip.c-289-{\n--\ndrivers/net/dsa/motorcomm/chip.c-295-\ndrivers/net/dsa/motorcomm/chip.c:296:\tres = yt921x_intif_wait(priv);\ndrivers/net/dsa/motorcomm/chip.c-297-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-303-\t YT921X_MBUS_CTRL_READ;\ndrivers/net/dsa/motorcomm/chip.c:304:\tres = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);\ndrivers/net/dsa/motorcomm/chip.c-305-\tif (res)\ndrivers/net/dsa/motorcomm/chip.c-306-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:307:\tres = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);\ndrivers/net/dsa/motorcomm/chip.c-308-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-310-\ndrivers/net/dsa/motorcomm/chip.c:311:\tres = yt921x_intif_wait(priv);\ndrivers/net/dsa/motorcomm/chip.c-312-\tif (res)\ndrivers/net/dsa/motorcomm/chip.c-313-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:314:\tres = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, \u0026val);\ndrivers/net/dsa/motorcomm/chip.c-315-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c=326=static int\ndrivers/net/dsa/motorcomm/chip.c:327:yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)\ndrivers/net/dsa/motorcomm/chip.c-328-{\n--\ndrivers/net/dsa/motorcomm/chip.c-332-\ndrivers/net/dsa/motorcomm/chip.c:333:\tres = yt921x_intif_wait(priv);\ndrivers/net/dsa/motorcomm/chip.c-334-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-340-\t YT921X_MBUS_CTRL_WRITE;\ndrivers/net/dsa/motorcomm/chip.c:341:\tres = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);\ndrivers/net/dsa/motorcomm/chip.c-342-\tif (res)\ndrivers/net/dsa/motorcomm/chip.c-343-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:344:\tres = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val);\ndrivers/net/dsa/motorcomm/chip.c-345-\tif (res)\ndrivers/net/dsa/motorcomm/chip.c-346-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:347:\tres = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);\ndrivers/net/dsa/motorcomm/chip.c-348-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-350-\ndrivers/net/dsa/motorcomm/chip.c:351:\treturn yt921x_intif_wait(priv);\ndrivers/net/dsa/motorcomm/chip.c-352-}\ndrivers/net/dsa/motorcomm/chip.c-353-\ndrivers/net/dsa/motorcomm/chip.c:354:static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)\ndrivers/net/dsa/motorcomm/chip.c-355-{\ndrivers/net/dsa/motorcomm/chip.c:356:\tstruct yt921x_priv *priv = mbus-\u003epriv;\ndrivers/net/dsa/motorcomm/chip.c-357-\tu16 val;\n--\ndrivers/net/dsa/motorcomm/chip.c-363-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:364:\tres = yt921x_intif_read(priv, port, reg, \u0026val);\ndrivers/net/dsa/motorcomm/chip.c-365-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c=372=static int\ndrivers/net/dsa/motorcomm/chip.c:373:yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)\ndrivers/net/dsa/motorcomm/chip.c-374-{\ndrivers/net/dsa/motorcomm/chip.c:375:\tstruct yt921x_priv *priv = mbus-\u003epriv;\ndrivers/net/dsa/motorcomm/chip.c-376-\tint res;\n--\ndrivers/net/dsa/motorcomm/chip.c-381-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:382:\tres = yt921x_intif_write(priv, port, reg, data);\ndrivers/net/dsa/motorcomm/chip.c-383-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c=388=static int\ndrivers/net/dsa/motorcomm/chip.c:389:yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)\ndrivers/net/dsa/motorcomm/chip.c-390-{\n--\ndrivers/net/dsa/motorcomm/chip.c-401-\tmbus-\u003epriv = priv;\ndrivers/net/dsa/motorcomm/chip.c:402:\tmbus-\u003eread = yt921x_mbus_int_read;\ndrivers/net/dsa/motorcomm/chip.c:403:\tmbus-\u003ewrite = yt921x_mbus_int_write;\ndrivers/net/dsa/motorcomm/chip.c-404-\tmbus-\u003eparent = dev;\n--\ndrivers/net/dsa/motorcomm/chip.c-415-\ndrivers/net/dsa/motorcomm/chip.c:416:static int yt921x_extif_wait(struct yt921x_priv *priv)\ndrivers/net/dsa/motorcomm/chip.c-417-{\n--\ndrivers/net/dsa/motorcomm/chip.c-419-\ndrivers/net/dsa/motorcomm/chip.c:420:\treturn yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START,\ndrivers/net/dsa/motorcomm/chip.c-421-\t\t\t \u0026val);\n--\ndrivers/net/dsa/motorcomm/chip.c=424=static int\ndrivers/net/dsa/motorcomm/chip.c:425:yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)\ndrivers/net/dsa/motorcomm/chip.c-426-{\n--\ndrivers/net/dsa/motorcomm/chip.c-432-\ndrivers/net/dsa/motorcomm/chip.c:433:\tres = yt921x_extif_wait(priv);\ndrivers/net/dsa/motorcomm/chip.c-434-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-440-\t YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ;\ndrivers/net/dsa/motorcomm/chip.c:441:\tres = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);\ndrivers/net/dsa/motorcomm/chip.c-442-\tif (res)\ndrivers/net/dsa/motorcomm/chip.c-443-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:444:\tres = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);\ndrivers/net/dsa/motorcomm/chip.c-445-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-447-\ndrivers/net/dsa/motorcomm/chip.c:448:\tres = yt921x_extif_wait(priv);\ndrivers/net/dsa/motorcomm/chip.c-449-\tif (res)\ndrivers/net/dsa/motorcomm/chip.c-450-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:451:\tres = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, \u0026val);\ndrivers/net/dsa/motorcomm/chip.c-452-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c=463=static int\ndrivers/net/dsa/motorcomm/chip.c:464:yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val)\ndrivers/net/dsa/motorcomm/chip.c-465-{\n--\ndrivers/net/dsa/motorcomm/chip.c-469-\ndrivers/net/dsa/motorcomm/chip.c:470:\tres = yt921x_extif_wait(priv);\ndrivers/net/dsa/motorcomm/chip.c-471-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-477-\t YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE;\ndrivers/net/dsa/motorcomm/chip.c:478:\tres = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);\ndrivers/net/dsa/motorcomm/chip.c-479-\tif (res)\ndrivers/net/dsa/motorcomm/chip.c-480-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:481:\tres = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val);\ndrivers/net/dsa/motorcomm/chip.c-482-\tif (res)\ndrivers/net/dsa/motorcomm/chip.c-483-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:484:\tres = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);\ndrivers/net/dsa/motorcomm/chip.c-485-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-487-\ndrivers/net/dsa/motorcomm/chip.c:488:\treturn yt921x_extif_wait(priv);\ndrivers/net/dsa/motorcomm/chip.c-489-}\ndrivers/net/dsa/motorcomm/chip.c-490-\ndrivers/net/dsa/motorcomm/chip.c:491:static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg)\ndrivers/net/dsa/motorcomm/chip.c-492-{\ndrivers/net/dsa/motorcomm/chip.c:493:\tstruct yt921x_priv *priv = mbus-\u003epriv;\ndrivers/net/dsa/motorcomm/chip.c-494-\tu16 val;\n--\ndrivers/net/dsa/motorcomm/chip.c-497-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:498:\tres = yt921x_extif_read(priv, port, reg, \u0026val);\ndrivers/net/dsa/motorcomm/chip.c-499-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c=506=static int\ndrivers/net/dsa/motorcomm/chip.c:507:yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data)\ndrivers/net/dsa/motorcomm/chip.c-508-{\ndrivers/net/dsa/motorcomm/chip.c:509:\tstruct yt921x_priv *priv = mbus-\u003epriv;\ndrivers/net/dsa/motorcomm/chip.c-510-\tint res;\n--\ndrivers/net/dsa/motorcomm/chip.c-512-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:513:\tres = yt921x_extif_write(priv, port, reg, data);\ndrivers/net/dsa/motorcomm/chip.c-514-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c=519=static int\ndrivers/net/dsa/motorcomm/chip.c:520:yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)\ndrivers/net/dsa/motorcomm/chip.c-521-{\n--\ndrivers/net/dsa/motorcomm/chip.c-533-\t/* TODO: c45? */\ndrivers/net/dsa/motorcomm/chip.c:534:\tmbus-\u003eread = yt921x_mbus_ext_read;\ndrivers/net/dsa/motorcomm/chip.c:535:\tmbus-\u003ewrite = yt921x_mbus_ext_write;\ndrivers/net/dsa/motorcomm/chip.c-536-\tmbus-\u003eparent = dev;\n--\ndrivers/net/dsa/motorcomm/chip.c-547-/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */\ndrivers/net/dsa/motorcomm/chip.c:548:static int yt921x_read_mib(struct yt921x_priv *priv, int port)\ndrivers/net/dsa/motorcomm/chip.c-549-{\ndrivers/net/dsa/motorcomm/chip.c:550:\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\ndrivers/net/dsa/motorcomm/chip.c-551-\tstruct device *dev = to_device(priv);\ndrivers/net/dsa/motorcomm/chip.c:552:\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\ndrivers/net/dsa/motorcomm/chip.c-553-\tint res = 0;\ndrivers/net/dsa/motorcomm/chip.c-554-\ndrivers/net/dsa/motorcomm/chip.c:555:\t/* Reading of yt921x_port::mib is not protected by a lock and it's vain\ndrivers/net/dsa/motorcomm/chip.c-556-\t * to keep its consistency, since we have to read registers one by one\n--\ndrivers/net/dsa/motorcomm/chip.c-562-\ndrivers/net/dsa/motorcomm/chip.c:563:\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\ndrivers/net/dsa/motorcomm/chip.c:564:\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\ndrivers/net/dsa/motorcomm/chip.c-565-\t\tu32 reg = YT921X_MIBn_DATA0(port) + desc-\u003eoffset;\n--\ndrivers/net/dsa/motorcomm/chip.c-569-\ndrivers/net/dsa/motorcomm/chip.c:570:\t\tres = yt921x_reg_read(priv, reg, \u0026val0);\ndrivers/net/dsa/motorcomm/chip.c-571-\t\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-582-\ndrivers/net/dsa/motorcomm/chip.c:583:\t\t\tres = yt921x_reg_read(priv, reg + 4, \u0026val1);\ndrivers/net/dsa/motorcomm/chip.c-584-\t\t\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-606-\ndrivers/net/dsa/motorcomm/chip.c:607:static void yt921x_poll_mib(struct work_struct *work)\ndrivers/net/dsa/motorcomm/chip.c-608-{\ndrivers/net/dsa/motorcomm/chip.c:609:\tstruct yt921x_port *pp = container_of_const(work, struct yt921x_port,\ndrivers/net/dsa/motorcomm/chip.c-610-\t\t\t\t\t\t mib_read.work);\ndrivers/net/dsa/motorcomm/chip.c:611:\tstruct yt921x_priv *priv = (void *)(pp - pp-\u003eindex) -\ndrivers/net/dsa/motorcomm/chip.c:612:\t\t\t\t offsetof(struct yt921x_priv, ports);\ndrivers/net/dsa/motorcomm/chip.c-613-\tunsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;\n--\ndrivers/net/dsa/motorcomm/chip.c-617-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:618:\tres = yt921x_read_mib(priv, port);\ndrivers/net/dsa/motorcomm/chip.c-619-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c=626=static void\ndrivers/net/dsa/motorcomm/chip.c:627:yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,\ndrivers/net/dsa/motorcomm/chip.c-628-\t\t uint8_t *data)\n--\ndrivers/net/dsa/motorcomm/chip.c-632-\ndrivers/net/dsa/motorcomm/chip.c:633:\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\ndrivers/net/dsa/motorcomm/chip.c:634:\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\ndrivers/net/dsa/motorcomm/chip.c-635-\n--\ndrivers/net/dsa/motorcomm/chip.c=641=static void\ndrivers/net/dsa/motorcomm/chip.c:642:yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)\ndrivers/net/dsa/motorcomm/chip.c-643-{\ndrivers/net/dsa/motorcomm/chip.c:644:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c:645:\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\ndrivers/net/dsa/motorcomm/chip.c:646:\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\ndrivers/net/dsa/motorcomm/chip.c-647-\tsize_t j;\n--\ndrivers/net/dsa/motorcomm/chip.c-649-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:650:\tyt921x_read_mib(priv, port);\ndrivers/net/dsa/motorcomm/chip.c-651-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c-653-\tj = 0;\ndrivers/net/dsa/motorcomm/chip.c:654:\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\ndrivers/net/dsa/motorcomm/chip.c:655:\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\ndrivers/net/dsa/motorcomm/chip.c-656-\n--\ndrivers/net/dsa/motorcomm/chip.c-664-\ndrivers/net/dsa/motorcomm/chip.c:665:static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)\ndrivers/net/dsa/motorcomm/chip.c-666-{\n--\ndrivers/net/dsa/motorcomm/chip.c-671-\ndrivers/net/dsa/motorcomm/chip.c:672:\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\ndrivers/net/dsa/motorcomm/chip.c:673:\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\ndrivers/net/dsa/motorcomm/chip.c-674-\n--\ndrivers/net/dsa/motorcomm/chip.c=682=static void\ndrivers/net/dsa/motorcomm/chip.c:683:yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,\ndrivers/net/dsa/motorcomm/chip.c-684-\t\t\t struct ethtool_eth_mac_stats *mac_stats)\ndrivers/net/dsa/motorcomm/chip.c-685-{\ndrivers/net/dsa/motorcomm/chip.c:686:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c:687:\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\ndrivers/net/dsa/motorcomm/chip.c:688:\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\ndrivers/net/dsa/motorcomm/chip.c-689-\ndrivers/net/dsa/motorcomm/chip.c-690-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:691:\tyt921x_read_mib(priv, port);\ndrivers/net/dsa/motorcomm/chip.c-692-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c=718=static void\ndrivers/net/dsa/motorcomm/chip.c:719:yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,\ndrivers/net/dsa/motorcomm/chip.c-720-\t\t\t struct ethtool_eth_ctrl_stats *ctrl_stats)\ndrivers/net/dsa/motorcomm/chip.c-721-{\ndrivers/net/dsa/motorcomm/chip.c:722:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c:723:\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\ndrivers/net/dsa/motorcomm/chip.c:724:\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\ndrivers/net/dsa/motorcomm/chip.c-725-\ndrivers/net/dsa/motorcomm/chip.c-726-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:727:\tyt921x_read_mib(priv, port);\ndrivers/net/dsa/motorcomm/chip.c-728-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c-734-\ndrivers/net/dsa/motorcomm/chip.c:735:static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {\ndrivers/net/dsa/motorcomm/chip.c-736-\t{ 0, 64 },\n--\ndrivers/net/dsa/motorcomm/chip.c=746=static void\ndrivers/net/dsa/motorcomm/chip.c:747:yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,\ndrivers/net/dsa/motorcomm/chip.c-748-\t\t\t struct ethtool_rmon_stats *rmon_stats,\n--\ndrivers/net/dsa/motorcomm/chip.c-750-{\ndrivers/net/dsa/motorcomm/chip.c:751:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c:752:\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\ndrivers/net/dsa/motorcomm/chip.c:753:\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\ndrivers/net/dsa/motorcomm/chip.c-754-\ndrivers/net/dsa/motorcomm/chip.c-755-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:756:\tyt921x_read_mib(priv, port);\ndrivers/net/dsa/motorcomm/chip.c-757-\tmutex_unlock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c-758-\ndrivers/net/dsa/motorcomm/chip.c:759:\t*ranges = yt921x_rmon_ranges;\ndrivers/net/dsa/motorcomm/chip.c-760-\n--\ndrivers/net/dsa/motorcomm/chip.c=783=static void\ndrivers/net/dsa/motorcomm/chip.c:784:yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,\ndrivers/net/dsa/motorcomm/chip.c-785-\t\t struct rtnl_link_stats64 *stats)\ndrivers/net/dsa/motorcomm/chip.c-786-{\ndrivers/net/dsa/motorcomm/chip.c:787:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c:788:\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\ndrivers/net/dsa/motorcomm/chip.c:789:\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\ndrivers/net/dsa/motorcomm/chip.c-790-\n--\ndrivers/net/dsa/motorcomm/chip.c=819=static void\ndrivers/net/dsa/motorcomm/chip.c:820:yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,\ndrivers/net/dsa/motorcomm/chip.c-821-\t\t\t struct ethtool_pause_stats *pause_stats)\ndrivers/net/dsa/motorcomm/chip.c-822-{\ndrivers/net/dsa/motorcomm/chip.c:823:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c:824:\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\ndrivers/net/dsa/motorcomm/chip.c:825:\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\ndrivers/net/dsa/motorcomm/chip.c-826-\ndrivers/net/dsa/motorcomm/chip.c-827-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:828:\tyt921x_read_mib(priv, port);\ndrivers/net/dsa/motorcomm/chip.c-829-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c=835=static int\ndrivers/net/dsa/motorcomm/chip.c:836:yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)\ndrivers/net/dsa/motorcomm/chip.c-837-{\n--\ndrivers/net/dsa/motorcomm/chip.c-849-\tif (!!new_mask != !!priv-\u003eeee_ports_mask) {\ndrivers/net/dsa/motorcomm/chip.c:850:\t\tres = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC,\ndrivers/net/dsa/motorcomm/chip.c-851-\t\t\t\t\t YT921X_PON_STRAP_EEE, !!new_mask);\n--\ndrivers/net/dsa/motorcomm/chip.c-853-\t\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:854:\t\tres = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL,\ndrivers/net/dsa/motorcomm/chip.c-855-\t\t\t\t\t YT921X_PON_STRAP_EEE, !!new_mask);\n--\ndrivers/net/dsa/motorcomm/chip.c-862-\t/* Enable / disable port EEE */\ndrivers/net/dsa/motorcomm/chip.c:863:\tres = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL,\ndrivers/net/dsa/motorcomm/chip.c-864-\t\t\t\t YT921X_EEE_CTRL_ENn(port), enable);\n--\ndrivers/net/dsa/motorcomm/chip.c-866-\t\treturn res;\ndrivers/net/dsa/motorcomm/chip.c:867:\tres = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port),\ndrivers/net/dsa/motorcomm/chip.c-868-\t\t\t\t YT921X_EEE_VAL_DATA, enable);\n--\ndrivers/net/dsa/motorcomm/chip.c=875=static int\ndrivers/net/dsa/motorcomm/chip.c:876:yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)\ndrivers/net/dsa/motorcomm/chip.c-877-{\ndrivers/net/dsa/motorcomm/chip.c:878:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c-879-\tint res;\n--\ndrivers/net/dsa/motorcomm/chip.c-881-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:882:\tres = yt921x_set_eee(priv, port, e);\ndrivers/net/dsa/motorcomm/chip.c-883-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c-887-\ndrivers/net/dsa/motorcomm/chip.c:888:static int yt921x_mtu_fetch(struct yt921x_priv *priv, int port)\ndrivers/net/dsa/motorcomm/chip.c-889-{\n--\ndrivers/net/dsa/motorcomm/chip.c=895=static int\ndrivers/net/dsa/motorcomm/chip.c:896:yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)\ndrivers/net/dsa/motorcomm/chip.c-897-{\n--\ndrivers/net/dsa/motorcomm/chip.c-901-\ndrivers/net/dsa/motorcomm/chip.c:902:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c-903-\tstruct dsa_port *dp = dsa_to_port(ds, port);\n--\ndrivers/net/dsa/motorcomm/chip.c-911-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:912:\tres = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port),\ndrivers/net/dsa/motorcomm/chip.c-913-\t\t\t\t YT921X_MAC_FRAME_SIZE_M,\n--\ndrivers/net/dsa/motorcomm/chip.c-919-\ndrivers/net/dsa/motorcomm/chip.c:920:static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)\ndrivers/net/dsa/motorcomm/chip.c-921-{\n--\ndrivers/net/dsa/motorcomm/chip.c=953=static u64 token2burst(u32 token, int unit, int C)\n--\ndrivers/net/dsa/motorcomm/chip.c-957-\ndrivers/net/dsa/motorcomm/chip.c:958:struct yt921x_marker {\ndrivers/net/dsa/motorcomm/chip.c-959-\tu32 cir;\n--\ndrivers/net/dsa/motorcomm/chip.c=969=static int\ndrivers/net/dsa/motorcomm/chip.c:970:yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst,\ndrivers/net/dsa/motorcomm/chip.c-971-\t\t unsigned int flags, unsigned int slot_ns, u32 cir_max,\ndrivers/net/dsa/motorcomm/chip.c:972:\t\t u32 cbs_max, int unit_max, struct yt921x_priv *priv, int port,\ndrivers/net/dsa/motorcomm/chip.c-973-\t\t struct netlink_ext_ack *extack)\n--\ndrivers/net/dsa/motorcomm/chip.c-977-\tstruct device *dev = to_device(priv);\ndrivers/net/dsa/motorcomm/chip.c:978:\tstruct yt921x_marker m;\ndrivers/net/dsa/motorcomm/chip.c-979-\tu64 burst_est;\n--\ndrivers/net/dsa/motorcomm/chip.c-999-\telse\ndrivers/net/dsa/motorcomm/chip.c:1000:\t\tburst_sug += ETH_HLEN + yt921x_mtu_fetch(priv, port) +\ndrivers/net/dsa/motorcomm/chip.c-1001-\t\t\t ETH_FCS_LEN;\n--\ndrivers/net/dsa/motorcomm/chip.c=1059=static int\ndrivers/net/dsa/motorcomm/chip.c:1060:yt921x_marker_tfm_police(struct yt921x_marker *marker,\ndrivers/net/dsa/motorcomm/chip.c-1061-\t\t\t const struct flow_action_police *police,\ndrivers/net/dsa/motorcomm/chip.c:1062:\t\t\t unsigned int flags, struct yt921x_priv *priv, int port,\ndrivers/net/dsa/motorcomm/chip.c-1063-\t\t\t struct netlink_ext_ack *extack)\n--\ndrivers/net/dsa/motorcomm/chip.c-1073-\ndrivers/net/dsa/motorcomm/chip.c:1074:\treturn yt921x_marker_tfm(marker, rate, burst, flags,\ndrivers/net/dsa/motorcomm/chip.c-1075-\t\t\t\t priv-\u003emeter_slot_ns, YT921X_METER_CIR_MAX,\n--\ndrivers/net/dsa/motorcomm/chip.c=1080=static int\ndrivers/net/dsa/motorcomm/chip.c:1081:yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst,\ndrivers/net/dsa/motorcomm/chip.c:1082:\t\t\tunsigned int flags, struct yt921x_priv *priv, int port,\ndrivers/net/dsa/motorcomm/chip.c-1083-\t\t\tstruct netlink_ext_ack *extack)\ndrivers/net/dsa/motorcomm/chip.c-1084-{\ndrivers/net/dsa/motorcomm/chip.c:1085:\treturn yt921x_marker_tfm(marker, rate, burst, flags,\ndrivers/net/dsa/motorcomm/chip.c-1086-\t\t\t\t priv-\u003eport_shape_slot_ns, YT921X_SHAPE_CIR_MAX,\n--\ndrivers/net/dsa/motorcomm/chip.c=1091=static int\ndrivers/net/dsa/motorcomm/chip.c:1092:yt921x_police_validate(const struct flow_action_police *police,\ndrivers/net/dsa/motorcomm/chip.c-1093-\t\t const struct flow_action *action,\n--\ndrivers/net/dsa/motorcomm/chip.c=1127=static int\ndrivers/net/dsa/motorcomm/chip.c:1128:yt921x_meter_config(struct yt921x_priv *priv, unsigned int id,\ndrivers/net/dsa/motorcomm/chip.c:1129:\t\t const struct yt921x_marker *marker)\ndrivers/net/dsa/motorcomm/chip.c-1130-{\n--\ndrivers/net/dsa/motorcomm/chip.c-1147-\ndrivers/net/dsa/motorcomm/chip.c:1148:\treturn yt921x_reg96_write(priv, YT921X_METERn_CTRL(id), ctrls);\ndrivers/net/dsa/motorcomm/chip.c-1149-}\ndrivers/net/dsa/motorcomm/chip.c-1150-\ndrivers/net/dsa/motorcomm/chip.c:1151:static void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port)\ndrivers/net/dsa/motorcomm/chip.c-1152-{\ndrivers/net/dsa/motorcomm/chip.c:1153:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c-1154-\tstruct device *dev = to_device(priv);\n--\ndrivers/net/dsa/motorcomm/chip.c-1157-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:1158:\tres = yt921x_reg_write(priv, YT921X_PORTn_METER(port), 0);\ndrivers/net/dsa/motorcomm/chip.c-1159-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n--\ndrivers/net/dsa/motorcomm/chip.c=1166=static int\ndrivers/net/dsa/motorcomm/chip.c:1167:yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,\ndrivers/net/dsa/motorcomm/chip.c-1168-\t\t\t const struct flow_action_police *police,\n--\ndrivers/net/dsa/motorcomm/chip.c-1170-{\ndrivers/net/dsa/motorcomm/chip.c:1171:\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\ndrivers/net/dsa/motorcomm/chip.c:1172:\tstruct yt921x_marker marker;\ndrivers/net/dsa/motorcomm/chip.c-1173-\tu32 ctrl;\n--\ndrivers/net/dsa/motorcomm/chip.c-1175-\ndrivers/net/dsa/motorcomm/chip.c:1176:\tres = yt921x_police_validate(police, NULL, NULL, extack);\ndrivers/net/dsa/motorcomm/chip.c-1177-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-1179-\ndrivers/net/dsa/motorcomm/chip.c:1180:\tres = yt921x_marker_tfm_police(\u0026marker, police, 0, priv, port, extack);\ndrivers/net/dsa/motorcomm/chip.c-1181-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-1184-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:1185:\tres = yt921x_meter_config(priv, port + YT921X_METER_NUM, \u0026marker);\ndrivers/net/dsa/motorcomm/chip.c-1186-\tif (res)\n--\ndrivers/net/dsa/motorcomm/chip.c-1189-\tctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN;\ndrivers/net/dsa/motorcomm/chip.c:1190:\tres = yt921x_reg_write(priv, YT921X_PORTn_METER(port), ctrl);\ndrivers/net/dsa/motorcomm/chip.c-1191-end:\n--\n\n"
} |
| 5/2 | 2026/07/03 17:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 34036 output: 0 thoughts: 0 |
| 6/2 | 2026/07/03 17:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 33740 output: 21 thoughts: 659 Thoughts: **Fuzzing Feasibility of Motorcomm LED Driver Patch** Okay, so I need to assess whether this patch series, which adds LED support to the Motorcomm DSA driver (`drivers/net/dsa/motorcomm/leds.c`), introduces functional changes worth fuzzing. It appears to be a straight-forward driver feature, registering LED class devices and handling things like blinking and brightness. Functionally, absolutely yes, this is a code path that needs scrutiny. But the crucial question is, is it *reachable*? This is a DSA switch driver, so it relies on the presence of the switch hardware. Realistically, this means an MDIO bus is involved, and DSA drivers are typically not easily emulated unless there's a specific emulator for the hardware. I need to think about syzkaller's capabilities here. Can it instantiate an MDIO device like this? My understanding is syzkaller *can* fuzz MDIO devices, especially if there's a netlink or sysfs interface involved. And bingo, LED class devices are exposed via `/sys/class/leds/`! So, if the driver probes, the LEDs are registered, and control can be done via sysfs. This is the key. So, can the driver *probe*? It's a `yt921x` device. I need to look at the probe function in `drivers/net/dsa/motorcomm/chip.c` or, the original file, `yt921x.c`. It's MDIO or SPI based. If the device isn't present, the probe fails. No probe, no LEDs. Therefore, it's not present for syzkaller to use. The real crux is: Can syzkaller instantiate this device? I'm thinking of `vhci`, `netdevsim`, and similar, but DSA switches are tricky. I don't think it can just easily instantiate arbitrary MDIO devices unless there's a virtual MDIO bus where we can inject nodes, or maybe using `dsa_loop`. I vaguely recall some mention of device tree injection support in syzkaller, but I doubt it is supported. No, `dsa_loop` is a specific driver, not a general virtualizer. Okay, here's the kicker from the instructions: "Return WorthFuzzing=false if the patch only contains ... Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware". That's what I am worried about. Is this `yt921x` reachable in QEMU? And, the usual answer is no, right? Specific Ethernet switch chips on MDIO/SPI buses are generally *not* emulated in standard QEMU. Unless someone's specifically added emulation for `yt921x`, or there's some clever software fallback, it's a dead end. I need to check. I need to search for the `yt921x` probe function and double-check to be sure, and see if there are any hints about virtual MDIO or software-emulated probing. I need to make sure, so I don't waste time looking for a bug that doesn't exist. |
| 7/2 | 2026/07/03 17:16 | tool | grepper |
0mError:no matches Args: {
"Expression": "yt921x_probe"
}Results: {
"error": "no matches"
} |
| 8/2 | 2026/07/03 17:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35127 output: 37 thoughts: 25 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating the Motorcomm DSA Driver** Alright, let's dive in. I need to understand something about this Motorcomm DSA driver, specifically the implementation within `chip.c`. My first instinct, given what I know about these drivers, is to look for a specific function or structure related to modules, likely the driver's core initialization or configuration logic. I'll search for instances of `module_` – that prefix is a pretty standard indicator of module-related code within the kernel. This usually encompasses functions like module loading/unloading, potentially module parameter handling, or references to the module's structure itself. It should give me a good entry point to understand how this driver is organized and interacts with the kernel's module system. Let's see what we find. |
| 9/2 | 2026/07/03 17:17 | tool | grepper |
0mArgs:{
"Expression": "module_.*driver",
"PathPrefix": "drivers/net/dsa/motorcomm/chip.c"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4768=static struct mdio_driver yt921x_mdio_driver = {\n--\ndrivers/net/dsa/motorcomm/chip.c-4777-\ndrivers/net/dsa/motorcomm/chip.c:4778:mdio_module_driver(yt921x_mdio_driver);\ndrivers/net/dsa/motorcomm/chip.c-4779-\n"
} |
| 10/2 | 2026/07/03 17:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35503 output: 19 thoughts: 254 Thoughts: **Analyzing the MDIO Driver and Its Reachability** Okay, so this is an MDIO driver. My initial thought is, can syzkaller even instantiate MDIO devices directly? Probably not, unless there's some special MDIO-specific fuzzer or some trick with device tree overlay injection. Though, wait, isn't there *some* limited netlink or sysfs support for injecting devices in syzkaller? No, typically MDIO devices are probed through the device tree. Hmm, can I trigger the LED code independently of the device being present? No, because `yt921x_leds_setup` is clearly called from `yt921x_dsa_setup`, which is only triggered during the DSA switch setup. That means, if the device isn't probed, that code's unreachable. Okay, is there any software emulation for the Motorcomm YT9215 that might help? Nothing in the kernel or QEMU, as far as I know. So, in virtualized environments like GCE or QEMU, this code path is pretty much dead. Wait, let me just double-check one more time, maybe there's some obscure way to hit MDIO drivers... Ah, the `dsa_loop` driver, but that's for a fixed dummy switch. Okay, what about `mdio-mockup`, or something similar? Let's quickly verify if that exists. |
| 11/2 | 2026/07/03 17:17 | tool | grepper |
0mError:no matches Args: {
"Expression": "mdio.*mockup"
}Results: {
"error": "no matches"
} |
| 12/2 | 2026/07/03 17:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 36096 output: 43 thoughts: 137 Thoughts: **MDIO Device Instantiation from Userspace: A Deep Dive** Okay, so I'm thinking about MDIO devices and how we might interact with their instantiation from userspace. The immediate instinct is to check if we can directly manipulate `/sys/bus/mdio_bus/devices/...`, but I'm pretty sure that's a dead end. From my experience, MDIO devices are typically brought online by the MDIO bus controller itself, either through hardware probing, reading those crucial PHY ID registers, or through the device tree. The interesting question then becomes: if we're dealing with a virtual MDIO bus, something like `fixed-mdio` or maybe even the MDIO bus within `dsa_loop`, is there a way to trick the system into probing a virtual device? Could we potentially mock the read of the PHY ID register? If we could simulate that read, would it trick the system to load drivers? Now, I need to check the exact driver matching scheme. I'll need to see how the `yt921x_mdio_driver` functions, to see if mocking the PHY ID might work. |
| 13/2 | 2026/07/03 17:17 | tool | grepper |
0mArgs:{
"Expression": "yt921x_mdio_driver",
"PathPrefix": "drivers/net/dsa/motorcomm/chip.c"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4766=MODULE_DEVICE_TABLE(of, yt921x_of_match);\ndrivers/net/dsa/motorcomm/chip.c-4767-\ndrivers/net/dsa/motorcomm/chip.c:4768:static struct mdio_driver yt921x_mdio_driver = {\ndrivers/net/dsa/motorcomm/chip.c-4769-\t.probe = yt921x_mdio_probe,\n--\ndrivers/net/dsa/motorcomm/chip.c-4777-\ndrivers/net/dsa/motorcomm/chip.c:4778:mdio_module_driver(yt921x_mdio_driver);\ndrivers/net/dsa/motorcomm/chip.c-4779-\n"
} |
| 14/2 | 2026/07/03 17:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 36740 output: 42 thoughts: 21 Thoughts: Okay, here's the summary, assuming I'm the one having these thoughts and I am an expert in my field. **Matching Criteria and Table Identification** Alright, let's break this down. My internal process is confirming a match based on the `of_match_table`. That's the key. I'm relying on that specific table, the `yt921x_of_match`, to determine if a match is valid. My focus now is purely on the output of `yt921x_of_match`. |
| 15/2 | 2026/07/03 17:17 | tool | grepper |
0mArgs:{
"Expression": "yt921x_of_match",
"PathPrefix": "drivers/net/dsa/motorcomm/chip.c"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4714=static int yt921x_mdio_probe(struct mdio_device *mdiodev)\n--\ndrivers/net/dsa/motorcomm/chip.c-4761-\ndrivers/net/dsa/motorcomm/chip.c:4762:static const struct of_device_id yt921x_of_match[] = {\ndrivers/net/dsa/motorcomm/chip.c-4763-\t{ .compatible = \"motorcomm,yt9215\" },\n--\ndrivers/net/dsa/motorcomm/chip.c-4765-};\ndrivers/net/dsa/motorcomm/chip.c:4766:MODULE_DEVICE_TABLE(of, yt921x_of_match);\ndrivers/net/dsa/motorcomm/chip.c-4767-\ndrivers/net/dsa/motorcomm/chip.c=4768=static struct mdio_driver yt921x_mdio_driver = {\n--\ndrivers/net/dsa/motorcomm/chip.c-4773-\t\t.name = YT921X_NAME,\ndrivers/net/dsa/motorcomm/chip.c:4774:\t\t.of_match_table = yt921x_of_match,\ndrivers/net/dsa/motorcomm/chip.c-4775-\t},\n"
} |
| 16/2 | 2026/07/03 17:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 37286 output: 43 thoughts: 115 Thoughts: **Analyzing the YT9215 Driver's Probable Behavior** Okay, so the driver in question identifies itself via the device tree compatible string "motorcomm,yt9215". Right now, I can't see this driver getting loaded with the standard Syzkaller setup because it can't inject arbitrary device tree nodes yet. While there's work being done on DT overlays, that's not a go-to solution for every driver. So, initial probing is unlikely. However, let's assume, for the sake of argument, that it *does* somehow manage to probe. If that happens, my immediate next step would be to delve into the `yt921x_mdio_probe` or `yt921x_setup` functions. This is because, even if it's there, the driver's going to attempt to verify the chip ID by reading registers through MDIO. I need to understand that MDIO interaction to see if there are any exploitable vulnerabilities. Gotta follow that MDIO data flow. |
| 17/2 | 2026/07/03 17:17 | tool | grepper |
0mArgs:{
"Expression": "yt921x_mdio_probe",
"PathPrefix": "drivers/net/dsa/motorcomm/chip.c"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4682=static void yt921x_mdio_remove(struct mdio_device *mdiodev)\n--\ndrivers/net/dsa/motorcomm/chip.c-4713-\ndrivers/net/dsa/motorcomm/chip.c:4714:static int yt921x_mdio_probe(struct mdio_device *mdiodev)\ndrivers/net/dsa/motorcomm/chip.c-4715-{\n--\ndrivers/net/dsa/motorcomm/chip.c=4768=static struct mdio_driver yt921x_mdio_driver = {\ndrivers/net/dsa/motorcomm/chip.c:4769:\t.probe = yt921x_mdio_probe,\ndrivers/net/dsa/motorcomm/chip.c-4770-\t.remove = yt921x_mdio_remove,\n"
} |
| 18/2 | 2026/07/03 17:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 37916 output: 42 thoughts: 19 Thoughts: Okay, let's dive into this. Here's my take on `yt921x_mdio_probe` based on what I can see: **Dissecting `yt921x_mdio_probe`** Alright, the objective is pretty clear: I need to understand the purpose of this `yt921x_mdio_probe` function. It seems like it's a critical piece of the puzzle, likely involved in the initialization or setup of something related to MDIO (Management Data Input/Output) communication, specifically for the `yt921x` device. My expertise tells me that MDIO is usually how the system talks to PHYs (Physical Layer Transceivers) and other low-level network interface components. So, I expect this function to be responsible for things like: detecting the PHY, configuring its registers, setting up the necessary clocks or interrupt lines for communication, and finally, getting the device ready for data transfer. I'll need to look deeper into the specifics of *how* it does those things, but that's my working hypothesis. Time to get started. |
| 19/2 | 2026/07/03 17:17 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4714,
"LineCount": 50
}Results: {
"Contents": "4714:\tstatic int yt921x_mdio_probe(struct mdio_device *mdiodev)\n4715:\t{\n4716:\t\tstruct device *dev = \u0026mdiodev-\u003edev;\n4717:\t\tstruct yt921x_reg_mdio *mdio;\n4718:\t\tstruct yt921x_priv *priv;\n4719:\t\tstruct dsa_switch *ds;\n4720:\t\n4721:\t\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n4722:\t\tif (!priv)\n4723:\t\t\treturn -ENOMEM;\n4724:\t\n4725:\t\tmdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);\n4726:\t\tif (!mdio)\n4727:\t\t\treturn -ENOMEM;\n4728:\t\n4729:\t\tmdio-\u003ebus = mdiodev-\u003ebus;\n4730:\t\tmdio-\u003eaddr = mdiodev-\u003eaddr;\n4731:\t\tmdio-\u003eswitchid = 0;\n4732:\t\n4733:\t\tmutex_init(\u0026priv-\u003ereg_lock);\n4734:\t\n4735:\t\tpriv-\u003ereg_ops = \u0026yt921x_reg_ops_mdio;\n4736:\t\tpriv-\u003ereg_ctx = mdio;\n4737:\t\n4738:\t\tfor (size_t i = 0; i \u003c ARRAY_SIZE(priv-\u003eports); i++) {\n4739:\t\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n4740:\t\n4741:\t\t\tpp-\u003eindex = i;\n4742:\t\t\tINIT_DELAYED_WORK(\u0026pp-\u003emib_read, yt921x_poll_mib);\n4743:\t\t}\n4744:\t\n4745:\t\tds = \u0026priv-\u003eds;\n4746:\t\tds-\u003edev = dev;\n4747:\t\tds-\u003eassisted_learning_on_cpu_port = true;\n4748:\t\tds-\u003edscp_prio_mapping_is_global = true;\n4749:\t\tds-\u003epriv = priv;\n4750:\t\tds-\u003eops = \u0026yt921x_dsa_switch_ops;\n4751:\t\tds-\u003eageing_time_min = 1 * 5000;\n4752:\t\tds-\u003eageing_time_max = U16_MAX * 5000;\n4753:\t\tds-\u003ephylink_mac_ops = \u0026yt921x_phylink_mac_ops;\n4754:\t\tds-\u003enum_lag_ids = YT921X_LAG_NUM;\n4755:\t\tds-\u003enum_ports = YT921X_PORT_NUM;\n4756:\t\n4757:\t\tmdiodev_set_drvdata(mdiodev, priv);\n4758:\t\n4759:\t\treturn dsa_register_switch(ds);\n4760:\t}\n4761:\t\n4762:\tstatic const struct of_device_id yt921x_of_match[] = {\n4763:\t\t{ .compatible = \"motorcomm,yt9215\" },\n"
} |
| 20/2 | 2026/07/03 17:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39074 output: 43 thoughts: 62 Thoughts: Okay, let's break this down. **Decoding This DSA Switch Registration** Alright, so the process begins with a call to `dsa_register_switch(ds)`. That's the entry point, the first step in getting this switch integrated. Now, I'm expecting `dsa_register_switch` to then call the setup function specific to the switch hardware, which, in this case, is `yt921x_dsa_setup`. It's likely that `ds->ops` holds the function pointers, so it's calling *that* function, passed through the `ds` structure. Right, `ds->ops` is the key. Now, I need to understand what `yt921x_dsa_setup` actually *does*. Let's delve into that function to see the details of the hardware configuration and initialization. |
| 21/2 | 2026/07/03 17:17 | tool | grepper |
0mArgs:{
"Expression": "yt921x_dsa_setup",
"PathPrefix": "drivers/net/dsa/motorcomm/chip.c"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4495=static int yt921x_chip_setup(struct yt921x_priv *priv)\n--\ndrivers/net/dsa/motorcomm/chip.c-4536-\ndrivers/net/dsa/motorcomm/chip.c:4537:static int yt921x_dsa_setup(struct dsa_switch *ds)\ndrivers/net/dsa/motorcomm/chip.c-4538-{\n--\ndrivers/net/dsa/motorcomm/chip.c=4598=static const struct dsa_switch_ops yt921x_dsa_switch_ops = {\n--\ndrivers/net/dsa/motorcomm/chip.c-4668-\t/* chip */\ndrivers/net/dsa/motorcomm/chip.c:4669:\t.setup\t\t\t= yt921x_dsa_setup,\ndrivers/net/dsa/motorcomm/chip.c-4670-};\n"
} |
| 22/2 | 2026/07/03 17:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39634 output: 42 thoughts: 40 Thoughts: Okay, here's my take on those thoughts, framed from an expert's perspective: **Debugging the Chip Verification Process** Alright, I need to pinpoint where the hardware verification is occurring within the setup routines. My initial thought is to dive into either `yt921x_chip_setup` or `yt921x_dsa_setup`. These are the most likely candidates, given their role in initializing the hardware. I'm assuming that somewhere in one of those, we'll find the logic that's directly interrogating the registers to confirm the chip's presence and proper configuration. This feels like the logical first step in tracking down the verification process. I'll start with those two and see what the code reveals. |
| 23/2 | 2026/07/03 17:17 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4495,
"LineCount": 50
}Results: {
"Contents": "4495:\tstatic int yt921x_chip_setup(struct yt921x_priv *priv)\n4496:\t{\n4497:\t\tu32 ctrl;\n4498:\t\tint res;\n4499:\t\n4500:\t\tctrl = YT921X_FUNC_MIB | YT921X_FUNC_ACL | YT921X_FUNC_METER;\n4501:\t\tres = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl);\n4502:\t\tif (res)\n4503:\t\t\treturn res;\n4504:\t\n4505:\t\tres = yt921x_chip_setup_dsa(priv);\n4506:\t\tif (res)\n4507:\t\t\treturn res;\n4508:\t\n4509:\t\tres = yt921x_chip_setup_tc(priv);\n4510:\t\tif (res)\n4511:\t\t\treturn res;\n4512:\t\n4513:\t\tres = yt921x_chip_setup_acl(priv);\n4514:\t\tif (res)\n4515:\t\t\treturn res;\n4516:\t\n4517:\t#if IS_ENABLED(CONFIG_DCB)\n4518:\t\tres = yt921x_chip_setup_qos(priv);\n4519:\t\tif (res)\n4520:\t\t\treturn res;\n4521:\t#endif\n4522:\t\n4523:\t\t/* Clear MIB */\n4524:\t\tctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT;\n4525:\t\tres = yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl);\n4526:\t\tif (res)\n4527:\t\t\treturn res;\n4528:\t\n4529:\t\t/* Miscellaneous */\n4530:\t\tres = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP);\n4531:\t\tif (res)\n4532:\t\t\treturn res;\n4533:\t\n4534:\t\treturn 0;\n4535:\t}\n4536:\t\n4537:\tstatic int yt921x_dsa_setup(struct dsa_switch *ds)\n4538:\t{\n4539:\t\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n4540:\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\n"
} |
| 24/2 | 2026/07/03 17:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 40635 output: 42 thoughts: 41 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Digging into the YT921x Driver** Hmm, something's not quite right. It looks like the `yt921x_chip_setup` function isn't the one responsible for pulling the chip ID. That's unexpected. My initial assumption was that this would be where it lived. Okay, let's back up a bit and take a look at `yt921x_dsa_setup` instead. It's likely that function is where the relevant initialization and identification steps actually occur. Time to see what's really happening. I need to understand the control flow here, because the architecture seems slightly unusual. |
| 25/2 | 2026/07/03 17:18 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4537,
"LineCount": 50
}Results: {
"Contents": "4537:\tstatic int yt921x_dsa_setup(struct dsa_switch *ds)\n4538:\t{\n4539:\t\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n4540:\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);\n"
} |
| 26/2 | 2026/07/03 17:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 41650 output: 42 thoughts: 24 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing the Chip Reset Function** Okay, so the code's initiating a reset with `yt921x_chip_reset(priv)`. My immediate thought is, let's dive into that function and understand precisely what it's doing. I need to know the implications of this reset – is it a full power cycle, a soft reset, a register clear, or something more nuanced? What's the `priv` argument? I'll need to trace its definition and see what data structure it holds – that'll give me context on what hardware resources this reset might be affecting. I'll be looking for register writes, delays, interrupt disable/enable sequences – anything that might provide insight into the reset's scope and potential impact on device functionality or data integrity. I'll pay close attention to the sequencing, too; the *order* of operations can be as important as the operations themselves. I'm expecting this is probably something fairly standard, but I need to confirm its specific handling of this particular chip. |
| 27/2 | 2026/07/03 17:18 | tool | grepper |
0mArgs:{
"Expression": "yt921x_chip_reset",
"PathPrefix": "drivers/net/dsa/motorcomm/chip.c"
}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"
} |
| 28/2 | 2026/07/03 17:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 42231 output: 42 thoughts: 27 Thoughts: Okay, here's my attempt at summarizing those thoughts from an expert's perspective, using a first-person narrative: **Time to Diagnose: `yt921x` Reset and Detection** Alright, let's dive in. My mind immediately goes to the essentials for a system like this. Before I start going down any rabbit holes with more complex debugging, I'm going to take a first pass on the obvious. The core of my analysis always starts from basics. I need to confirm the device is reset properly, and whether the system is detecting the intended chip. So, first on my list: double check the functions `yt921x_chip_reset` and `yt921x_chip_detect`. I want to make sure the chip is getting a solid reset signal, and that the device detection routine is even picking up on its presence. This gives me a starting point. |
| 29/2 | 2026/07/03 17:18 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4227,
"LineCount": 50
}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;\n"
} |
| 30/2 | 2026/07/03 17:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 43296 output: 42 thoughts: 80 Thoughts: Okay, here's my interpretation of that logic, as if I were processing it myself: **Initial Assessment: yt921x Chip Detection** Alright, let's break this down. From what I'm seeing, this `yt921x_chip_detect` function is essentially the gatekeeper for our YT921X chip's presence. It's tasked with identifying the chip model. Specifically, it's grabbing the value of `YT921X_CHIP_ID`. That's the key identifier, the chip's "fingerprint" so to speak. Then, this value is compared with the set of expected identifiers defined in `yt921x_infos`. If the grabbed ID doesn't jive with any of the expected values, that's a red flag. The function signals a serious problem, returning `-ENODEV`, indicating that the device isn't the one we're expecting or it's simply not detected. This is a critical check. Now, a related question pops into mind. I need to understand the full context of how this detection is used. Is `yt921x_chip_detect` invoked from `yt921x_chip_reset`? Understanding this relationship is critical to figuring out the overall logic. Specifically, how do these functions work together? Is the `chip_detect` part of the overall chip reset? Does the reset function require a known, detectable chip? Does the reset function occur regardless? Knowing this will help me trace the execution path and any potential dependencies. |
| 31/2 | 2026/07/03 17:18 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4294,
"LineCount": 50
}Results: {
"Contents": "4294:\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,\n4327:\t\t\t\tETH_P_YT921X);\n4328:\t\t\t/* Despite being possible, we choose not to set CPU_TAG_TPID,\n4329:\t\t\t * since there is no way it can be different unless you have the\n4330:\t\t\t * wrong chip.\n4331:\t\t\t */\n4332:\t\t\treturn -EINVAL;\n4333:\t\t}\n4334:\t\n4335:\t\treturn 0;\n4336:\t}\n4337:\t\n4338:\tstatic int yt921x_chip_setup_dsa(struct yt921x_priv *priv)\n4339:\t{\n4340:\t\tstruct dsa_switch *ds = \u0026priv-\u003eds;\n4341:\t\tunsigned long cpu_ports_mask;\n4342:\t\tu32 ctrls[2];\n4343:\t\tu32 ctrl;\n"
} |
| 32/2 | 2026/07/03 17:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 44561 output: 18 thoughts: 180 Thoughts: **Analyzing the `yt921x_chip_reset` Function Call** Okay, so `yt921x_chip_reset` definitely calls `yt921x_chip_detect`, I can see that. And if `yt921x_chip_detect` can't identify the chip, it's going to return `-ENODEV`. Makes perfect sense. Now, the problem is, syzkaller is running in a virtual environment – something like QEMU or GCE, right? We don't *actually* have this specific piece of hardware in the system. Which means those MDIO reads are either going to completely fail, or return some garbage value, probably 0 or maybe `0xffff`. Either way, it's not going to match `YT9215_MAJOR`, whatever that defines. But wait a minute... can syzkaller *mock* those MDIO reads? That would be the ideal situation. I don't recall seeing a generic MDIO mock driver in the base syzkaller setup that allows injecting arbitrary read values for arbitrary PHYs or devices unless one has been specifically written for it. I don't recall seeing it. Hold on, there's `mdio-mockup`, right? I'd better double-check to see if `mdio-mockup` or potentially even `mdio-fault-inject` exists. That could be a game-changer. |
| 33/2 | 2026/07/03 17:18 | tool | grepper |
0mError:no matches Args: {
"Expression": "mdio.*mock"
}Results: {
"error": "no matches"
} |
| 34/2 | 2026/07/03 17:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 45081 output: 19 thoughts: 17 Thoughts: Okay, let's see. My first thought is: **Exploring the Fuzzing Landscape**. I need to quickly assess the fuzzing landscape for anything specifically targeting `mdio`. My expertise tells me that this is the first crucial step. Understanding what's already been explored, what's been tried, and what vulnerabilities may have been unearthed is vital before I even begin to consider my own approach. I want to avoid duplicating effort and, more importantly, ensure I'm building on existing knowledge. Fuzzing is a deep field and I need a baseline before deciding if I'm reinventing the wheel. |
| 35/2 | 2026/07/03 17:19 | tool | grepper |
0mError:no matches Args: {
"Expression": "mdio.*fuzz"
}Results: {
"error": "no matches"
} |
| 36/2 | 2026/07/03 17:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 45258 output: 24 thoughts: 431 Thoughts: **Analyzing the `yt921x` Driver Probe and LED Setup** Okay, so I'm trying to understand the flow here, specifically how the LED code in `yt921x` is reached, and I'm a bit hung up. Let me break this down logically. First, I'm thinking about `dsa_loop`. It's a mock DSA switch, but it uses its own driver, `dsa_loop.c`, not the `yt921x` driver. That's important. My initial understanding is that the `yt921x` driver should only probe if two conditions are met: a device tree node with `motorcomm,yt9215` exists, *and* the MDIO bus returns the correct chip ID. Right, makes sense. Given the typical syzkaller environment, I know we don't have this specific hardware, and there isn't any software emulation of it, so my gut tells me the driver's probe will *always* fail at `yt921x_chip_detect`. The question then becomes, can the LED code ever execute without a successful probe? The sequence appears to be: `yt921x_leds_setup` is called from `yt921x_dsa_setup`, which is called by `dsa_register_switch`, which, in turn, is called at the end of `yt921x_mdio_probe`. So if `yt921x_chip_detect` fails, then `yt921x_dsa_setup` will fail and return an error and subsequently, `dsa_register_switch` will fail. I've got that part. Hold on, I'm seeing a potential inconsistency. I had a quick mental hiccup, but I remember it's `yt921x_dsa_setup` that is called *by* `dsa_register_switch`. That's where I need to reconfirm. Let's go to the source, either `net/dsa/dsa.c` or `net/dsa/dsa2.c`. Got it, `dsa_register_switch` calls `ds->ops->setup(ds)`. If that `setup` function fails, the switch is not registered. And since `yt921x_chip_detect` is part of `yt921x_dsa_setup`, the check will fail there. Therefore, my LED setup code (`yt921x_leds_setup`) is never reached. Alright, I'm pretty comfortable with that conclusion. But... it's always a good idea to double check, just to be certain. Let me make sure there are no other potential call paths to `yt921x_leds_setup`. Always good to be thorough! |
| 37/2 | 2026/07/03 17:19 | tool | grepper |
0mArgs:{
"Expression": "yt921x_leds_setup"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4537=static int yt921x_dsa_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-4583-#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.c:4584:\tres = yt921x_leds_setup(priv);\ndrivers/net/dsa/motorcomm/chip.c-4585-\tif (res)\n--\ndrivers/net/dsa/motorcomm/leds.c=499=static void yt921x_leds_teardown(struct yt921x_priv *priv)\n--\ndrivers/net/dsa/motorcomm/leds.c-535-\ndrivers/net/dsa/motorcomm/leds.c:536:int yt921x_leds_setup(struct yt921x_priv *priv)\ndrivers/net/dsa/motorcomm/leds.c-537-{\n--\ndrivers/net/dsa/motorcomm/leds.h=96=struct yt921x_led {\n--\ndrivers/net/dsa/motorcomm/leds.h-106-\ndrivers/net/dsa/motorcomm/leds.h:107:int yt921x_leds_setup(struct yt921x_priv *priv);\ndrivers/net/dsa/motorcomm/leds.h-108-\n--\ndrivers/net/dsa/motorcomm/leds.h-110-\ndrivers/net/dsa/motorcomm/leds.h:111:static inline int yt921x_leds_setup(struct yt921x_priv *priv)\ndrivers/net/dsa/motorcomm/leds.h-112-{\n"
} |
| 38/2 | 2026/07/03 17:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 46820 output: 134 thoughts: 1051 Thoughts: **WorthFuzzing Assessment: `false`** Okay, let's break this down. My understanding is that `yt921x_leds_setup` is *only* called from `yt921x_dsa_setup`, and that function is only invoked as the `.setup` callback within the `dsa_switch_ops` structure. The `.setup` callback itself is called during DSA tree instantiation, which hinges on the switch being registered, and critically, the hardware being present *and* detected (or, at a bare minimum, the `setup` callback not returning an error). Since we're dealing with a specific piece of hardware – the Motorcomm YT9215 ethernet switch – and that hardware check, specifically `yt921x_chip_detect`, will definitely fail and return `-ENODEV` if the chip isn't there. So, `yt921x_leds_setup` is completely unreachable in any virtualized environment without the physical hardware. Now, the instructions are clear: if the patch only contains unreachable code paths in virtualized environments (even with software emulated hardware), then we return `WorthFuzzing=false`. This is a hardware-specific driver, no software emulation exists, so the code is unreachable. Simple. I've checked if there's any way around the hardware check. Any debugfs/sysfs interface for bypass? Nope. Is `yt921x_leds_setup` used anywhere else? No, it's specific to this driver. Is there an emulator for this chip? I couldn't find one. Can syzkaller somehow mock the MDIO read? Well, no, syzkaller doesn't have a generic MDIO mock that can be configured from userspace to return arbitrary values, and even if it did, it would require a highly specific sequence of register values, which syzkaller wouldn't know automatically. Alright, let me double-check: is `CONFIG_NET_DSA_YT921X` even enabled in standard syzkaller configurations? It might be, but it still won't probe without the hardware. I have also verified the "WorthFuzzing=true" condition in the instructions, and it definitely applies, because the code isn't reachable. I also checked which files were touched. Aside from the documentation and build system changes, the main changes are: a refactoring of the existing `yt921x` driver, and the addition of `leds.c` and `leds.h`, moving some `smi` functions into `smi.c` and `smi.h`, and adding the call to `yt921x_leds_setup`. The LED-specific logic is now there, but the fundamental problem remains: it's tied to this specific hardware. So, the patch is just refactoring and adding LED support to the `yt921x` driver. Since that driver requires specific hardware not emulated in QEMU, the new code is unreachable. Therefore, and for all those reasons, my assessment is `WorthFuzzing=false`. We're dealing with hardware-specific code that's fundamentally impossible to reach in the virtualized environment that we're dealing with. |
| 39/2 | 2026/07/03 17:19 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch refactors the Motorcomm YT9215 DSA switch driver and adds LED support for it. This is a hardware-specific driver that requires the physical Motorcomm switch chip to be present on an MDIO bus to probe successfully (it reads the chip ID via MDIO in `yt921x_chip_detect`). Since there is no software emulation for this hardware in QEMU/syzkaller, the modified code paths are impossible to reach in a virtualized fuzzing environment.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch refactors the Motorcomm YT9215 DSA switch driver and adds LED support for it. This is a hardware-specific driver that requires the physical Motorcomm switch chip to be present on an MDIO bus to probe successfully (it reads the chip ID via MDIO in `yt921x_chip_detect`). Since there is no software emulation for this hardware in QEMU/syzkaller, the modified code paths are impossible to reach in a virtualized fuzzing environment.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|