| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/16 20:39 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch adds LED support for the Motorcomm YT9215 ethernet switch chip (a DSA driver). This is a hardware-specific driver that communicates with a physical chip via MDIO. Since there is no software emulation for this specific switch chip in standard QEMU/virtualized environments, the driver will fail to probe (e.g., during chip ID detection), and the newly added LED setup code will be unreachable.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/16 20:39 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 2860983d3f4de01f9fce3760ff31992914ad9103\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sun Aug 16 20:38:59 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml b/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml\nindex 33a6552e46fc0..93ba5b181205b 100644\n--- a/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml\n+++ b/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml\n@@ -21,6 +21,9 @@ description: |\n \n Any port can be used as the CPU port.\n \n+ Each port has at most 3 LEDs connected and can be declared using the standard\n+ LEDs structure.\n+\n properties:\n compatible:\n const: motorcomm,yt9215\n@@ -59,6 +62,7 @@ unevaluatedProperties: false\n examples:\n - |\n #include \u003cdt-bindings/gpio/gpio.h\u003e\n+ #include \u003cdt-bindings/leds/common.h\u003e\n \n mdio {\n #address-cells = \u003c1\u003e;\n@@ -113,6 +117,25 @@ examples:\n label = \"lan1\";\n phy-mode = \"internal\";\n phy-handle = \u003c\u0026sw_phy0\u003e;\n+\n+ leds {\n+ #address-cells = \u003c1\u003e;\n+ #size-cells = \u003c0\u003e;\n+\n+ led@0 {\n+ reg = \u003c0\u003e;\n+ color = \u003cLED_COLOR_ID_WHITE\u003e;\n+ function = LED_FUNCTION_LAN;\n+ default-state = \"keep\";\n+ };\n+\n+ led@1 {\n+ reg = \u003c1\u003e;\n+ color = \u003cLED_COLOR_ID_AMBER\u003e;\n+ function = LED_FUNCTION_LAN;\n+ default-state = \"keep\";\n+ };\n+ };\n };\n \n ethernet-port@1 {\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 991460050da74..55cc5ac0e86f0 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -18256,7 +18256,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 4ab567c5bbafc..98e9bbe47de7b 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 d2975badffc09..138225baa4d56 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 0000000000000..3438e0b143611\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 0000000000000..5a63db0029ffe\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 159b16606f6ca..553efacd4f5ee 100644\n--- a/drivers/net/dsa/yt921x.c\n+++ b/drivers/net/dsa/motorcomm/chip.c\n@@ -13,7 +13,6 @@\n #include \u003clinux/if_bridge.h\u003e\n #include \u003clinux/if_hsr.h\u003e\n #include \u003clinux/if_vlan.h\u003e\n-#include \u003clinux/iopoll.h\u003e\n #include \u003clinux/mdio.h\u003e\n #include \u003clinux/module.h\u003e\n #include \u003clinux/of.h\u003e\n@@ -26,7 +25,9 @@\n #include \u003cnet/ieee8021q.h\u003e\n #include \u003cnet/pkt_cls.h\u003e\n \n-#include \"yt921x.h\"\n+#include \"chip.h\"\n+#include \"leds.h\"\n+#include \"smi.h\"\n \n struct yt921x_mib_desc {\n \tunsigned int size;\n@@ -151,13 +152,8 @@ static const struct yt921x_info yt921x_infos[] = {\n \t{}\n };\n \n-#define YT921X_NAME\t\"yt921x\"\n-\n #define YT921X_VID_UNWARE\t4095\n \n-#define YT921X_POLL_SLEEP_US\t10000\n-#define YT921X_POLL_TIMEOUT_US\t100000\n-\n /* The interval should be small enough to avoid overflow of 32bit MIBs.\n *\n * Until we can read MIBs from stats64 call directly (i.e. sleep\n@@ -196,208 +192,6 @@ static u32 ethaddr_lo2_to_u32(const unsigned char *addr)\n \treturn (addr[4] \u003c\u003c 8) | addr[5];\n }\n \n-static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)\n-{\n-\tWARN_ON(!mutex_is_locked(\u0026priv-\u003ereg_lock));\n-\n-\treturn priv-\u003ereg_ops-\u003eread(priv-\u003ereg_ctx, reg, valp);\n-}\n-\n-static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)\n-{\n-\tWARN_ON(!mutex_is_locked(\u0026priv-\u003ereg_lock));\n-\n-\treturn priv-\u003ereg_ops-\u003ewrite(priv-\u003ereg_ctx, reg, val);\n-}\n-\n-static int\n-yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)\n-{\n-\tu32 val;\n-\tint res;\n-\tint ret;\n-\n-\tret = read_poll_timeout(yt921x_reg_read, res,\n-\t\t\t\tres || (val \u0026 mask) == *valp,\n-\t\t\t\tYT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,\n-\t\t\t\tfalse, priv, reg, \u0026val);\n-\tif (ret)\n-\t\treturn ret;\n-\tif (res)\n-\t\treturn res;\n-\n-\t*valp = val;\n-\treturn 0;\n-}\n-\n-static int\n-yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)\n-{\n-\tint res;\n-\tu32 v;\n-\tu32 u;\n-\n-\tres = yt921x_reg_read(priv, reg, \u0026v);\n-\tif (res)\n-\t\treturn res;\n-\n-\tu = v;\n-\tu \u0026= ~mask;\n-\tu |= val;\n-\tif (u == v)\n-\t\treturn 0;\n-\n-\treturn yt921x_reg_write(priv, reg, u);\n-}\n-\n-static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)\n-{\n-\treturn yt921x_reg_update_bits(priv, reg, 0, mask);\n-}\n-\n-static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)\n-{\n-\treturn yt921x_reg_update_bits(priv, reg, mask, 0);\n-}\n-\n-static int\n-yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)\n-{\n-\treturn yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);\n-}\n-\n-/* Some multi-word registers, like VLANn_CTRL, should be treated as a single\n- * long register. More specifically, writes to parts of its words won't become\n- * visible, until the last word is written.\n- *\n- * Here we require full read and write operations over these registers to\n- * eliminate potential issues, although partial reads/writes are also possible.\n- */\n-\n-static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)\n-{\n-\t*lo \u0026= ~lower_32_bits(mask);\n-\t*hi \u0026= ~upper_32_bits(mask);\n-\t*lo |= lower_32_bits(val);\n-\t*hi |= upper_32_bits(val);\n-}\n-\n-static int\n-yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,\n-\t\t unsigned int num_regs)\n-{\n-\tint res;\n-\n-\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n-\t\tres = yt921x_reg_read(priv, reg + 4 * i, \u0026vals[i]);\n-\t\tif (res)\n-\t\t\treturn res;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int\n-yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,\n-\t\t unsigned int num_regs)\n-{\n-\tint res;\n-\n-\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n-\t\tres = yt921x_reg_write(priv, reg + 4 * i, vals[i]);\n-\t\tif (res)\n-\t\t\treturn res;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int\n-yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n-\t\t\tconst u32 *vals, unsigned int num_regs)\n-{\n-\tbool changed = false;\n-\tu32 vs[4];\n-\tint res;\n-\n-\tBUILD_BUG_ON(num_regs \u003e ARRAY_SIZE(vs));\n-\n-\tres = yt921x_regs_read(priv, reg, vs, num_regs);\n-\tif (res)\n-\t\treturn res;\n-\n-\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n-\t\tu32 u = vs[i];\n-\n-\t\tu \u0026= ~masks[i];\n-\t\tu |= vals[i];\n-\t\tif (u != vs[i])\n-\t\t\tchanged = true;\n-\n-\t\tvs[i] = u;\n-\t}\n-\n-\tif (!changed)\n-\t\treturn 0;\n-\n-\treturn yt921x_regs_write(priv, reg, vs, num_regs);\n-}\n-\n-static int\n-yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n-\t\t unsigned int num_regs)\n-{\n-\tbool changed = false;\n-\tu32 vs[4];\n-\tint res;\n-\n-\tBUILD_BUG_ON(num_regs \u003e ARRAY_SIZE(vs));\n-\n-\tres = yt921x_regs_read(priv, reg, vs, num_regs);\n-\tif (res)\n-\t\treturn res;\n-\n-\tfor (unsigned int i = 0; i \u003c num_regs; i++) {\n-\t\tu32 u = vs[i];\n-\n-\t\tu \u0026= ~masks[i];\n-\t\tif (u != vs[i])\n-\t\t\tchanged = true;\n-\n-\t\tvs[i] = u;\n-\t}\n-\n-\tif (!changed)\n-\t\treturn 0;\n-\n-\treturn yt921x_regs_write(priv, reg, vs, num_regs);\n-}\n-\n-static int\n-yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)\n-{\n-\treturn yt921x_regs_write(priv, reg, vals, 2);\n-}\n-\n-static int\n-yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,\n-\t\t\t const u32 *vals)\n-{\n-\treturn yt921x_regs_update_bits(priv, reg, masks, vals, 2);\n-}\n-\n-static int\n-yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)\n-{\n-\treturn yt921x_regs_clear_bits(priv, reg, masks, 2);\n-}\n-\n-static int\n-yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)\n-{\n-\treturn yt921x_regs_write(priv, reg, vals, 3);\n-}\n-\n static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)\n {\n \tstruct yt921x_reg_mdio *mdio = context;\n@@ -4786,6 +4580,12 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)\n \tif (res)\n \t\treturn res;\n \n+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n+\tres = yt921x_leds_setup(priv);\n+\tif (res)\n+\t\tdev_warn(dev, \"Failed to setup LEDs: %d\\n\", res);\n+#endif\n+\n \treturn 0;\n }\n \n@@ -4886,6 +4686,10 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)\n \tif (!priv)\n \t\treturn;\n \n+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n+\tyt921x_leds_remove(priv);\n+#endif\n+\n \tfor (size_t i = ARRAY_SIZE(priv-\u003eports); i-- \u003e 0; ) {\n \t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n \ndiff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/motorcomm/chip.h\nsimilarity index 99%\nrename from drivers/net/dsa/yt921x.h\nrename to drivers/net/dsa/motorcomm/chip.h\nindex 5f3b99e189c41..e6815cf9cb3cd 100644\n--- a/drivers/net/dsa/yt921x.h\n+++ b/drivers/net/dsa/motorcomm/chip.h\n@@ -850,9 +850,14 @@ 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+#define YT921X_LED_PORT_NUM\t10\n+\n /* 8 internal + 2 external + 1 mcu */\n #define YT921X_PORT_NUM\t\t\t11\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) ((port) == 8 || (port) == 9)\n \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 0000000000000..e7ef0ef80a266\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/leds.c\n@@ -0,0 +1,654 @@\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+struct yt921x_led_trigger_map {\n+\tunsigned long flags;\n+\tu32 mask;\n+};\n+\n+static const struct yt921x_led_trigger_map yt921x_led_trigger_maps[] = {\n+\t{BIT(TRIGGER_NETDEV_LINK),\n+\t YT921X_LEDx_PORT_ACT_DUPLEX_HALF | YT921X_LEDx_PORT_ACT_DUPLEX_FULL},\n+\t{BIT(TRIGGER_NETDEV_LINK_10), YT921X_LEDx_PORT_ACT_10M},\n+\t{BIT(TRIGGER_NETDEV_LINK_100), YT921X_LEDx_PORT_ACT_100M},\n+\t{BIT(TRIGGER_NETDEV_LINK_1000), YT921X_LEDx_PORT_ACT_1000M},\n+\t{BIT(TRIGGER_NETDEV_HALF_DUPLEX), YT921X_LEDx_PORT_ACT_DUPLEX_HALF},\n+\t{BIT(TRIGGER_NETDEV_FULL_DUPLEX), YT921X_LEDx_PORT_ACT_DUPLEX_FULL},\n+\t{BIT(TRIGGER_NETDEV_TX), YT921X_LEDx_PORT_ACT_TX_BLINK},\n+\t{BIT(TRIGGER_NETDEV_RX), YT921X_LEDx_PORT_ACT_RX_BLINK},\n+\t{BIT(TRIGGER_NETDEV_TX_ERR) | BIT(TRIGGER_NETDEV_RX_ERR),\n+\t YT921X_LEDx_PORT_ACT_COLLISION_BLINK},\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+\tif (!flags)\n+\t\treturn true;\n+\n+\tfor (unsigned int i = 0; i \u003c ARRAY_SIZE(yt921x_led_trigger_maps); i++) {\n+\t\tconst struct yt921x_led_trigger_map *map = \u0026yt921x_led_trigger_maps[i];\n+\n+\t\tif ((flags \u0026 map-\u003eflags) == map-\u003eflags) {\n+\t\t\tflags \u0026= ~map-\u003eflags;\n+\t\t\tif (!flags)\n+\t\t\t\treturn true;\n+\t\t}\n+\t}\n+\n+\treturn false;\n+}\n+\n+static int\n+yt921x_led_trigger_get(struct yt921x_priv *priv, int port, int group,\n+\t\t unsigned long *flagsp)\n+{\n+\tu32 addr;\n+\tu32 val;\n+\tint res;\n+\n+\tres = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), \u0026val);\n+\tif (res)\n+\t\treturn res;\n+\n+\tif ((val \u0026 YT921X_LED2_PORT_FORCEn_M(group)) !=\n+\t YT921X_LED2_PORT_FORCEn_DONTCARE(group)) {\n+\t\t*flagsp = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tif (group != 2) {\n+\t\taddr = yt921x_led_regaddr(priv, port, group);\n+\t\tres = yt921x_reg_read(priv, addr, \u0026val);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\t*flagsp = 0;\n+\tfor (unsigned int i = 0; i \u003c ARRAY_SIZE(yt921x_led_trigger_maps); i++) {\n+\t\tconst struct yt921x_led_trigger_map *map = \u0026yt921x_led_trigger_maps[i];\n+\n+\t\tif ((val \u0026 map-\u003emask) == map-\u003emask)\n+\t\t\t*flagsp |= map-\u003eflags;\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+\tu32 addr;\n+\tu32 ctrl;\n+\tu32 mask;\n+\tint res;\n+\n+\tctrl = 0;\n+\tfor (unsigned int i = 0; i \u003c ARRAY_SIZE(yt921x_led_trigger_maps); i++) {\n+\t\tconst struct yt921x_led_trigger_map *map = \u0026yt921x_led_trigger_maps[i];\n+\n+\t\tif ((flags \u0026 map-\u003eflags) == map-\u003eflags) {\n+\t\t\tflags \u0026= ~map-\u003eflags;\n+\t\t\tctrl |= map-\u003emask;\n+\t\t\tif (!flags)\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (flags)\n+\t\treturn -EOPNOTSUPP;\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)\n+\t\treturn NULL;\n+\n+\tif (dsa_port_is_user(dp))\n+\t\treturn \u0026dp-\u003euser-\u003edev;\n+\tif (dsa_port_is_cpu(dp))\n+\t\treturn \u0026dp-\u003econduit-\u003edev;\n+\n+\treturn NULL;\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)\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+\tu32 group;\n+\tu32 mask;\n+\tu32 ctrl;\n+\tu32 val;\n+\tbool on;\n+\tint res;\n+\tint ret;\n+\n+\tres = fwnode_property_read_u32(fwnode, \"reg\", \u0026group);\n+\tif (res)\n+\t\treturn res;\n+\tif (group \u003e= YT921X_LED_GROUP_NUM) {\n+\t\tdev_err(dev, \"Invalid LED reg %u for port %d\\n\", group, port);\n+\t\treturn -EINVAL;\n+\t}\n+\tif (pp-\u003eleds[group])\n+\t\treturn -EEXIST;\n+\n+\tforce_high = fwnode_property_read_bool(fwnode, \"active-high\");\n+\tforce_low = fwnode_property_read_bool(fwnode, \"active-low\");\n+\tif (force_high \u0026\u0026 force_low) {\n+\t\tdev_err(dev, \"Duplicate polarities for LED %02d:%02u\\n\",\n+\t\t\tport, group);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tled = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);\n+\tif (!led)\n+\t\treturn -ENOMEM;\n+\tled-\u003eport = pp;\n+\tled-\u003egroup = group;\n+\n+\tstate = led_init_default_state_get(fwnode);\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\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 (force_high || force_low) {\n+\t\tres = yt921x_reg_read(priv, YT921X_LED_PAR_INV, \u0026val);\n+\t\tif (res)\n+\t\t\tgoto unlock;\n+\n+\t\tmask = YT921X_LED_PAR_INV_INVnm(group, port);\n+\t\tctrl = force_high ? val | mask : val \u0026 ~mask;\n+\t\tres = yt921x_reg_write(priv, YT921X_LED_PAR_INV, ctrl);\n+\t\tif (res)\n+\t\t\tgoto unlock;\n+\t}\n+\n+\tpp-\u003eleds[group] = led;\n+\n+\tswitch (state) {\n+\tcase LEDS_DEFSTATE_OFF:\n+\tcase LEDS_DEFSTATE_ON:\n+\tdefault:\n+\t\ton = state == LEDS_DEFSTATE_ON;\n+\t\tres = yt921x_led_force_set(priv, port, group, on);\n+\t\tbreak;\n+\tcase LEDS_DEFSTATE_KEEP:\n+\t\tres = yt921x_led_force_get(priv, port, group, \u0026on);\n+\t\tbreak;\n+\t}\n+\n+unlock:\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.default_label = \":port\",\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+\treturn 0;\n+\n+err:\n+\tret = res;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tif (pp-\u003eleds[group]) {\n+\t\tif (force_high || force_low) {\n+\t\t\tres = yt921x_reg_write(priv, YT921X_LED_PAR_INV, val);\n+\t\t\tif (res)\n+\t\t\t\tdev_warn(dev,\n+\t\t\t\t\t \"Failed to revoke initialization for LED %02d:%02u\\n\",\n+\t\t\t\t\t port, group);\n+\t\t}\n+\n+\t\tpp-\u003eleds[group] = NULL;\n+\t}\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\n+\tdevm_kfree(dev, led);\n+\treturn ret;\n+}\n+\n+static void yt921x_leds_remove_port(struct yt921x_priv *priv, int port)\n+{\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct device *dev = to_device(priv);\n+\tbool has_leds = false;\n+\n+\tfor (int group = 0; group \u003c YT921X_LED_GROUP_NUM; group++) {\n+\t\tstruct yt921x_led *led = pp-\u003eleds[group];\n+\n+\t\tif (led) {\n+\t\t\tdevm_led_classdev_unregister(dev, \u0026led-\u003ecdev);\n+\t\t\thas_leds = true;\n+\t\t}\n+\t}\n+\n+\tif (!has_leds)\n+\t\treturn;\n+\n+\t/* Now no one can access the LED control blocks, free them without\n+\t * holding the lock.\n+\t */\n+\tfor (int group = 0; group \u003c YT921X_LED_GROUP_NUM; group++) {\n+\t\tstruct yt921x_led *led = pp-\u003eleds[group];\n+\n+\t\tif (led) {\n+\t\t\tpp-\u003eleds[group] = NULL;\n+\t\t\tdevm_kfree(dev, led);\n+\t\t}\n+\t}\n+}\n+\n+static int yt921x_leds_setup_port(struct yt921x_priv *priv, int port)\n+{\n+\tstruct device *dev = to_device(priv);\n+\tstruct dsa_switch *ds = \u0026priv-\u003eds;\n+\tstruct device_node *leds_np;\n+\tstruct dsa_port *dp;\n+\n+\tdp = dsa_to_port(ds, port);\n+\tleds_np = of_get_child_by_name(dp-\u003edn, \"leds\");\n+\tif (!leds_np)\n+\t\treturn 0;\n+\n+\tif (port \u003e= YT921X_LED_PORT_NUM) {\n+\t\tdev_err(dev, \"Cannot configure LEDs for port %d\\n\", port);\n+\t\tof_node_put(leds_np);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor_each_child_of_node_scoped(leds_np, led_np) {\n+\t\tyt921x_led_setup(priv, port, of_fwnode_handle(led_np));\n+\t\t/* Allow partial configuration: LEDs are optional */\n+\t}\n+\n+\tof_node_put(leds_np);\n+\treturn 0;\n+}\n+\n+void yt921x_leds_remove(struct yt921x_priv *priv)\n+{\n+\tfor (int port = 0; port \u003c YT921X_LED_PORT_NUM; port++)\n+\t\tyt921x_leds_remove_port(priv, port);\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+\n+\t/* LEDs are always enabled. There is no way to disable them altogether\n+\t * (as far as I know).\n+\t */\n+\n+\tdsa_switch_for_each_port(dp, ds) {\n+\t\tint port = dp-\u003eindex;\n+\n+\t\tif (!dp-\u003edn)\n+\t\t\tcontinue;\n+\n+\t\tyt921x_leds_setup_port(priv, port);\n+\t\t/* Allow partial configuration: LEDs are optional */\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/net/dsa/motorcomm/leds.h b/drivers/net/dsa/motorcomm/leds.h\nnew file mode 100644\nindex 0000000000000..3c3642988ac77\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/leds.h\n@@ -0,0 +1,121 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#ifndef _YT_LEDS_H\n+#define _YT_LEDS_H\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/bits.h\u003e\n+#include \u003clinux/kconfig.h\u003e\n+#include \u003clinux/leds.h\u003e\n+\n+#define YT921X_LED_CTRL\t\t\t0xd0000\n+#define YT921X_LED_CTRL_UNK\t\t\tBIT(21)\n+#define YT921X_LED_CTRL_LOOPDETECT_BLINK_M\tGENMASK(20, 19)\t/* cycle = 512 * x ms */\n+#define YT921X_LED_CTRL_LOOPDETECT_BLINK(x)\t\tFIELD_PREP(YT921X_LED_CTRL_LOOPDETECT_BLINK_M, (x))\n+#define YT921X_LED_CTRL_PORT_NUM_M\t\tGENMASK(16, 13)\n+#define YT921X_LED_CTRL_PORT_NUM(x)\t\t\tFIELD_PREP(YT921X_LED_CTRL_PORT_NUM_M, (x))\n+#define YT921X_LED_CTRL_MODE_M\t\t\tGENMASK(1, 0)\n+#define YT921X_LED_CTRL_MODE(x)\t\t\tFIELD_PREP(YT921X_LED_CTRL_MODE_M, (x))\n+#define YT921X_LED_CTRL_MODE_PARALLEL\t\t\tYT921X_LED_CTRL_MODE(0)\n+#define YT921X_LED_CTRL_MODE_SERIAL\t\t\tYT921X_LED_CTRL_MODE(2)\n+#define YT921X_LED0_PORTn(port)\t\t(0xd0004 + 4 * (port))\n+#define YT921X_LED0_PORT_ACT_M\t\t\tGENMASK(17, 0)\n+#define YT921X_LED0_PORT_ACT_LINK_TRY_DIS\tBIT(17)\n+#define YT921X_LED0_PORT_ACT_COLLISION_BLINK_INDI\tBIT(16)\n+#define YT921X_LED1_PORTn(port)\t\t(0xd0040 + 4 * (port))\n+#define YT921X_LED1_PORT_OTHER_BLINK_M\t\tGENMASK(31, 30)\t/* cycle = 512 \u003e\u003e x ms */\n+#define YT921X_LED1_PORT_OTHER_BLINK(x)\t\tFIELD_PREP(YT921X_LED1_PORT_OTHER_BLINK_M, (x))\n+#define YT921X_LED1_PORT_EEE_BLINK_M\t\tGENMASK(29, 28)\t/* cycle = 512 \u003e\u003e x ms */\n+#define YT921X_LED1_PORT_EEE_BLINK(x)\t\t\tFIELD_PREP(YT921X_LED1_PORT_EEE_BLINK_M, (x))\n+#define YT921X_LED1_PORT_BLINK_DUTY_COMP\tBIT(27)\n+#define YT921X_LED1_PORT_BLINK_DUTY_M\t\tGENMASK(26, 25)\n+#define YT921X_LED1_PORT_BLINK_DUTY(x)\t\tFIELD_PREP(YT921X_LED1_PORT_BLINK_DUTY_M, (x))\n+#define YT921X_LED1_PORT_BLINK_DUTY_1_2\t\tYT921X_LED1_PORT_BLINK_DUTY(0)\n+#define YT921X_LED1_PORT_BLINK_DUTY_2_3\t\tYT921X_LED1_PORT_BLINK_DUTY(1)\n+#define YT921X_LED1_PORT_BLINK_DUTY_3_4\t\tYT921X_LED1_PORT_BLINK_DUTY(2)\n+#define YT921X_LED1_PORT_BLINK_DUTY_5_6\t\tYT921X_LED1_PORT_BLINK_DUTY(3)\n+#define YT921X_LED2_PORTn(port)\t\t(0xd0080 + 4 * (port))\n+#define YT921X_LED2_PORT_FORCEn_M(grp)\t\tGENMASK(4 * (grp) + 19, 4 * (grp) + 18)\n+#define YT921X_LED2_PORT_FORCEn(grp, x)\t\t((x) \u003c\u003c (4 * (grp) + 18))\n+#define YT921X_LED2_PORT_FORCEn_DONTCARE(grp)\t\tYT921X_LED2_PORT_FORCEn(grp, 0)\n+#define YT921X_LED2_PORT_FORCEn_BLINK(grp)\t\tYT921X_LED2_PORT_FORCEn(grp, 1)\n+#define YT921X_LED2_PORT_FORCEn_ON(grp)\t\tYT921X_LED2_PORT_FORCEn(grp, 2)\n+#define YT921X_LED2_PORT_FORCEn_OFF(grp)\t\tYT921X_LED2_PORT_FORCEn(grp, 3)\n+#define YT921X_LED2_PORT_FORCE_BLINKn_M(grp)\tGENMASK(4 * (grp) + 17, 4 * (grp) + 16)\t/* cycle = 512 \u003c\u003c x ms */\n+#define YT921X_LED2_PORT_FORCE_BLINKn(grp, x)\t\t((x) \u003c\u003c (4 * (grp) + 16))\n+#define YT921X_LED2_PORT_FORCE_BLINKn_OTHER(grp)\tYT921X_LED2_PORT_FORCE_BLINKn(grp, 3)\n+#define YT921X_LEDx_PORT_ACT_M\t\t\tGENMASK(15, 0)\n+#define YT921X_LEDx_PORT_ACT_EEE_BLINK\t\tBIT(15)\n+#define YT921X_LEDx_PORT_ACT_LOOPDETECT_BLINK\tBIT(14)\n+#define YT921X_LEDx_PORT_ACT_ACTIVE_BLINK\tBIT(13)\n+#define YT921X_LEDx_PORT_ACT_DUPLEX_FULL\tBIT(12)\n+#define YT921X_LEDx_PORT_ACT_DUPLEX_HALF\tBIT(11)\n+#define YT921X_LEDx_PORT_ACT_TX_BLINK\t\tBIT(10)\n+#define YT921X_LEDx_PORT_ACT_RX_BLINK\t\tBIT(9)\n+#define YT921X_LEDx_PORT_ACT_TX\t\tBIT(8)\n+#define YT921X_LEDx_PORT_ACT_RX\t\tBIT(7)\n+#define YT921X_LEDx_PORT_ACT_1000M\t\tBIT(6)\n+#define YT921X_LEDx_PORT_ACT_100M\t\tBIT(5)\n+#define YT921X_LEDx_PORT_ACT_10M\t\tBIT(4)\n+#define YT921X_LEDx_PORT_ACT_COLLISION_BLINK\tBIT(3)\n+#define YT921X_LEDx_PORT_ACT_1000M_BLINK\tBIT(2)\n+#define YT921X_LEDx_PORT_ACT_100M_BLINK\tBIT(1)\n+#define YT921X_LEDx_PORT_ACT_10M_BLINK\t\tBIT(0)\n+#define YT921X_LED_SER_CTRL\t\t0xd0100\n+#define YT921X_LED_SER_CTRL_UNK\t\tGENMASK(25, 24)\t/* delay? */\n+#define YT921X_LED_SER_CTRL_ACTIVE_LOW\t\tBIT(4)\n+#define YT921X_LED_SER_CTRL_GRP_NUM_M\t\tGENMASK(1, 0)\t/* #grp - 1 */\n+#define YT921X_LED_SER_CTRL_GRP_NUM(x)\t\tFIELD_PREP(YT921X_LED_SER_CTRL_GRP_NUM_M, (x))\n+#define YT921X_LED_SER_MAPnm(grp, port)\t(0xd0104 + 8 * (2 - (grp)) + 4 * ((port) / 5))\n+#define YT921X_LED_SER_MAP_DSTn_PORT_M(port)\tGENMASK(6 * ((port) % 5) + 5, 6 * ((port) % 5) + 2)\n+#define YT921X_LED_SER_MAP_DSTn_PORT(port, x)\t\t((x) \u003c\u003c (6 * ((port) % 5) + 2))\n+#define YT921X_LED_SER_MAP_DSTn_LED_M(port)\tGENMASK(6 * ((port) % 5) + 1, 6 * ((port) % 5))\n+#define YT921X_LED_SER_MAP_DSTn_LED(port, x)\t\t((x) \u003c\u003c (6 * ((port) % 5)))\n+#define YT921X_LED_PAR_PORTS\t\t0xd01c4\n+#define YT921X_LED_PAR_INV\t\t0xd01c8\n+#define YT921X_LED_PAR_INV_INVnm(grp, port)\tBIT(10 * (grp) + (port))\n+#define YT921X_LED_PAR_MAPn(port)\t(0xd01d0 + 4 * (port))\n+#define YT921X_LED_PAR_MAP_DSTn_PORT_M(grp)\tGENMASK(6 * (grp) + 5, 6 * (grp) + 2)\n+#define YT921X_LED_PAR_MAP_DSTn_PORT(grp, x)\t\t((x) \u003c\u003c (6 * (grp) + 2))\n+#define YT921X_LED_PAR_MAP_DSTn_LED_M(grp)\tGENMASK(6 * (grp) + 1, 6 * (grp))\n+#define YT921X_LED_PAR_MAP_DSTn_LED(grp, x)\t\t((x) \u003c\u003c (6 * (grp)))\n+\n+#define YT921X_LED_BLINK_MIN\t64\n+#define YT921X_LED_BLINK_DEF\t512\n+#define YT921X_LED_BLINK_MAX\t2048\n+\n+/* 2 * lcm(2, 3, 4, 6) */\n+#define YT921X_LED_DUTY_DENOM\t\t24\n+#define YT921X_LED_DUTY(nom, denom)\t(YT921X_LED_DUTY_DENOM * (nom) / (denom))\n+\n+struct yt921x_priv;\n+\n+struct yt921x_led {\n+\tstruct led_classdev cdev;\n+\tstruct yt921x_port *port;\n+\tunsigned char group;\n+\n+\tbool use_cycle:1;\n+\tbool use_duty:1;\n+};\n+\n+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\n+\n+void yt921x_leds_remove(struct yt921x_priv *priv);\n+int yt921x_leds_setup(struct yt921x_priv *priv);\n+\n+#else\n+\n+static inline void yt921x_leds_remove(struct yt921x_priv *priv) {}\n+\n+static inline int yt921x_leds_setup(struct yt921x_priv *priv)\n+{\n+\treturn 0;\n+}\n+\n+#endif\n+\n+#endif\ndiff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c\nnew file mode 100644\nindex 0000000000000..164c25ef494a1\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+\tWARN_ON(!mutex_is_locked(\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+\tWARN_ON(!mutex_is_locked(\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 0000000000000..2995e91f3b3d1\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/smi.h\n@@ -0,0 +1,92 @@\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+/* yt921x_regs_*() are exported for linkage only. Do not use them directly. Use\n+ * yt921x_reg64_*() or yt921x_reg96_*() instead.\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/08/16 20:39 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds LED support for the Motorcomm YT9215 ethernet switch chip (a DSA driver). This is a hardware-specific driver that communicates with a physical chip via MDIO. Since there is no software emulation for this specific switch chip in standard QEMU/virtualized environments, the driver will fail to probe (e.g., during chip ID detection), and the newly added LED setup code will be unreachable.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 2860983d3f4de01f9fce3760ff31992914ad9103
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Aug 16 20:38:59 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml b/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml
index 33a6552e46fc0..93ba5b181205b 100644
--- a/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml
+++ b/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml
@@ -21,6 +21,9 @@ description: |
Any port can be used as the CPU port.
+ Each port has at most 3 LEDs connected and can be declared using the standard
+ LEDs structure.
+
properties:
compatible:
const: motorcomm,yt9215
@@ -59,6 +62,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/leds/common.h>
mdio {
#address-cells = <1>;
@@ -113,6 +117,25 @@ examples:
label = "lan1";
phy-mode = "internal";
phy-handle = <&sw_phy0>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@0 {
+ reg = <0>;
+ color = <LED_COLOR_ID_WHITE>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+
+ led@1 {
+ reg = <1>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+ };
};
ethernet-port@1 {
diff --git a/MAINTAINERS b/MAINTAINERS
index 991460050da74..55cc5ac0e86f0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18256,7 +18256,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 4ab567c5bbafc..98e9bbe47de7b 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 d2975badffc09..138225baa4d56 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 0000000000000..3438e0b143611
--- /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 0000000000000..5a63db0029ffe
--- /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 159b16606f6ca..553efacd4f5ee 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -13,7 +13,6 @@
#include <linux/if_bridge.h>
#include <linux/if_hsr.h>
#include <linux/if_vlan.h>
-#include <linux/iopoll.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -26,7 +25,9 @@
#include <net/ieee8021q.h>
#include <net/pkt_cls.h>
-#include "yt921x.h"
+#include "chip.h"
+#include "leds.h"
+#include "smi.h"
struct yt921x_mib_desc {
unsigned int size;
@@ -151,13 +152,8 @@ static const struct yt921x_info yt921x_infos[] = {
{}
};
-#define YT921X_NAME "yt921x"
-
#define YT921X_VID_UNWARE 4095
-#define YT921X_POLL_SLEEP_US 10000
-#define YT921X_POLL_TIMEOUT_US 100000
-
/* The interval should be small enough to avoid overflow of 32bit MIBs.
*
* Until we can read MIBs from stats64 call directly (i.e. sleep
@@ -196,208 +192,6 @@ static u32 ethaddr_lo2_to_u32(const unsigned char *addr)
return (addr[4] << 8) | addr[5];
}
-static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
-{
- WARN_ON(!mutex_is_locked(&priv->reg_lock));
-
- return priv->reg_ops->read(priv->reg_ctx, reg, valp);
-}
-
-static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
-{
- WARN_ON(!mutex_is_locked(&priv->reg_lock));
-
- return priv->reg_ops->write(priv->reg_ctx, reg, val);
-}
-
-static int
-yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
-{
- u32 val;
- int res;
- int ret;
-
- ret = read_poll_timeout(yt921x_reg_read, res,
- res || (val & mask) == *valp,
- YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
- false, priv, reg, &val);
- if (ret)
- return ret;
- if (res)
- return res;
-
- *valp = val;
- return 0;
-}
-
-static int
-yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
-{
- int res;
- u32 v;
- u32 u;
-
- res = yt921x_reg_read(priv, reg, &v);
- if (res)
- return res;
-
- u = v;
- u &= ~mask;
- u |= val;
- if (u == v)
- return 0;
-
- return yt921x_reg_write(priv, reg, u);
-}
-
-static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
-{
- return yt921x_reg_update_bits(priv, reg, 0, mask);
-}
-
-static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
-{
- return yt921x_reg_update_bits(priv, reg, mask, 0);
-}
-
-static int
-yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
-{
- return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
-}
-
-/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
- * long register. More specifically, writes to parts of its words won't become
- * visible, until the last word is written.
- *
- * Here we require full read and write operations over these registers to
- * eliminate potential issues, although partial reads/writes are also possible.
- */
-
-static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)
-{
- *lo &= ~lower_32_bits(mask);
- *hi &= ~upper_32_bits(mask);
- *lo |= lower_32_bits(val);
- *hi |= upper_32_bits(val);
-}
-
-static int
-yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
- unsigned int num_regs)
-{
- int res;
-
- for (unsigned int i = 0; i < num_regs; i++) {
- res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]);
- if (res)
- return res;
- }
-
- return 0;
-}
-
-static int
-yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
- unsigned int num_regs)
-{
- int res;
-
- for (unsigned int i = 0; i < num_regs; i++) {
- res = yt921x_reg_write(priv, reg + 4 * i, vals[i]);
- if (res)
- return res;
- }
-
- return 0;
-}
-
-static int
-yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
- const u32 *vals, unsigned int num_regs)
-{
- bool changed = false;
- u32 vs[4];
- int res;
-
- BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
-
- res = yt921x_regs_read(priv, reg, vs, num_regs);
- if (res)
- return res;
-
- for (unsigned int i = 0; i < num_regs; i++) {
- u32 u = vs[i];
-
- u &= ~masks[i];
- u |= vals[i];
- if (u != vs[i])
- changed = true;
-
- vs[i] = u;
- }
-
- if (!changed)
- return 0;
-
- return yt921x_regs_write(priv, reg, vs, num_regs);
-}
-
-static int
-yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
- unsigned int num_regs)
-{
- bool changed = false;
- u32 vs[4];
- int res;
-
- BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
-
- res = yt921x_regs_read(priv, reg, vs, num_regs);
- if (res)
- return res;
-
- for (unsigned int i = 0; i < num_regs; i++) {
- u32 u = vs[i];
-
- u &= ~masks[i];
- if (u != vs[i])
- changed = true;
-
- vs[i] = u;
- }
-
- if (!changed)
- return 0;
-
- return yt921x_regs_write(priv, reg, vs, num_regs);
-}
-
-static int
-yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
-{
- return yt921x_regs_write(priv, reg, vals, 2);
-}
-
-static int
-yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
- const u32 *vals)
-{
- return yt921x_regs_update_bits(priv, reg, masks, vals, 2);
-}
-
-static int
-yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)
-{
- return yt921x_regs_clear_bits(priv, reg, masks, 2);
-}
-
-static int
-yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
-{
- return yt921x_regs_write(priv, reg, vals, 3);
-}
-
static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
{
struct yt921x_reg_mdio *mdio = context;
@@ -4786,6 +4580,12 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)
if (res)
return res;
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+ res = yt921x_leds_setup(priv);
+ if (res)
+ dev_warn(dev, "Failed to setup LEDs: %d\n", res);
+#endif
+
return 0;
}
@@ -4886,6 +4686,10 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)
if (!priv)
return;
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+ yt921x_leds_remove(priv);
+#endif
+
for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
struct yt921x_port *pp = &priv->ports[i];
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/motorcomm/chip.h
similarity index 99%
rename from drivers/net/dsa/yt921x.h
rename to drivers/net/dsa/motorcomm/chip.h
index 5f3b99e189c41..e6815cf9cb3cd 100644
--- a/drivers/net/dsa/yt921x.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -850,9 +850,14 @@ 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
+#define YT921X_LED_PORT_NUM 10
+
/* 8 internal + 2 external + 1 mcu */
#define YT921X_PORT_NUM 11
+#define YT921X_NAME "yt921x"
+
#define yt921x_port_is_internal(port) ((port) < 8)
#define yt921x_port_is_external(port) ((port) == 8 || (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 0000000000000..e7ef0ef80a266
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/leds.c
@@ -0,0 +1,654 @@
+// 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;
+}
+
+struct yt921x_led_trigger_map {
+ unsigned long flags;
+ u32 mask;
+};
+
+static const struct yt921x_led_trigger_map yt921x_led_trigger_maps[] = {
+ {BIT(TRIGGER_NETDEV_LINK),
+ YT921X_LEDx_PORT_ACT_DUPLEX_HALF | YT921X_LEDx_PORT_ACT_DUPLEX_FULL},
+ {BIT(TRIGGER_NETDEV_LINK_10), YT921X_LEDx_PORT_ACT_10M},
+ {BIT(TRIGGER_NETDEV_LINK_100), YT921X_LEDx_PORT_ACT_100M},
+ {BIT(TRIGGER_NETDEV_LINK_1000), YT921X_LEDx_PORT_ACT_1000M},
+ {BIT(TRIGGER_NETDEV_HALF_DUPLEX), YT921X_LEDx_PORT_ACT_DUPLEX_HALF},
+ {BIT(TRIGGER_NETDEV_FULL_DUPLEX), YT921X_LEDx_PORT_ACT_DUPLEX_FULL},
+ {BIT(TRIGGER_NETDEV_TX), YT921X_LEDx_PORT_ACT_TX_BLINK},
+ {BIT(TRIGGER_NETDEV_RX), YT921X_LEDx_PORT_ACT_RX_BLINK},
+ {BIT(TRIGGER_NETDEV_TX_ERR) | BIT(TRIGGER_NETDEV_RX_ERR),
+ YT921X_LEDx_PORT_ACT_COLLISION_BLINK},
+};
+
+static bool
+yt921x_led_trigger_is_supported(const struct yt921x_priv *priv, int port,
+ int group, unsigned long flags)
+{
+ if (!flags)
+ return true;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(yt921x_led_trigger_maps); i++) {
+ const struct yt921x_led_trigger_map *map = &yt921x_led_trigger_maps[i];
+
+ if ((flags & map->flags) == map->flags) {
+ flags &= ~map->flags;
+ if (!flags)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int
+yt921x_led_trigger_get(struct yt921x_priv *priv, int port, int group,
+ unsigned long *flagsp)
+{
+ u32 addr;
+ u32 val;
+ int res;
+
+ res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val);
+ if (res)
+ return res;
+
+ if ((val & YT921X_LED2_PORT_FORCEn_M(group)) !=
+ YT921X_LED2_PORT_FORCEn_DONTCARE(group)) {
+ *flagsp = 0;
+ return 0;
+ }
+
+ if (group != 2) {
+ addr = yt921x_led_regaddr(priv, port, group);
+ res = yt921x_reg_read(priv, addr, &val);
+ if (res)
+ return res;
+ }
+
+ *flagsp = 0;
+ for (unsigned int i = 0; i < ARRAY_SIZE(yt921x_led_trigger_maps); i++) {
+ const struct yt921x_led_trigger_map *map = &yt921x_led_trigger_maps[i];
+
+ if ((val & map->mask) == map->mask)
+ *flagsp |= map->flags;
+ }
+
+ 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];
+ u32 addr;
+ u32 ctrl;
+ u32 mask;
+ int res;
+
+ ctrl = 0;
+ for (unsigned int i = 0; i < ARRAY_SIZE(yt921x_led_trigger_maps); i++) {
+ const struct yt921x_led_trigger_map *map = &yt921x_led_trigger_maps[i];
+
+ if ((flags & map->flags) == map->flags) {
+ flags &= ~map->flags;
+ ctrl |= map->mask;
+ if (!flags)
+ break;
+ }
+ }
+ if (flags)
+ return -EOPNOTSUPP;
+
+ 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)
+ return NULL;
+
+ if (dsa_port_is_user(dp))
+ return &dp->user->dev;
+ if (dsa_port_is_cpu(dp))
+ return &dp->conduit->dev;
+
+ return NULL;
+}
+
+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)
+{
+ 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;
+ u32 group;
+ u32 mask;
+ u32 ctrl;
+ u32 val;
+ bool on;
+ int res;
+ int ret;
+
+ res = fwnode_property_read_u32(fwnode, "reg", &group);
+ if (res)
+ return res;
+ if (group >= YT921X_LED_GROUP_NUM) {
+ dev_err(dev, "Invalid LED reg %u for port %d\n", group, port);
+ return -EINVAL;
+ }
+ if (pp->leds[group])
+ return -EEXIST;
+
+ force_high = fwnode_property_read_bool(fwnode, "active-high");
+ force_low = fwnode_property_read_bool(fwnode, "active-low");
+ if (force_high && force_low) {
+ dev_err(dev, "Duplicate polarities for LED %02d:%02u\n",
+ port, group);
+ return -EINVAL;
+ }
+
+ led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+ led->port = pp;
+ led->group = group;
+
+ state = led_init_default_state_get(fwnode);
+
+ mutex_lock(&priv->reg_lock);
+
+ /* Inversion is internal - force on will give low logic.
+ * In the rest of the file, treat LEDs as if active-low.
+ */
+ if (force_high || force_low) {
+ res = yt921x_reg_read(priv, YT921X_LED_PAR_INV, &val);
+ if (res)
+ goto unlock;
+
+ mask = YT921X_LED_PAR_INV_INVnm(group, port);
+ ctrl = force_high ? val | mask : val & ~mask;
+ res = yt921x_reg_write(priv, YT921X_LED_PAR_INV, ctrl);
+ if (res)
+ goto unlock;
+ }
+
+ pp->leds[group] = led;
+
+ switch (state) {
+ case LEDS_DEFSTATE_OFF:
+ case LEDS_DEFSTATE_ON:
+ default:
+ on = state == LEDS_DEFSTATE_ON;
+ res = yt921x_led_force_set(priv, port, group, on);
+ break;
+ case LEDS_DEFSTATE_KEEP:
+ res = yt921x_led_force_get(priv, port, group, &on);
+ break;
+ }
+
+unlock:
+ 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,
+ .default_label = ":port",
+ .devicename = name,
+ .devname_mandatory = true,
+ };
+ res = devm_led_classdev_register_ext(dev, led_cdev, &init_data);
+ if (res)
+ goto err;
+
+ return 0;
+
+err:
+ ret = res;
+
+ mutex_lock(&priv->reg_lock);
+ if (pp->leds[group]) {
+ if (force_high || force_low) {
+ res = yt921x_reg_write(priv, YT921X_LED_PAR_INV, val);
+ if (res)
+ dev_warn(dev,
+ "Failed to revoke initialization for LED %02d:%02u\n",
+ port, group);
+ }
+
+ pp->leds[group] = NULL;
+ }
+ mutex_unlock(&priv->reg_lock);
+
+ devm_kfree(dev, led);
+ return ret;
+}
+
+static void yt921x_leds_remove_port(struct yt921x_priv *priv, int port)
+{
+ struct yt921x_port *pp = &priv->ports[port];
+ struct device *dev = to_device(priv);
+ bool has_leds = false;
+
+ for (int group = 0; group < YT921X_LED_GROUP_NUM; group++) {
+ struct yt921x_led *led = pp->leds[group];
+
+ if (led) {
+ devm_led_classdev_unregister(dev, &led->cdev);
+ has_leds = true;
+ }
+ }
+
+ if (!has_leds)
+ return;
+
+ /* Now no one can access the LED control blocks, free them without
+ * holding the lock.
+ */
+ for (int group = 0; group < YT921X_LED_GROUP_NUM; group++) {
+ struct yt921x_led *led = pp->leds[group];
+
+ if (led) {
+ pp->leds[group] = NULL;
+ devm_kfree(dev, led);
+ }
+ }
+}
+
+static int yt921x_leds_setup_port(struct yt921x_priv *priv, int port)
+{
+ struct device *dev = to_device(priv);
+ struct dsa_switch *ds = &priv->ds;
+ struct device_node *leds_np;
+ struct dsa_port *dp;
+
+ dp = dsa_to_port(ds, port);
+ leds_np = of_get_child_by_name(dp->dn, "leds");
+ if (!leds_np)
+ return 0;
+
+ if (port >= YT921X_LED_PORT_NUM) {
+ dev_err(dev, "Cannot configure LEDs for port %d\n", port);
+ of_node_put(leds_np);
+ return -EINVAL;
+ }
+
+ for_each_child_of_node_scoped(leds_np, led_np) {
+ yt921x_led_setup(priv, port, of_fwnode_handle(led_np));
+ /* Allow partial configuration: LEDs are optional */
+ }
+
+ of_node_put(leds_np);
+ return 0;
+}
+
+void yt921x_leds_remove(struct yt921x_priv *priv)
+{
+ for (int port = 0; port < YT921X_LED_PORT_NUM; port++)
+ yt921x_leds_remove_port(priv, port);
+}
+
+int yt921x_leds_setup(struct yt921x_priv *priv)
+{
+ struct dsa_switch *ds = &priv->ds;
+ struct dsa_port *dp;
+
+ /* LEDs are always enabled. There is no way to disable them altogether
+ * (as far as I know).
+ */
+
+ dsa_switch_for_each_port(dp, ds) {
+ int port = dp->index;
+
+ if (!dp->dn)
+ continue;
+
+ yt921x_leds_setup_port(priv, port);
+ /* Allow partial configuration: LEDs are optional */
+ }
+
+ return 0;
+}
diff --git a/drivers/net/dsa/motorcomm/leds.h b/drivers/net/dsa/motorcomm/leds.h
new file mode 100644
index 0000000000000..3c3642988ac77
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/leds.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_LEDS_H
+#define _YT_LEDS_H
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/kconfig.h>
+#include <linux/leds.h>
+
+#define YT921X_LED_CTRL 0xd0000
+#define YT921X_LED_CTRL_UNK BIT(21)
+#define YT921X_LED_CTRL_LOOPDETECT_BLINK_M GENMASK(20, 19) /* cycle = 512 * x ms */
+#define YT921X_LED_CTRL_LOOPDETECT_BLINK(x) FIELD_PREP(YT921X_LED_CTRL_LOOPDETECT_BLINK_M, (x))
+#define YT921X_LED_CTRL_PORT_NUM_M GENMASK(16, 13)
+#define YT921X_LED_CTRL_PORT_NUM(x) FIELD_PREP(YT921X_LED_CTRL_PORT_NUM_M, (x))
+#define YT921X_LED_CTRL_MODE_M GENMASK(1, 0)
+#define YT921X_LED_CTRL_MODE(x) FIELD_PREP(YT921X_LED_CTRL_MODE_M, (x))
+#define YT921X_LED_CTRL_MODE_PARALLEL YT921X_LED_CTRL_MODE(0)
+#define YT921X_LED_CTRL_MODE_SERIAL YT921X_LED_CTRL_MODE(2)
+#define YT921X_LED0_PORTn(port) (0xd0004 + 4 * (port))
+#define YT921X_LED0_PORT_ACT_M GENMASK(17, 0)
+#define YT921X_LED0_PORT_ACT_LINK_TRY_DIS BIT(17)
+#define YT921X_LED0_PORT_ACT_COLLISION_BLINK_INDI BIT(16)
+#define YT921X_LED1_PORTn(port) (0xd0040 + 4 * (port))
+#define YT921X_LED1_PORT_OTHER_BLINK_M GENMASK(31, 30) /* cycle = 512 >> x ms */
+#define YT921X_LED1_PORT_OTHER_BLINK(x) FIELD_PREP(YT921X_LED1_PORT_OTHER_BLINK_M, (x))
+#define YT921X_LED1_PORT_EEE_BLINK_M GENMASK(29, 28) /* cycle = 512 >> x ms */
+#define YT921X_LED1_PORT_EEE_BLINK(x) FIELD_PREP(YT921X_LED1_PORT_EEE_BLINK_M, (x))
+#define YT921X_LED1_PORT_BLINK_DUTY_COMP BIT(27)
+#define YT921X_LED1_PORT_BLINK_DUTY_M GENMASK(26, 25)
+#define YT921X_LED1_PORT_BLINK_DUTY(x) FIELD_PREP(YT921X_LED1_PORT_BLINK_DUTY_M, (x))
+#define YT921X_LED1_PORT_BLINK_DUTY_1_2 YT921X_LED1_PORT_BLINK_DUTY(0)
+#define YT921X_LED1_PORT_BLINK_DUTY_2_3 YT921X_LED1_PORT_BLINK_DUTY(1)
+#define YT921X_LED1_PORT_BLINK_DUTY_3_4 YT921X_LED1_PORT_BLINK_DUTY(2)
+#define YT921X_LED1_PORT_BLINK_DUTY_5_6 YT921X_LED1_PORT_BLINK_DUTY(3)
+#define YT921X_LED2_PORTn(port) (0xd0080 + 4 * (port))
+#define YT921X_LED2_PORT_FORCEn_M(grp) GENMASK(4 * (grp) + 19, 4 * (grp) + 18)
+#define YT921X_LED2_PORT_FORCEn(grp, x) ((x) << (4 * (grp) + 18))
+#define YT921X_LED2_PORT_FORCEn_DONTCARE(grp) YT921X_LED2_PORT_FORCEn(grp, 0)
+#define YT921X_LED2_PORT_FORCEn_BLINK(grp) YT921X_LED2_PORT_FORCEn(grp, 1)
+#define YT921X_LED2_PORT_FORCEn_ON(grp) YT921X_LED2_PORT_FORCEn(grp, 2)
+#define YT921X_LED2_PORT_FORCEn_OFF(grp) YT921X_LED2_PORT_FORCEn(grp, 3)
+#define YT921X_LED2_PORT_FORCE_BLINKn_M(grp) GENMASK(4 * (grp) + 17, 4 * (grp) + 16) /* cycle = 512 << x ms */
+#define YT921X_LED2_PORT_FORCE_BLINKn(grp, x) ((x) << (4 * (grp) + 16))
+#define YT921X_LED2_PORT_FORCE_BLINKn_OTHER(grp) YT921X_LED2_PORT_FORCE_BLINKn(grp, 3)
+#define YT921X_LEDx_PORT_ACT_M GENMASK(15, 0)
+#define YT921X_LEDx_PORT_ACT_EEE_BLINK BIT(15)
+#define YT921X_LEDx_PORT_ACT_LOOPDETECT_BLINK BIT(14)
+#define YT921X_LEDx_PORT_ACT_ACTIVE_BLINK BIT(13)
+#define YT921X_LEDx_PORT_ACT_DUPLEX_FULL BIT(12)
+#define YT921X_LEDx_PORT_ACT_DUPLEX_HALF BIT(11)
+#define YT921X_LEDx_PORT_ACT_TX_BLINK BIT(10)
+#define YT921X_LEDx_PORT_ACT_RX_BLINK BIT(9)
+#define YT921X_LEDx_PORT_ACT_TX BIT(8)
+#define YT921X_LEDx_PORT_ACT_RX BIT(7)
+#define YT921X_LEDx_PORT_ACT_1000M BIT(6)
+#define YT921X_LEDx_PORT_ACT_100M BIT(5)
+#define YT921X_LEDx_PORT_ACT_10M BIT(4)
+#define YT921X_LEDx_PORT_ACT_COLLISION_BLINK BIT(3)
+#define YT921X_LEDx_PORT_ACT_1000M_BLINK BIT(2)
+#define YT921X_LEDx_PORT_ACT_100M_BLINK BIT(1)
+#define YT921X_LEDx_PORT_ACT_10M_BLINK BIT(0)
+#define YT921X_LED_SER_CTRL 0xd0100
+#define YT921X_LED_SER_CTRL_UNK GENMASK(25, 24) /* delay? */
+#define YT921X_LED_SER_CTRL_ACTIVE_LOW BIT(4)
+#define YT921X_LED_SER_CTRL_GRP_NUM_M GENMASK(1, 0) /* #grp - 1 */
+#define YT921X_LED_SER_CTRL_GRP_NUM(x) FIELD_PREP(YT921X_LED_SER_CTRL_GRP_NUM_M, (x))
+#define YT921X_LED_SER_MAPnm(grp, port) (0xd0104 + 8 * (2 - (grp)) + 4 * ((port) / 5))
+#define YT921X_LED_SER_MAP_DSTn_PORT_M(port) GENMASK(6 * ((port) % 5) + 5, 6 * ((port) % 5) + 2)
+#define YT921X_LED_SER_MAP_DSTn_PORT(port, x) ((x) << (6 * ((port) % 5) + 2))
+#define YT921X_LED_SER_MAP_DSTn_LED_M(port) GENMASK(6 * ((port) % 5) + 1, 6 * ((port) % 5))
+#define YT921X_LED_SER_MAP_DSTn_LED(port, x) ((x) << (6 * ((port) % 5)))
+#define YT921X_LED_PAR_PORTS 0xd01c4
+#define YT921X_LED_PAR_INV 0xd01c8
+#define YT921X_LED_PAR_INV_INVnm(grp, port) BIT(10 * (grp) + (port))
+#define YT921X_LED_PAR_MAPn(port) (0xd01d0 + 4 * (port))
+#define YT921X_LED_PAR_MAP_DSTn_PORT_M(grp) GENMASK(6 * (grp) + 5, 6 * (grp) + 2)
+#define YT921X_LED_PAR_MAP_DSTn_PORT(grp, x) ((x) << (6 * (grp) + 2))
+#define YT921X_LED_PAR_MAP_DSTn_LED_M(grp) GENMASK(6 * (grp) + 1, 6 * (grp))
+#define YT921X_LED_PAR_MAP_DSTn_LED(grp, x) ((x) << (6 * (grp)))
+
+#define YT921X_LED_BLINK_MIN 64
+#define YT921X_LED_BLINK_DEF 512
+#define YT921X_LED_BLINK_MAX 2048
+
+/* 2 * lcm(2, 3, 4, 6) */
+#define YT921X_LED_DUTY_DENOM 24
+#define YT921X_LED_DUTY(nom, denom) (YT921X_LED_DUTY_DENOM * (nom) / (denom))
+
+struct yt921x_priv;
+
+struct yt921x_led {
+ struct led_classdev cdev;
+ struct yt921x_port *port;
+ unsigned char group;
+
+ bool use_cycle:1;
+ bool use_duty:1;
+};
+
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
+
+void yt921x_leds_remove(struct yt921x_priv *priv);
+int yt921x_leds_setup(struct yt921x_priv *priv);
+
+#else
+
+static inline void yt921x_leds_remove(struct yt921x_priv *priv) {}
+
+static inline int yt921x_leds_setup(struct yt921x_priv *priv)
+{
+ return 0;
+}
+
+#endif
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c
new file mode 100644
index 0000000000000..164c25ef494a1
--- /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)
+{
+ WARN_ON(!mutex_is_locked(&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)
+{
+ WARN_ON(!mutex_is_locked(&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 0000000000000..2995e91f3b3d1
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/smi.h
@@ -0,0 +1,92 @@
+/* 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);
+}
+
+/* yt921x_regs_*() are exported for linkage only. Do not use them directly. Use
+ * yt921x_reg64_*() or yt921x_reg96_*() instead.
+ */
+
+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/08/16 20:39 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21448 output: 24 thoughts: 1057 Thoughts: **WorthFuzzing Assessment: Motorcomm YT9215 Ethernet Switch Driver** Okay, so this patch adds LED support for the Motorcomm YT9215 ethernet switch chip, moving the driver around and introducing a new config option. My first thought is: can I even reach this thing in a virtualized environment like QEMU? DSA drivers, in general, are for physical switch chips, connected via MDIO/SPI/I2C, which makes me doubt it. Unless there's *specific* QEMU emulation for this particular chip, or maybe even a software-emulated version within the kernel, I'm out of luck. I'd need to check for QEMU emulation, but without source, it's difficult. DSA drivers aren't usually emulated in standard QEMU. Could I get around it with a device tree overlay, or user-space tools like `i2c-stub` or `mdio-mockup`? `mdio-mockup` exists, but it's really for PHYs, so I don't know if it'll emulate a full DSA switch. Even if it *could*, is it worth fuzzing? These hardware-specific drivers that need physical hardware—DSA switches, PHYs—usually aren't fuzzed unless there's software emulation or they're part of a mockable generic subsystem. Looking at the instructions again, it says to return `WorthFuzzing=false` if the code path is impossible to reach in virtualized environments, even with software emulation, or if it's in vendor-specific PCIe switch, SmartNIC, or GPU drivers. This is a vendor-specific Ethernet switch chip driver, so it falls into the first category. It's an MDIO device. Can MDIO devices be fuzzed in syzkaller? Syzkaller has I2C/SPI fuzzing, but I don't recall it being for MDIO directly, or DSA, for that matter. The prompt specifically mentions code impossible to reach in virtual environments. This is a DSA switch driver that needs an MDIO bus and a device tree setup, so in a standard QEMU environment, without *specific* emulation, it's highly unlikely to be reachable. Therefore, `WorthFuzzing=false`. Let me just double-check, though, to be absolutely sure. This driver registers itself as an MDIO device driver: `mdio_driver_register(&yt921x_mdio_driver)`. For the driver to probe, an MDIO device with a matching ID or device tree compatible string needs to be present. In QEMU, unless there's a specific board instantiation, it won't probe. While syzkaller can inject device tree nodes, the driver would still try to read/write MDIO registers. If there's no hardware, the probe will fail early, likely by checking a hardware ID. Let's look at `yt921x_mdio_probe` just to double check, though I don't have the source for that at the moment. But the conclusion remains: not worth fuzzing, based on this. |
| 4/2 | 2026/08/16 20:39 | tool | grepper |
0mArgs:{
"Expression": "yt921x_mdio_probe"
}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-4717-\ndrivers/net/dsa/motorcomm/chip.c:4718:static int yt921x_mdio_probe(struct mdio_device *mdiodev)\ndrivers/net/dsa/motorcomm/chip.c-4719-{\n--\ndrivers/net/dsa/motorcomm/chip.c=4772=static struct mdio_driver yt921x_mdio_driver = {\ndrivers/net/dsa/motorcomm/chip.c:4773:\t.probe = yt921x_mdio_probe,\ndrivers/net/dsa/motorcomm/chip.c-4774-\t.remove = yt921x_mdio_remove,\n"
} |
| 5/2 | 2026/08/16 20:39 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23449 output: 42 thoughts: 19 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing the `yt921x_mdio_probe` Function** Okay, let's dive into `yt921x_mdio_probe`. My immediate goal is to understand its role within the broader framework, presumably related to the initialization and interaction with an MDIO (Management Data Input/Output) interface, likely associated with a PHY or other network device within a system leveraging a Yt921x chip. I'll be looking for key aspects: * **Device Tree Interaction:** How does this function utilize the Device Tree (or equivalent configuration mechanism) to identify and configure the specific MDIO controller and attached PHY(s)? I'll be searching for calls to functions like `of_match_table`, `of_property_read_*`, and the like. This will be the foundational information. * **Resource Allocation:** What resources does it request and manage? Specifically, I'm interested in memory regions (for MMIO access), interrupt lines, and possibly clock configurations. This includes how it handles error conditions related to resource allocation, and what it does in case of failures. * **MDIO Controller Initialization:** I need to find the specific initialization routines for the underlying MDIO controller. This might involve setting up the clock frequency, configuring the MDIO bus speed, and potentially enabling specific MDIO functionalities (like auto-negotiation, etc.). I anticipate low-level register writes at some point. * **PHY Detection and Configuration:** Is there PHY detection involved? Does it probe for specific PHYs or use a generic approach? What are the mechanisms for configuring the detected PHY? This is critical for understanding the device's operational characteristics. I expect to see register accesses, but I'm looking for the overall logic to see how the software determines whether PHYs are present or not. * **Driver Registration:** How does the driver register itself with the kernel's MDIO subsystem (if applicable)? I'll look for registrations of bus drivers, device drivers, or anything related to the kernel's core MDIO API. I'm trying to see how the system understands what is connected. * **Error Handling:** It will be important to carefully examine the error conditions and handling throughout the function. Are there retries? Fallbacks? Graceful error reporting? How does it handle device-specific issues? I'll be assessing the robustness of the implementation. In essence, I need to get a clear picture of how this function sets up the MDIO interface, discovers and configures any connected PHYs, and integrates with the rest of the system. I'm ready to dig into the code and start figuring this out. |
| 6/2 | 2026/08/16 20:39 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4718,
"LineCount": 50
}Results: {
"Contents": "4718:\tstatic int yt921x_mdio_probe(struct mdio_device *mdiodev)\n4719:\t{\n4720:\t\tstruct device *dev = \u0026mdiodev-\u003edev;\n4721:\t\tstruct yt921x_reg_mdio *mdio;\n4722:\t\tstruct yt921x_priv *priv;\n4723:\t\tstruct dsa_switch *ds;\n4724:\t\n4725:\t\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n4726:\t\tif (!priv)\n4727:\t\t\treturn -ENOMEM;\n4728:\t\n4729:\t\tmdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);\n4730:\t\tif (!mdio)\n4731:\t\t\treturn -ENOMEM;\n4732:\t\n4733:\t\tmdio-\u003ebus = mdiodev-\u003ebus;\n4734:\t\tmdio-\u003eaddr = mdiodev-\u003eaddr;\n4735:\t\tmdio-\u003eswitchid = 0;\n4736:\t\n4737:\t\tmutex_init(\u0026priv-\u003ereg_lock);\n4738:\t\n4739:\t\tpriv-\u003ereg_ops = \u0026yt921x_reg_ops_mdio;\n4740:\t\tpriv-\u003ereg_ctx = mdio;\n4741:\t\n4742:\t\tfor (size_t i = 0; i \u003c ARRAY_SIZE(priv-\u003eports); i++) {\n4743:\t\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n4744:\t\n4745:\t\t\tpp-\u003eindex = i;\n4746:\t\t\tINIT_DELAYED_WORK(\u0026pp-\u003emib_read, yt921x_poll_mib);\n4747:\t\t}\n4748:\t\n4749:\t\tds = \u0026priv-\u003eds;\n4750:\t\tds-\u003edev = dev;\n4751:\t\tds-\u003eassisted_learning_on_cpu_port = true;\n4752:\t\tds-\u003edscp_prio_mapping_is_global = true;\n4753:\t\tds-\u003epriv = priv;\n4754:\t\tds-\u003eops = \u0026yt921x_dsa_switch_ops;\n4755:\t\tds-\u003eageing_time_min = 1 * 5000;\n4756:\t\tds-\u003eageing_time_max = U16_MAX * 5000;\n4757:\t\tds-\u003ephylink_mac_ops = \u0026yt921x_phylink_mac_ops;\n4758:\t\tds-\u003enum_lag_ids = YT921X_LAG_NUM;\n4759:\t\tds-\u003enum_ports = YT921X_PORT_NUM;\n4760:\t\n4761:\t\tmdiodev_set_drvdata(mdiodev, priv);\n4762:\t\n4763:\t\treturn dsa_register_switch(ds);\n4764:\t}\n4765:\t\n4766:\tstatic const struct of_device_id yt921x_of_match[] = {\n4767:\t\t{ .compatible = \"motorcomm,yt9215\" },\n"
} |
| 7/2 | 2026/08/16 20:39 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24944 output: 24 thoughts: 128 Thoughts: **Analyzing the DSA Switch Probe** Okay, so I'm looking at this probe function and it's trying to register a DSA switch. That's the starting point. But, the crucial piece is that this probe requires an MDIO device to be present, and specifically, one that identifies itself with the "motorcomm,yt9215" compatible string. The immediate problem is that Syzkaller, in its current state, isn't capable of emulating either MDIO devices or DSA switches. I can see a potential workaround – injecting a device tree node, theoretically, could trigger the probe function. But even if I got that far, the driver is immediately going to try and talk to the hardware using MDIO. I'm thinking specifically about that `yt921x_dsa_setup` function; it's called by `dsa_register_switch`, so it's a key part of the setup. I need to take a closer look at that function to really understand the implications of this reliance on MDIO. Let's dig into `yt921x_dsa_setup`. |
| 8/2 | 2026/08/16 20:39 | tool | grepper |
0mArgs:{
"Expression": "yt921x_dsa_setup"
}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"
} |
| 9/2 | 2026/08/16 20:39 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25599 output: 42 thoughts: 16 Thoughts: Okay, here's my interpretation of that: **Diving into the Setup Script** Alright, time to crack open `yt921x_dsa_setup`. Let's see what we've got here. I'm expecting this script to handle all the initial configurations and groundwork for the DSA system. I'll be looking closely at how it handles key generation, certificate management (if applicable), and any network settings or dependencies it sets up. My immediate focus will be on understanding the automation flow, looking for potential areas of optimization, and identifying any assumptions or external dependencies this script relies on. From a quick glance at the file name, "setup," I am expecting this script to cover everything from the start. I'll need to know whether to make changes to this file or if I need to look for something different. I'll be looking to quickly determine if it's well-documented and follows best practices; readability is key. I'll be checking for security vulnerabilities as well. Should be an interesting read. |
| 10/2 | 2026/08/16 20:39 | 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"
} |
| 11/2 | 2026/08/16 20:39 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 26650 output: 23 thoughts: 48 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already quite familiar with embedded systems and hardware interaction: **Investigating the `yt921x_chip_reset` Function** Right, so I'm looking at this `yt921x_chip_reset` function. It seems pretty obvious that its primary purpose is to, well, reset the hardware related to that `yt921x` chip. That's the expected behavior, anyway. My main concern is going to be how it handles the fundamental check: whether the hardware is actually *present* in the first place. If the system's configured without that specific chip, I don't want the function to crash, and I need to know how graceful the error handling is. Time to dive into the code and see what happens when there's no physical hardware to talk to; I need to see what the function does with that case. Let's start by checking its implementation. |
| 12/2 | 2026/08/16 20:39 | tool | grepper |
0mArgs:{
"Expression": "yt921x_chip_reset"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4227=static int yt921x_chip_detect(struct yt921x_priv *priv)\n--\ndrivers/net/dsa/motorcomm/chip.c-4293-\ndrivers/net/dsa/motorcomm/chip.c:4294:static int yt921x_chip_reset(struct yt921x_priv *priv)\ndrivers/net/dsa/motorcomm/chip.c-4295-{\n--\ndrivers/net/dsa/motorcomm/chip.c=4537=static int yt921x_dsa_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-4545-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:4546:\tres = yt921x_chip_reset(priv);\ndrivers/net/dsa/motorcomm/chip.c-4547-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n"
} |
| 13/2 | 2026/08/16 20:39 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27210 output: 42 thoughts: 15 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in my technical field: **Analyzing the Call: `yt921x_chip_reset`** Alright, let's dive into this. "yt921x_chip_reset"... Immediately, the name itself is intriguing. The "yt921x" strongly suggests a specific chip, likely a custom or semi-custom design, perhaps a YottaTek product, given the naming convention. The underscore separating "chip" and "reset" is standard practice, indicating this is a dedicated function or routine specifically for resetting that particular hardware component. So, my mind is already formulating questions. What kind of reset are we talking about? Is it a hard reset, a soft reset, a register-level reset, or something else entirely? The specificity of the name implies a more targeted approach than a general system reset, which piques my interest. I'll need to know the context: where is this function called from? What are the potential trigger conditions? What's the error handling like? Are there any preconditions that need to be met before calling it? How is the chip's state interrogated after the reset to ensure a clean slate? This is important. If the chip has a state machine, the reset must return it to a known state; otherwise, there will be issues. I'm mentally preparing to look for the function's definition, the data structures it uses, and any related documentation. I'll be looking for clues about the hardware register mappings, interrupt handling, and any potential side effects on other parts of the system. I have an internal list of potential gotchas to watch for, like power sequencing or potential race conditions during the reset process. This is going to be good. Let's see what's in there. |
| 14/2 | 2026/08/16 20:39 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4294,
"LineCount": 20
}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;\n"
} |
| 15/2 | 2026/08/16 20:39 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27961 output: 42 thoughts: 21 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing the Chip ID Detection Procedure** Okay, so I'm looking at `yt921x_chip_detect`. The function's job is straightforward: determine the specific silicon revision of the `yt921x` chip we're dealing with. The primary mechanism is a register read. That's a classic and efficient approach. I'm assuming this register holds a unique identifier or a maskable value that allows differentiation between various hardware revisions. I'll need to dig into the datasheet for that chip to figure out the exact register address and the bit field layout. I'll also want to understand the implications of the possible values. Are we just identifying different revisions, or are there functional differences tied to these IDs? If so, I'll have to make sure the code handles these variations correctly. I'll be looking for any error handling around the register read itself. What happens if the read fails? Does it retry, or does it assume a default ID? Robust error handling is crucial. Finally, I'll see how this detection result feeds into the rest of the driver initialization. Are we branching based on the detected ID? If so, I want to confirm the branching logic is solid and the code paths for each revision are complete and tested. I'm hoping it's well-structured and easy to follow. |
| 16/2 | 2026/08/16 20:39 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4227,
"LineCount": 20
}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;\n"
} |
| 17/2 | 2026/08/16 20:39 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28635 output: 118 thoughts: 199 Thoughts: **My Assessment of the Patch's Fuzzing Potential** Okay, let's break this down. From what I understand, this patch is introducing LED support specifically for a `yt921x` hardware switch chip. Since the core functionality relies on this specific hardware, the `yt921x_chip_detect` function is going to fail in a virtualized environment like QEMU or GCE because the underlying hardware simply won't be present, or at least won't present with an appropriate ID. Consequently, the driver will fail during the probe, and the LED setup code, specifically `yt921x_leds_setup`, will never be executed. Given my understanding of the prompt – "Return WorthFuzzing=false if the patch only contains code paths that are impossible to reach in virtualized environments..." – this is a clear match. The introduced LED support is entirely contingent on the presence of physical hardware, and thus, unreachable in a virtualized setting. There's no way to emulate it reliably, and no amount of software trickery (e.g., usb gadget, mac80211_hwsim) will change that. Therefore, I will confidently set `WorthFuzzing=false`. This patch is irrelevant for fuzzing in virtual environments. |
| 18/2 | 2026/08/16 20:39 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch adds LED support for the Motorcomm YT9215 ethernet switch chip (a DSA driver). This is a hardware-specific driver that communicates with a physical chip via MDIO. Since there is no software emulation for this specific switch chip in standard QEMU/virtualized environments, the driver will fail to probe (e.g., during chip ID detection), and the newly added LED setup code will be unreachable.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds LED support for the Motorcomm YT9215 ethernet switch chip (a DSA driver). This is a hardware-specific driver that communicates with a physical chip via MDIO. Since there is no software emulation for this specific switch chip in standard QEMU/virtualized environments, the driver will fail to probe (e.g., during chip ID detection), and the newly added LED setup code will be unreachable.",
"WorthFuzzing": false
} |
| 19/1 | 2026/08/16 20:39 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|