| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/23 20:59 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies only the Motorcomm YT9215 ethernet switch chip driver in drivers/net/dsa/motorcomm/. This driver controls a physical Ethernet switch ASIC connected via MDIO and matched via Device Tree (\"motorcomm,yt9215\"). It is not emulated in standard QEMU/virtualized environments on amd64 and is structurally unreachable for fuzzing.",
"WorthFuzzing": false
} |
| 1/1 | 2026/09/23 20:59 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit cf0e235c3222ff189bf8867206c248b901b3ff3a\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Sep 23 20:59:35 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile\nindex aeb12cb91f935..5c2d34ec05fc0 100644\n--- a/drivers/net/dsa/motorcomm/Makefile\n+++ b/drivers/net/dsa/motorcomm/Makefile\n@@ -2,4 +2,5 @@\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 += mib.o\n yt921x-objs += smi.o\ndiff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c\nindex 8b0ac5fc8c8b6..292a43f5d442f 100644\n--- a/drivers/net/dsa/motorcomm/chip.c\n+++ b/drivers/net/dsa/motorcomm/chip.c\n@@ -19,6 +19,7 @@\n #include \u003clinux/of_mdio.h\u003e\n #include \u003clinux/of_net.h\u003e\n #include \u003clinux/sort.h\u003e\n+#include \u003clinux/u64_stats_sync.h\u003e\n \n #include \u003cnet/dsa.h\u003e\n #include \u003cnet/dscp.h\u003e\n@@ -27,75 +28,9 @@\n \n #include \"chip.h\"\n #include \"leds.h\"\n+#include \"mib.h\"\n #include \"smi.h\"\n \n-struct yt921x_mib_desc {\n-\tunsigned int size;\n-\tunsigned int offset;\n-\tconst char *name;\n-};\n-\n-#define MIB_DESC(_size, _offset, _name) \\\n-\t{_size, _offset, _name}\n-\n-/* Must agree with yt921x_mib\n- *\n- * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),\n- * structured go to their *_stats() methods, but we need their sizes and offsets\n- * to perform 32bit MIB overflow wraparound.\n- */\n-static const struct yt921x_mib_desc yt921x_mib_descs[] = {\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),\n-\tMIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),\n-\n-\tMIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, \"RxBadBytes\"),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),\n-\n-\tMIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),\n-\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),\n-\tMIB_DESC(1, YT921X_MIB_DATA_RX_OAM, \"RxOAM\"),\n-\tMIB_DESC(1, YT921X_MIB_DATA_TX_OAM, \"TxOAM\"),\n-};\n-\n struct yt921x_info {\n \tconst char *name;\n \tu16 major;\n@@ -154,14 +89,6 @@ static const struct yt921x_info yt921x_infos[] = {\n \n #define YT921X_VID_UNWARE\t4095\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- * there), we have to poll stats more frequently then it is actually needed.\n- * For overflow protection, normally, 100 sec interval should have been OK.\n- */\n-#define YT921X_STATS_INTERVAL_JIFFIES\t(3 * HZ)\n-\n struct yt921x_reg_mdio {\n \tstruct mii_bus *bus;\n \tint addr;\n@@ -544,294 +471,6 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)\n \treturn 0;\n }\n \n-/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */\n-static int yt921x_read_mib(struct yt921x_priv *priv, int port)\n-{\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct device *dev = to_device(priv);\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\tint res = 0;\n-\n-\t/* Reading of yt921x_port::mib is not protected by a lock and it's vain\n-\t * to keep its consistency, since we have to read registers one by one\n-\t * and there is no way to make a snapshot of MIB stats.\n-\t *\n-\t * Writing (by this function only) is and should be protected by\n-\t * reg_lock.\n-\t */\n-\n-\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n-\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n-\t\tu32 reg = YT921X_MIBn_DATA0(port) + desc-\u003eoffset;\n-\t\tu64 *valp = \u0026((u64 *)mib)[i];\n-\t\tu32 val0;\n-\t\tu64 val;\n-\n-\t\tres = yt921x_reg_read(priv, reg, \u0026val0);\n-\t\tif (res)\n-\t\t\tbreak;\n-\n-\t\tif (desc-\u003esize \u003c= 1) {\n-\t\t\tu64 old_val = *valp;\n-\n-\t\t\tval = (old_val \u0026 ~(u64)U32_MAX) | val0;\n-\t\t\tif (val \u003c old_val)\n-\t\t\t\tval += 1ull \u003c\u003c 32;\n-\t\t} else {\n-\t\t\tu32 val1;\n-\n-\t\t\tres = yt921x_reg_read(priv, reg + 4, \u0026val1);\n-\t\t\tif (res)\n-\t\t\t\tbreak;\n-\t\t\tval = ((u64)val1 \u003c\u003c 32) | val0;\n-\t\t}\n-\n-\t\tWRITE_ONCE(*valp, val);\n-\t}\n-\n-\tpp-\u003erx_frames = mib-\u003erx_64byte + mib-\u003erx_65_127byte +\n-\t\t\tmib-\u003erx_128_255byte + mib-\u003erx_256_511byte +\n-\t\t\tmib-\u003erx_512_1023byte + mib-\u003erx_1024_1518byte +\n-\t\t\tmib-\u003erx_jumbo;\n-\tpp-\u003etx_frames = mib-\u003etx_64byte + mib-\u003etx_65_127byte +\n-\t\t\tmib-\u003etx_128_255byte + mib-\u003etx_256_511byte +\n-\t\t\tmib-\u003etx_512_1023byte + mib-\u003etx_1024_1518byte +\n-\t\t\tmib-\u003etx_jumbo;\n-\n-\tif (res)\n-\t\tdev_err(dev, \"Failed to %s port %d: %i\\n\", \"read stats for\",\n-\t\t\tport, res);\n-\treturn res;\n-}\n-\n-static void yt921x_poll_mib(struct work_struct *work)\n-{\n-\tstruct yt921x_port *pp = container_of_const(work, struct yt921x_port,\n-\t\t\t\t\t\t mib_read.work);\n-\tstruct yt921x_priv *priv = (void *)(pp - pp-\u003eindex) -\n-\t\t\t\t offsetof(struct yt921x_priv, ports);\n-\tunsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;\n-\tint port = pp-\u003eindex;\n-\tint res;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tres = yt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\tif (res)\n-\t\tdelay *= 4;\n-\n-\tschedule_delayed_work(\u0026pp-\u003emib_read, delay);\n-}\n-\n-static void\n-yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,\n-\t\t uint8_t *data)\n-{\n-\tif (stringset != ETH_SS_STATS)\n-\t\treturn;\n-\n-\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n-\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n-\n-\t\tif (desc-\u003ename)\n-\t\t\tethtool_puts(\u0026data, desc-\u003ename);\n-\t}\n-}\n-\n-static void\n-yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\tsize_t j;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\tj = 0;\n-\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n-\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n-\n-\t\tif (!desc-\u003ename)\n-\t\t\tcontinue;\n-\n-\t\tdata[j] = ((u64 *)mib)[i];\n-\t\tj++;\n-\t}\n-}\n-\n-static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)\n-{\n-\tint cnt = 0;\n-\n-\tif (sset != ETH_SS_STATS)\n-\t\treturn 0;\n-\n-\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n-\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n-\n-\t\tif (desc-\u003ename)\n-\t\t\tcnt++;\n-\t}\n-\n-\treturn cnt;\n-}\n-\n-static void\n-yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,\n-\t\t\t struct ethtool_eth_mac_stats *mac_stats)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\tmac_stats-\u003eFramesTransmittedOK = pp-\u003etx_frames;\n-\tmac_stats-\u003eSingleCollisionFrames = mib-\u003etx_single_collisions;\n-\tmac_stats-\u003eMultipleCollisionFrames = mib-\u003etx_multiple_collisions;\n-\tmac_stats-\u003eFramesReceivedOK = pp-\u003erx_frames;\n-\tmac_stats-\u003eFrameCheckSequenceErrors = mib-\u003erx_crc_errors;\n-\tmac_stats-\u003eAlignmentErrors = mib-\u003erx_alignment_errors;\n-\tmac_stats-\u003eOctetsTransmittedOK = mib-\u003etx_good_bytes;\n-\tmac_stats-\u003eFramesWithDeferredXmissions = mib-\u003etx_deferred;\n-\tmac_stats-\u003eLateCollisions = mib-\u003etx_late_collisions;\n-\tmac_stats-\u003eFramesAbortedDueToXSColls = mib-\u003etx_aborted_errors;\n-\t/* mac_stats-\u003eFramesLostDueToIntMACXmitError */\n-\t/* mac_stats-\u003eCarrierSenseErrors */\n-\tmac_stats-\u003eOctetsReceivedOK = mib-\u003erx_good_bytes;\n-\t/* mac_stats-\u003eFramesLostDueToIntMACRcvError */\n-\tmac_stats-\u003eMulticastFramesXmittedOK = mib-\u003etx_multicast;\n-\tmac_stats-\u003eBroadcastFramesXmittedOK = mib-\u003etx_broadcast;\n-\t/* mac_stats-\u003eFramesWithExcessiveDeferral */\n-\tmac_stats-\u003eMulticastFramesReceivedOK = mib-\u003erx_multicast;\n-\tmac_stats-\u003eBroadcastFramesReceivedOK = mib-\u003erx_broadcast;\n-\t/* mac_stats-\u003eInRangeLengthErrors */\n-\t/* mac_stats-\u003eOutOfRangeLengthField */\n-\tmac_stats-\u003eFrameTooLongErrors = mib-\u003erx_oversize_errors;\n-}\n-\n-static void\n-yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,\n-\t\t\t struct ethtool_eth_ctrl_stats *ctrl_stats)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\tctrl_stats-\u003eMACControlFramesTransmitted = mib-\u003etx_pause;\n-\tctrl_stats-\u003eMACControlFramesReceived = mib-\u003erx_pause;\n-\t/* ctrl_stats-\u003eUnsupportedOpcodesReceived */\n-}\n-\n-static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {\n-\t{ 0, 64 },\n-\t{ 65, 127 },\n-\t{ 128, 255 },\n-\t{ 256, 511 },\n-\t{ 512, 1023 },\n-\t{ 1024, 1518 },\n-\t{ 1519, YT921X_FRAME_SIZE_MAX },\n-\t{}\n-};\n-\n-static void\n-yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,\n-\t\t\t struct ethtool_rmon_stats *rmon_stats,\n-\t\t\t const struct ethtool_rmon_hist_range **ranges)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\t*ranges = yt921x_rmon_ranges;\n-\n-\trmon_stats-\u003eundersize_pkts = mib-\u003erx_undersize_errors;\n-\trmon_stats-\u003eoversize_pkts = mib-\u003erx_oversize_errors;\n-\trmon_stats-\u003efragments = mib-\u003erx_alignment_errors;\n-\t/* rmon_stats-\u003ejabbers */\n-\n-\trmon_stats-\u003ehist[0] = mib-\u003erx_64byte;\n-\trmon_stats-\u003ehist[1] = mib-\u003erx_65_127byte;\n-\trmon_stats-\u003ehist[2] = mib-\u003erx_128_255byte;\n-\trmon_stats-\u003ehist[3] = mib-\u003erx_256_511byte;\n-\trmon_stats-\u003ehist[4] = mib-\u003erx_512_1023byte;\n-\trmon_stats-\u003ehist[5] = mib-\u003erx_1024_1518byte;\n-\trmon_stats-\u003ehist[6] = mib-\u003erx_jumbo;\n-\n-\trmon_stats-\u003ehist_tx[0] = mib-\u003etx_64byte;\n-\trmon_stats-\u003ehist_tx[1] = mib-\u003etx_65_127byte;\n-\trmon_stats-\u003ehist_tx[2] = mib-\u003etx_128_255byte;\n-\trmon_stats-\u003ehist_tx[3] = mib-\u003etx_256_511byte;\n-\trmon_stats-\u003ehist_tx[4] = mib-\u003etx_512_1023byte;\n-\trmon_stats-\u003ehist_tx[5] = mib-\u003etx_1024_1518byte;\n-\trmon_stats-\u003ehist_tx[6] = mib-\u003etx_jumbo;\n-}\n-\n-static void\n-yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,\n-\t\t struct rtnl_link_stats64 *stats)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tstats-\u003erx_length_errors = mib-\u003erx_undersize_errors +\n-\t\t\t\t mib-\u003erx_fragment_errors;\n-\tstats-\u003erx_over_errors = mib-\u003erx_oversize_errors;\n-\tstats-\u003erx_crc_errors = mib-\u003erx_crc_errors;\n-\tstats-\u003erx_frame_errors = mib-\u003erx_alignment_errors;\n-\t/* stats-\u003erx_fifo_errors */\n-\t/* stats-\u003erx_missed_errors */\n-\n-\tstats-\u003etx_aborted_errors = mib-\u003etx_aborted_errors;\n-\t/* stats-\u003etx_carrier_errors */\n-\tstats-\u003etx_fifo_errors = mib-\u003etx_undersize_errors;\n-\t/* stats-\u003etx_heartbeat_errors */\n-\tstats-\u003etx_window_errors = mib-\u003etx_late_collisions;\n-\n-\tstats-\u003erx_packets = pp-\u003erx_frames;\n-\tstats-\u003etx_packets = pp-\u003etx_frames;\n-\tstats-\u003erx_bytes = mib-\u003erx_good_bytes - ETH_FCS_LEN * stats-\u003erx_packets;\n-\tstats-\u003etx_bytes = mib-\u003etx_good_bytes - ETH_FCS_LEN * stats-\u003etx_packets;\n-\tstats-\u003erx_errors = stats-\u003erx_length_errors + stats-\u003erx_over_errors +\n-\t\t\t stats-\u003erx_crc_errors + stats-\u003erx_frame_errors;\n-\tstats-\u003etx_errors = stats-\u003etx_aborted_errors + stats-\u003etx_fifo_errors +\n-\t\t\t stats-\u003etx_window_errors;\n-\tstats-\u003erx_dropped = mib-\u003erx_dropped;\n-\t/* stats-\u003etx_dropped */\n-\tstats-\u003emulticast = mib-\u003erx_multicast;\n-\tstats-\u003ecollisions = mib-\u003etx_collisions;\n-}\n-\n-static void\n-yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,\n-\t\t\t struct ethtool_pause_stats *pause_stats)\n-{\n-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n-\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n-\tstruct yt921x_mib *mib = \u0026pp-\u003emib;\n-\n-\tmutex_lock(\u0026priv-\u003ereg_lock);\n-\tyt921x_read_mib(priv, port);\n-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n-\n-\tpause_stats-\u003etx_pause_frames = mib-\u003etx_pause;\n-\tpause_stats-\u003erx_pause_frames = mib-\u003erx_pause;\n-}\n-\n static int\n yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)\n {\n@@ -3953,11 +3592,14 @@ yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,\n {\n \tstruct dsa_port *dp = dsa_phylink_to_port(config);\n \tstruct yt921x_priv *priv = to_yt921x_priv(dp-\u003eds);\n+\tstruct yt921x_mib *pm;\n \tint port = dp-\u003eindex;\n \tint res;\n \n \t/* No need to sync; port control block is hold until device remove */\n-\tcancel_delayed_work(\u0026priv-\u003eports[port].mib_read);\n+\tpm = priv-\u003eports[port].mib;\n+\tif (pm)\n+\t\tcancel_delayed_work(\u0026pm-\u003ework);\n \n \tmutex_lock(\u0026priv-\u003ereg_lock);\n \tres = yt921x_port_down(priv, port);\n@@ -3976,6 +3618,7 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,\n {\n \tstruct dsa_port *dp = dsa_phylink_to_port(config);\n \tstruct yt921x_priv *priv = to_yt921x_priv(dp-\u003eds);\n+\tstruct yt921x_mib *pm;\n \tint port = dp-\u003eindex;\n \tint res;\n \n@@ -3988,7 +3631,9 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,\n \t\tdev_err(dp-\u003eds-\u003edev, \"Failed to %s port %d: %i\\n\", \"bring up\",\n \t\t\tport, res);\n \n-\tschedule_delayed_work(\u0026priv-\u003eports[port].mib_read, 0);\n+\tpm = priv-\u003eports[port].mib;\n+\tif (pm)\n+\t\tschedule_delayed_work(\u0026pm-\u003ework, 0);\n }\n \n static void\n@@ -4108,11 +3753,41 @@ yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,\n \treturn DSA_TAG_PROTO_YT921X;\n }\n \n+static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\n+\t/* get_stats64() might not be synchronized with port_teardown(), hold\n+\t * the MIB block till the device teardown\n+\t */\n+\tif (pm)\n+\t\tdisable_delayed_work_sync(\u0026pm-\u003ework);\n+}\n+\n static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)\n {\n \tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct device *dev = to_device(priv);\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n \tint res;\n \n+\tif (pm) {\n+\t\tenable_delayed_work(\u0026pm-\u003ework);\n+\t} else if (BIT(port) \u0026 (priv-\u003einfo-\u003einternal_mask |\n+\t\t\t\tpriv-\u003einfo-\u003eexternal_mask)) {\n+\t\tpm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);\n+\t\tif (!pm)\n+\t\t\treturn -ENOMEM;\n+\t\tpp-\u003emib = pm;\n+\n+\t\tpm-\u003eport = pp;\n+\t\tu64_stats_init(\u0026pm-\u003esyncp);\n+\t\tINIT_DELAYED_WORK(\u0026pm-\u003ework, yt921x_mib_poll);\n+\t}\n+\n \tmutex_lock(\u0026priv-\u003ereg_lock);\n \tres = yt921x_port_setup(priv, port);\n \tmutex_unlock(\u0026priv-\u003ereg_lock);\n@@ -4667,6 +4342,7 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {\n \t/* port */\n \t.get_tag_protocol\t= yt921x_dsa_get_tag_protocol,\n \t.phylink_get_caps\t= yt921x_dsa_phylink_get_caps,\n+\t.port_teardown\t\t= yt921x_dsa_port_teardown,\n \t.port_setup\t\t= yt921x_dsa_port_setup,\n #if IS_ENABLED(CONFIG_DCB)\n \t/* dscp */\n@@ -4696,12 +4372,6 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)\n \tif (!priv)\n \t\treturn;\n \n-\tfor (size_t i = ARRAY_SIZE(priv-\u003eports); i-- \u003e 0; ) {\n-\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n-\n-\t\tdisable_delayed_work_sync(\u0026pp-\u003emib_read);\n-\t}\n-\n \tdsa_unregister_switch(\u0026priv-\u003eds);\n \n \tfor (unsigned int i = 0; i \u003c ARRAY_SIZE(priv-\u003eacl_blks); i++) {\n@@ -4749,7 +4419,6 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)\n \t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n \n \t\tpp-\u003eindex = i;\n-\t\tINIT_DELAYED_WORK(\u0026pp-\u003emib_read, yt921x_poll_mib);\n \t}\n \n \tds = \u0026priv-\u003eds;\ndiff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h\nindex 83cd454955ddb..b2e39b70bf61e 100644\n--- a/drivers/net/dsa/motorcomm/chip.h\n+++ b/drivers/net/dsa/motorcomm/chip.h\n@@ -167,69 +167,6 @@\n #define YT921X_EEE_CTRL\t\t\t0xb0000\n #define YT921X_EEE_CTRL_ENn(port)\t\tBIT(port)\n \n-#define YT921X_MIB_CTRL\t\t\t0xc0004\n-#define YT921X_MIB_CTRL_CLEAN\t\t\tBIT(30)\n-#define YT921X_MIB_CTRL_PORT_M\t\t\tGENMASK(6, 3)\n-#define YT921X_MIB_CTRL_PORT(x)\t\t\tFIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))\n-#define YT921X_MIB_CTRL_ONE_PORT\t\tBIT(1)\n-#define YT921X_MIB_CTRL_ALL_PORT\t\tBIT(0)\n-#define YT921X_MIBn_DATA0(port)\t\t(0xc0100 + 0x100 * (port))\n-#define YT921X_MIBn_DATAm(port, x)\t(YT921X_MIBn_DATA0(port) + 4 * (x))\n-#define YT921X_MIB_DATA_RX_BROADCAST\t\t0x00\n-#define YT921X_MIB_DATA_RX_PAUSE\t\t0x04\n-#define YT921X_MIB_DATA_RX_MULTICAST\t\t0x08\n-#define YT921X_MIB_DATA_RX_CRC_ERR\t\t0x0c\n-\n-#define YT921X_MIB_DATA_RX_ALIGN_ERR\t\t0x10\n-#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR\t0x14\n-#define YT921X_MIB_DATA_RX_FRAG_ERR\t\t0x18\n-#define YT921X_MIB_DATA_RX_PKT_SZ_64\t\t0x1c\n-\n-#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127\t0x20\n-#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255\t0x24\n-#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511\t0x28\n-#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023\t0x2c\n-\n-#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518\t0x30\n-#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX\t0x34\n-/* 0x38: unused */\n-#define YT921X_MIB_DATA_RX_GOOD_BYTES\t\t0x3c\n-\n-/* 0x40: 64 bytes */\n-#define YT921X_MIB_DATA_RX_BAD_BYTES\t\t0x44\n-/* 0x48: 64 bytes */\n-#define YT921X_MIB_DATA_RX_OVERSIZE_ERR\t0x4c\n-\n-#define YT921X_MIB_DATA_RX_DROPPED\t\t0x50\n-#define YT921X_MIB_DATA_TX_BROADCAST\t\t0x54\n-#define YT921X_MIB_DATA_TX_PAUSE\t\t0x58\n-#define YT921X_MIB_DATA_TX_MULTICAST\t\t0x5c\n-\n-#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR\t0x60\n-#define YT921X_MIB_DATA_TX_PKT_SZ_64\t\t0x64\n-#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127\t0x68\n-#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255\t0x6c\n-\n-#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511\t0x70\n-#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023\t0x74\n-#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518\t0x78\n-#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX\t0x7c\n-\n-/* 0x80: unused */\n-#define YT921X_MIB_DATA_TX_GOOD_BYTES\t\t0x84\n-/* 0x88: 64 bytes */\n-#define YT921X_MIB_DATA_TX_COLLISION\t\t0x8c\n-\n-#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION\t0x90\n-#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION\t0x94\n-#define YT921X_MIB_DATA_TX_SINGLE_COLLISION\t0x98\n-#define YT921X_MIB_DATA_TX_PKT\t\t\t0x9c\n-\n-#define YT921X_MIB_DATA_TX_DEFERRED\t\t0xa0\n-#define YT921X_MIB_DATA_TX_LATE_COLLISION\t0xa4\n-#define YT921X_MIB_DATA_RX_OAM\t\t\t0xa8\n-#define YT921X_MIB_DATA_TX_OAM\t\t\t0xac\n-\n #define YT921X_EDATA_CTRL\t\t0xe0000\n #define YT921X_EDATA_CTRL_ADDR_M\t\tGENMASK(15, 8)\n #define YT921X_EDATA_CTRL_ADDR(x)\t\t\tFIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x))\n@@ -861,58 +798,6 @@ enum yt921x_fdb_entry_status {\n #define yt921x_port_is_internal(port) ((port) \u003c 8)\n #define yt921x_port_is_external(port) ((port) == 8 || (port) == 9)\n \n-struct yt921x_mib {\n-\tu64 rx_broadcast;\n-\tu64 rx_pause;\n-\tu64 rx_multicast;\n-\tu64 rx_crc_errors;\n-\n-\tu64 rx_alignment_errors;\n-\tu64 rx_undersize_errors;\n-\tu64 rx_fragment_errors;\n-\tu64 rx_64byte;\n-\n-\tu64 rx_65_127byte;\n-\tu64 rx_128_255byte;\n-\tu64 rx_256_511byte;\n-\tu64 rx_512_1023byte;\n-\n-\tu64 rx_1024_1518byte;\n-\tu64 rx_jumbo;\n-\tu64 rx_good_bytes;\n-\n-\tu64 rx_bad_bytes;\n-\tu64 rx_oversize_errors;\n-\n-\tu64 rx_dropped;\n-\tu64 tx_broadcast;\n-\tu64 tx_pause;\n-\tu64 tx_multicast;\n-\n-\tu64 tx_undersize_errors;\n-\tu64 tx_64byte;\n-\tu64 tx_65_127byte;\n-\tu64 tx_128_255byte;\n-\n-\tu64 tx_256_511byte;\n-\tu64 tx_512_1023byte;\n-\tu64 tx_1024_1518byte;\n-\tu64 tx_jumbo;\n-\n-\tu64 tx_good_bytes;\n-\tu64 tx_collisions;\n-\n-\tu64 tx_aborted_errors;\n-\tu64 tx_multiple_collisions;\n-\tu64 tx_single_collisions;\n-\tu64 tx_good;\n-\n-\tu64 tx_deferred;\n-\tu64 tx_late_collisions;\n-\tu64 rx_oam;\n-\tu64 tx_oam;\n-};\n-\n struct yt921x_acl_entry {\n \tu32 key[2];\n \tu32 mask[2];\n@@ -939,11 +824,6 @@ struct yt921x_port {\n \tbool hairpin;\n \tbool isolated;\n \n-\tstruct delayed_work mib_read;\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@@ -953,6 +833,8 @@ struct yt921x_port {\n \n \tstruct yt921x_led *leds[YT921X_LED_GROUP_NUM];\n #endif\n+\n+\tstruct yt921x_mib *mib;\n };\n \n struct yt921x_reg_ops {\n@@ -989,4 +871,7 @@ struct yt921x_priv {\n \tstruct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM];\n };\n \n+#define yt921x_port_to_priv(pp) \\\n+\tcontainer_of_const((pp), struct yt921x_priv, ports[(pp)-\u003eindex])\n+\n #endif\ndiff --git a/drivers/net/dsa/motorcomm/leds.c b/drivers/net/dsa/motorcomm/leds.c\nindex 87a6b9cf60682..dd5b3684250db 100644\n--- a/drivers/net/dsa/motorcomm/leds.c\n+++ b/drivers/net/dsa/motorcomm/leds.c\n@@ -12,8 +12,6 @@\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@@ -321,7 +319,7 @@ yt921x_cled_brightness_set_blocking(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 yt921x_priv *priv = yt921x_port_to_priv(pp);\n \tint res;\n \n \tmutex_lock(\u0026priv-\u003ereg_lock);\n@@ -337,7 +335,7 @@ yt921x_cled_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,\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 yt921x_priv *priv = yt921x_port_to_priv(pp);\n \tint res;\n \n \tmutex_lock(\u0026priv-\u003ereg_lock);\n@@ -353,7 +351,7 @@ 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 yt921x_priv *priv = yt921x_port_to_priv(pp);\n \tstruct dsa_port *dp;\n \n \tdp = dsa_to_port(\u0026priv-\u003eds, pp-\u003eindex);\n@@ -374,7 +372,7 @@ yt921x_cled_hw_control_is_supported(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 yt921x_priv *priv = yt921x_port_to_priv(pp);\n \n \tif (yt921x_led_trigger_is_supported(priv, pp-\u003eindex, led-\u003egroup, flags))\n \t\treturn 0;\n@@ -386,7 +384,7 @@ 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+\tstruct yt921x_priv *priv = yt921x_port_to_priv(pp);\n \tint res;\n \n \tmutex_lock(\u0026priv-\u003ereg_lock);\n@@ -401,7 +399,7 @@ 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+\tstruct yt921x_priv *priv = yt921x_port_to_priv(pp);\n \tint res;\n \n \tmutex_lock(\u0026priv-\u003ereg_lock);\ndiff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c\nnew file mode 100644\nindex 0000000000000..2786d9ec29320\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/mib.c\n@@ -0,0 +1,431 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#include \"chip.h\"\n+#include \"mib.h\"\n+#include \"smi.h\"\n+\n+struct yt921x_mib_desc {\n+\tunsigned int size;\n+\tunsigned int offset;\n+\tconst char *name;\n+};\n+\n+#define MIB_DESC(_size, _offset, _name) \\\n+\t{_size, _offset, _name}\n+\n+/* Must agree with yt921x_mib_stats\n+ *\n+ * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),\n+ * structured go to their *_stats() methods, but we need their sizes and offsets\n+ * to perform 32bit MIB overflow wraparound.\n+ */\n+static const struct yt921x_mib_desc yt921x_mib_descs[] = {\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),\n+\tMIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),\n+\n+\tMIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, \"RxBadBytes\"),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),\n+\n+\tMIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),\n+\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),\n+\tMIB_DESC(1, YT921X_MIB_DATA_RX_OAM, \"RxOAM\"),\n+\tMIB_DESC(1, YT921X_MIB_DATA_TX_OAM, \"TxOAM\"),\n+};\n+\n+static_assert(ARRAY_SIZE(yt921x_mib_descs) == YT921X_MIB_STAT_NUM);\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+ * there), we have to poll stats more frequently then it is actually needed.\n+ * For overflow protection, normally, 100 sec interval should have been OK.\n+ */\n+#define YT921X_STATS_INTERVAL_JIFFIES\t(3 * HZ)\n+\n+#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)\n+#define to_device(priv) ((priv)-\u003eds.dev)\n+\n+/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */\n+static int yt921x_mib_read(struct yt921x_priv *priv, int port)\n+{\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct device *dev = to_device(priv);\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\tu64 *buf = pm-\u003edata;\n+\tunsigned long flags;\n+\tu64 rx_frames;\n+\tu64 tx_frames;\n+\tint res = 0;\n+\n+\tmib = \u0026pm-\u003estats;\n+\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n+\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n+\t\tu32 reg = YT921X_MIBn_DATA0(port) + desc-\u003eoffset;\n+\n+\t\tif (desc-\u003esize \u003c= 1) {\n+\t\t\tu64 old_val = buf[i];\n+\t\t\tu32 val0;\n+\t\t\tu64 val;\n+\n+\t\t\tres = yt921x_reg_read(priv, reg, \u0026val0);\n+\t\t\tif (res)\n+\t\t\t\tbreak;\n+\n+\t\t\tval = (old_val \u0026 ~(u64)U32_MAX) | val0;\n+\t\t\tif (val \u003c old_val)\n+\t\t\t\tval += 1ull \u003c\u003c 32;\n+\n+\t\t\tbuf[i] = val;\n+\t\t} else {\n+\t\t\tres = yt921x_counter_read(priv, reg, \u0026buf[i]);\n+\t\t\tif (res)\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tflags = u64_stats_update_begin_irqsave(\u0026pm-\u003esyncp);\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++)\n+\t\tu64_stats_set(\u0026((u64_stats_t *)mib)[i], buf[i]);\n+\trx_frames = u64_stats_read(\u0026mib-\u003erx_64byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_65_127byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_128_255byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_256_511byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_512_1023byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_1024_1518byte) +\n+\t\t u64_stats_read(\u0026mib-\u003erx_jumbo);\n+\ttx_frames = u64_stats_read(\u0026mib-\u003etx_64byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_65_127byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_128_255byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_256_511byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_512_1023byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_1024_1518byte) +\n+\t\t u64_stats_read(\u0026mib-\u003etx_jumbo);\n+\tu64_stats_set(\u0026pm-\u003erx_frames, rx_frames);\n+\tu64_stats_set(\u0026pm-\u003etx_frames, tx_frames);\n+\tu64_stats_update_end_irqrestore(\u0026pm-\u003esyncp, flags);\n+\n+\tif (res)\n+\t\tdev_err(dev, \"Failed to %s port %d: %i\\n\", \"read stats for\",\n+\t\t\tport, res);\n+\treturn res;\n+}\n+\n+void yt921x_mib_poll(struct work_struct *work)\n+{\n+\tstruct yt921x_mib *pm = container_of_const(work, struct yt921x_mib,\n+\t\t\t\t\t\t work.work);\n+\tstruct yt921x_port *pp = pm-\u003eport;\n+\tunsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;\n+\tstruct yt921x_priv *priv = yt921x_port_to_priv(pp);\n+\tint port = pp-\u003eindex;\n+\tint res;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\tres = yt921x_mib_read(priv, port);\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+\tif (res)\n+\t\tdelay *= 4;\n+\n+\tschedule_delayed_work(\u0026pm-\u003ework, delay);\n+}\n+\n+void\n+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,\n+\t\t uint8_t *data)\n+{\n+\tif (stringset != ETH_SS_STATS)\n+\t\treturn;\n+\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n+\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n+\n+\t\tif (desc-\u003ename)\n+\t\t\tethtool_puts(\u0026data, desc-\u003ename);\n+\t}\n+}\n+\n+void\n+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tu64 *buf;\n+\tsize_t j;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tbuf = pm-\u003edata;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\tj = 0;\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n+\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n+\n+\t\tif (!desc-\u003ename)\n+\t\t\tcontinue;\n+\n+\t\tdata[j] = buf[i];\n+\t\tj++;\n+\t}\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\n+\n+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)\n+{\n+\tint cnt = 0;\n+\n+\tif (sset != ETH_SS_STATS)\n+\t\treturn 0;\n+\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(yt921x_mib_descs); i++) {\n+\t\tconst struct yt921x_mib_desc *desc = \u0026yt921x_mib_descs[i];\n+\n+\t\tif (desc-\u003ename)\n+\t\t\tcnt++;\n+\t}\n+\n+\treturn cnt;\n+}\n+\n+void\n+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_eth_mac_stats *mac_stats)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\tmac_stats-\u003eFramesTransmittedOK = u64_stats_read(\u0026pm-\u003etx_frames);\n+\tmac_stats-\u003eSingleCollisionFrames = u64_stats_read(\u0026mib-\u003etx_single_collisions);\n+\tmac_stats-\u003eMultipleCollisionFrames = u64_stats_read(\u0026mib-\u003etx_multiple_collisions);\n+\tmac_stats-\u003eFramesReceivedOK = u64_stats_read(\u0026pm-\u003erx_frames);\n+\tmac_stats-\u003eFrameCheckSequenceErrors = u64_stats_read(\u0026mib-\u003erx_crc_errors);\n+\tmac_stats-\u003eAlignmentErrors = u64_stats_read(\u0026mib-\u003erx_alignment_errors);\n+\tmac_stats-\u003eOctetsTransmittedOK = u64_stats_read(\u0026mib-\u003etx_good_bytes);\n+\tmac_stats-\u003eFramesWithDeferredXmissions = u64_stats_read(\u0026mib-\u003etx_deferred);\n+\tmac_stats-\u003eLateCollisions = u64_stats_read(\u0026mib-\u003etx_late_collisions);\n+\tmac_stats-\u003eFramesAbortedDueToXSColls = u64_stats_read(\u0026mib-\u003etx_aborted_errors);\n+\t/* mac_stats-\u003eFramesLostDueToIntMACXmitError */\n+\t/* mac_stats-\u003eCarrierSenseErrors */\n+\tmac_stats-\u003eOctetsReceivedOK = u64_stats_read(\u0026mib-\u003erx_good_bytes);\n+\t/* mac_stats-\u003eFramesLostDueToIntMACRcvError */\n+\tmac_stats-\u003eMulticastFramesXmittedOK = u64_stats_read(\u0026mib-\u003etx_multicast);\n+\tmac_stats-\u003eBroadcastFramesXmittedOK = u64_stats_read(\u0026mib-\u003etx_broadcast);\n+\t/* mac_stats-\u003eFramesWithExcessiveDeferral */\n+\tmac_stats-\u003eMulticastFramesReceivedOK = u64_stats_read(\u0026mib-\u003erx_multicast);\n+\tmac_stats-\u003eBroadcastFramesReceivedOK = u64_stats_read(\u0026mib-\u003erx_broadcast);\n+\t/* mac_stats-\u003eInRangeLengthErrors */\n+\t/* mac_stats-\u003eOutOfRangeLengthField */\n+\tmac_stats-\u003eFrameTooLongErrors = u64_stats_read(\u0026mib-\u003erx_oversize_errors);\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\n+\n+void\n+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_eth_ctrl_stats *ctrl_stats)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\tctrl_stats-\u003eMACControlFramesTransmitted = u64_stats_read(\u0026mib-\u003etx_pause);\n+\tctrl_stats-\u003eMACControlFramesReceived = u64_stats_read(\u0026mib-\u003erx_pause);\n+\t/* ctrl_stats-\u003eUnsupportedOpcodesReceived */\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\n+\n+static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {\n+\t{ 0, 64 },\n+\t{ 65, 127 },\n+\t{ 128, 255 },\n+\t{ 256, 511 },\n+\t{ 512, 1023 },\n+\t{ 1024, 1518 },\n+\t{ 1519, YT921X_FRAME_SIZE_MAX },\n+\t{}\n+};\n+\n+void\n+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_rmon_stats *rmon_stats,\n+\t\t\t const struct ethtool_rmon_hist_range **ranges)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\t*ranges = yt921x_rmon_ranges;\n+\n+\trmon_stats-\u003eundersize_pkts = u64_stats_read(\u0026mib-\u003erx_undersize_errors);\n+\trmon_stats-\u003eoversize_pkts = u64_stats_read(\u0026mib-\u003erx_oversize_errors);\n+\trmon_stats-\u003efragments = u64_stats_read(\u0026mib-\u003erx_alignment_errors);\n+\t/* rmon_stats-\u003ejabbers */\n+\n+\trmon_stats-\u003ehist[0] = u64_stats_read(\u0026mib-\u003erx_64byte);\n+\trmon_stats-\u003ehist[1] = u64_stats_read(\u0026mib-\u003erx_65_127byte);\n+\trmon_stats-\u003ehist[2] = u64_stats_read(\u0026mib-\u003erx_128_255byte);\n+\trmon_stats-\u003ehist[3] = u64_stats_read(\u0026mib-\u003erx_256_511byte);\n+\trmon_stats-\u003ehist[4] = u64_stats_read(\u0026mib-\u003erx_512_1023byte);\n+\trmon_stats-\u003ehist[5] = u64_stats_read(\u0026mib-\u003erx_1024_1518byte);\n+\trmon_stats-\u003ehist[6] = u64_stats_read(\u0026mib-\u003erx_jumbo);\n+\n+\trmon_stats-\u003ehist_tx[0] = u64_stats_read(\u0026mib-\u003etx_64byte);\n+\trmon_stats-\u003ehist_tx[1] = u64_stats_read(\u0026mib-\u003etx_65_127byte);\n+\trmon_stats-\u003ehist_tx[2] = u64_stats_read(\u0026mib-\u003etx_128_255byte);\n+\trmon_stats-\u003ehist_tx[3] = u64_stats_read(\u0026mib-\u003etx_256_511byte);\n+\trmon_stats-\u003ehist_tx[4] = u64_stats_read(\u0026mib-\u003etx_512_1023byte);\n+\trmon_stats-\u003ehist_tx[5] = u64_stats_read(\u0026mib-\u003etx_1024_1518byte);\n+\trmon_stats-\u003ehist_tx[6] = u64_stats_read(\u0026mib-\u003etx_jumbo);\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\n+\n+void\n+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,\n+\t\t struct rtnl_link_stats64 *stats)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\tunsigned int start;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tdo {\n+\t\tstart = u64_stats_fetch_begin(\u0026pm-\u003esyncp);\n+\n+\t\tstats-\u003erx_length_errors = u64_stats_read(\u0026mib-\u003erx_undersize_errors) +\n+\t\t\t\t\t u64_stats_read(\u0026mib-\u003erx_fragment_errors);\n+\t\tstats-\u003erx_over_errors = u64_stats_read(\u0026mib-\u003erx_oversize_errors);\n+\t\tstats-\u003erx_crc_errors = u64_stats_read(\u0026mib-\u003erx_crc_errors);\n+\t\tstats-\u003erx_frame_errors = u64_stats_read(\u0026mib-\u003erx_alignment_errors);\n+\t\t/* stats-\u003erx_fifo_errors */\n+\t\t/* stats-\u003erx_missed_errors */\n+\n+\t\tstats-\u003etx_aborted_errors = u64_stats_read(\u0026mib-\u003etx_aborted_errors);\n+\t\t/* stats-\u003etx_carrier_errors */\n+\t\tstats-\u003etx_fifo_errors = u64_stats_read(\u0026mib-\u003etx_undersize_errors);\n+\t\t/* stats-\u003etx_heartbeat_errors */\n+\t\tstats-\u003etx_window_errors = u64_stats_read(\u0026mib-\u003etx_late_collisions);\n+\n+\t\tstats-\u003erx_packets = u64_stats_read(\u0026pm-\u003erx_frames);\n+\t\tstats-\u003etx_packets = u64_stats_read(\u0026pm-\u003etx_frames);\n+\t\tstats-\u003erx_bytes = u64_stats_read(\u0026mib-\u003erx_good_bytes) -\n+\t\t\t\t ETH_FCS_LEN * stats-\u003erx_packets;\n+\t\tstats-\u003etx_bytes = u64_stats_read(\u0026mib-\u003etx_good_bytes) -\n+\t\t\t\t ETH_FCS_LEN * stats-\u003etx_packets;\n+\t\tstats-\u003erx_dropped = u64_stats_read(\u0026mib-\u003erx_dropped);\n+\t\t/* stats-\u003etx_dropped */\n+\t\tstats-\u003emulticast = u64_stats_read(\u0026mib-\u003erx_multicast);\n+\t\tstats-\u003ecollisions = u64_stats_read(\u0026mib-\u003etx_collisions);\n+\t} while (u64_stats_fetch_retry(\u0026pm-\u003esyncp, start));\n+\n+\tstats-\u003erx_errors = stats-\u003erx_length_errors + stats-\u003erx_over_errors +\n+\t\t\t stats-\u003erx_crc_errors + stats-\u003erx_frame_errors;\n+\tstats-\u003etx_errors = stats-\u003etx_aborted_errors + stats-\u003etx_fifo_errors +\n+\t\t\t stats-\u003etx_window_errors;\n+}\n+\n+void\n+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_pause_stats *pause_stats)\n+{\n+\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n+\tstruct yt921x_port *pp = \u0026priv-\u003eports[port];\n+\tstruct yt921x_mib *pm = pp-\u003emib;\n+\tstruct yt921x_mib_stats *mib;\n+\n+\tif (!pm)\n+\t\treturn;\n+\tmib = \u0026pm-\u003estats;\n+\n+\tmutex_lock(\u0026priv-\u003ereg_lock);\n+\n+\tyt921x_mib_read(priv, port);\n+\n+\tpause_stats-\u003etx_pause_frames = u64_stats_read(\u0026mib-\u003etx_pause);\n+\tpause_stats-\u003erx_pause_frames = u64_stats_read(\u0026mib-\u003erx_pause);\n+\n+\tmutex_unlock(\u0026priv-\u003ereg_lock);\n+}\ndiff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h\nnew file mode 100644\nindex 0000000000000..55719c29992ba\n--- /dev/null\n+++ b/drivers/net/dsa/motorcomm/mib.h\n@@ -0,0 +1,167 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (c) 2026 David Yang\n+ */\n+\n+#ifndef _YT_MIB_H\n+#define _YT_MIB_H\n+\n+#include \u003clinux/u64_stats_sync.h\u003e\n+\n+#include \u003cnet/dsa.h\u003e\n+\n+#define YT921X_MIB_CTRL\t\t\t0xc0004\n+#define YT921X_MIB_CTRL_CLEAN\t\t\tBIT(30)\n+#define YT921X_MIB_CTRL_PORT_M\t\t\tGENMASK(6, 3)\n+#define YT921X_MIB_CTRL_PORT(x)\t\t\tFIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))\n+#define YT921X_MIB_CTRL_ONE_PORT\t\tBIT(1)\n+#define YT921X_MIB_CTRL_ALL_PORT\t\tBIT(0)\n+#define YT921X_MIBn_DATA0(port)\t\t(0xc0100 + 0x100 * (port))\n+#define YT921X_MIBn_DATAm(port, x)\t(YT921X_MIBn_DATA0(port) + 4 * (x))\n+#define YT921X_MIB_DATA_RX_BROADCAST\t\t0x00\n+#define YT921X_MIB_DATA_RX_PAUSE\t\t0x04\n+#define YT921X_MIB_DATA_RX_MULTICAST\t\t0x08\n+#define YT921X_MIB_DATA_RX_CRC_ERR\t\t0x0c\n+\n+#define YT921X_MIB_DATA_RX_ALIGN_ERR\t\t0x10\n+#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR\t0x14\n+#define YT921X_MIB_DATA_RX_FRAG_ERR\t\t0x18\n+#define YT921X_MIB_DATA_RX_PKT_SZ_64\t\t0x1c\n+\n+#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127\t0x20\n+#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255\t0x24\n+#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511\t0x28\n+#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023\t0x2c\n+\n+#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518\t0x30\n+#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX\t0x34\n+/* 0x38: unused */\n+#define YT921X_MIB_DATA_RX_GOOD_BYTES\t\t0x3c\n+\n+/* 0x40: 64 bytes */\n+#define YT921X_MIB_DATA_RX_BAD_BYTES\t\t0x44\n+/* 0x48: 64 bytes */\n+#define YT921X_MIB_DATA_RX_OVERSIZE_ERR\t0x4c\n+\n+#define YT921X_MIB_DATA_RX_DROPPED\t\t0x50\n+#define YT921X_MIB_DATA_TX_BROADCAST\t\t0x54\n+#define YT921X_MIB_DATA_TX_PAUSE\t\t0x58\n+#define YT921X_MIB_DATA_TX_MULTICAST\t\t0x5c\n+\n+#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR\t0x60\n+#define YT921X_MIB_DATA_TX_PKT_SZ_64\t\t0x64\n+#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127\t0x68\n+#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255\t0x6c\n+\n+#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511\t0x70\n+#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023\t0x74\n+#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518\t0x78\n+#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX\t0x7c\n+\n+/* 0x80: unused */\n+#define YT921X_MIB_DATA_TX_GOOD_BYTES\t\t0x84\n+/* 0x88: 64 bytes */\n+#define YT921X_MIB_DATA_TX_COLLISION\t\t0x8c\n+\n+#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION\t0x90\n+#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION\t0x94\n+#define YT921X_MIB_DATA_TX_SINGLE_COLLISION\t0x98\n+#define YT921X_MIB_DATA_TX_PKT\t\t\t0x9c\n+\n+#define YT921X_MIB_DATA_TX_DEFERRED\t\t0xa0\n+#define YT921X_MIB_DATA_TX_LATE_COLLISION\t0xa4\n+#define YT921X_MIB_DATA_RX_OAM\t\t\t0xa8\n+#define YT921X_MIB_DATA_TX_OAM\t\t\t0xac\n+\n+struct yt921x_mib_stats {\n+\tu64_stats_t rx_broadcast;\n+\tu64_stats_t rx_pause;\n+\tu64_stats_t rx_multicast;\n+\tu64_stats_t rx_crc_errors;\n+\n+\tu64_stats_t rx_alignment_errors;\n+\tu64_stats_t rx_undersize_errors;\n+\tu64_stats_t rx_fragment_errors;\n+\tu64_stats_t rx_64byte;\n+\n+\tu64_stats_t rx_65_127byte;\n+\tu64_stats_t rx_128_255byte;\n+\tu64_stats_t rx_256_511byte;\n+\tu64_stats_t rx_512_1023byte;\n+\n+\tu64_stats_t rx_1024_1518byte;\n+\tu64_stats_t rx_jumbo;\n+\tu64_stats_t rx_good_bytes;\n+\n+\tu64_stats_t rx_bad_bytes;\n+\tu64_stats_t rx_oversize_errors;\n+\n+\tu64_stats_t rx_dropped;\n+\tu64_stats_t tx_broadcast;\n+\tu64_stats_t tx_pause;\n+\tu64_stats_t tx_multicast;\n+\n+\tu64_stats_t tx_undersize_errors;\n+\tu64_stats_t tx_64byte;\n+\tu64_stats_t tx_65_127byte;\n+\tu64_stats_t tx_128_255byte;\n+\n+\tu64_stats_t tx_256_511byte;\n+\tu64_stats_t tx_512_1023byte;\n+\tu64_stats_t tx_1024_1518byte;\n+\tu64_stats_t tx_jumbo;\n+\n+\tu64_stats_t tx_good_bytes;\n+\tu64_stats_t tx_collisions;\n+\n+\tu64_stats_t tx_aborted_errors;\n+\tu64_stats_t tx_multiple_collisions;\n+\tu64_stats_t tx_single_collisions;\n+\tu64_stats_t tx_good;\n+\n+\tu64_stats_t tx_deferred;\n+\tu64_stats_t tx_late_collisions;\n+\tu64_stats_t rx_oam;\n+\tu64_stats_t tx_oam;\n+};\n+\n+#define YT921X_MIB_STAT_NUM\t(sizeof(struct yt921x_mib_stats) / sizeof(u64_stats_t))\n+\n+struct yt921x_mib {\n+\tstruct yt921x_port *port;\n+\n+\tstruct delayed_work work;\n+\tstruct u64_stats_sync syncp;\n+\t/* protected by syncp OR priv-\u003ereg_lock */\n+\tstruct yt921x_mib_stats stats;\n+\tu64_stats_t rx_frames;\n+\tu64_stats_t tx_frames;\n+\t/* protected by priv-\u003ereg_lock */\n+\tu64 data[YT921X_MIB_STAT_NUM];\n+};\n+\n+void yt921x_mib_poll(struct work_struct *work);\n+void\n+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,\n+\t\t uint8_t *data);\n+void\n+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);\n+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset);\n+void\n+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_eth_mac_stats *mac_stats);\n+void\n+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_eth_ctrl_stats *ctrl_stats);\n+void\n+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_rmon_stats *rmon_stats,\n+\t\t\t const struct ethtool_rmon_hist_range **ranges);\n+void\n+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,\n+\t\t struct rtnl_link_stats64 *stats);\n+void\n+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,\n+\t\t\t struct ethtool_pause_stats *pause_stats);\n+\n+#endif\ndiff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c\nindex bf3adfd64165a..27086602997bb 100644\n--- a/drivers/net/dsa/motorcomm/smi.c\n+++ b/drivers/net/dsa/motorcomm/smi.c\n@@ -63,6 +63,36 @@ int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)\n \treturn yt921x_reg_write(priv, reg, u);\n }\n \n+/* Reliably read a 64bit counter */\n+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp)\n+{\n+\tu32 old_lo;\n+\tint res;\n+\tu32 hi;\n+\tu32 lo;\n+\n+\tres = yt921x_reg_read(priv, reg, \u0026old_lo);\n+\tif (res)\n+\t\treturn res;\n+\n+\tfor (int i = 0; i \u003c 16; i++) {\n+\t\tres = yt921x_reg_read(priv, reg + 4, \u0026hi);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t\tres = yt921x_reg_read(priv, reg, \u0026lo);\n+\t\tif (res)\n+\t\t\treturn res;\n+\n+\t\tif (lo \u003e= old_lo) {\n+\t\t\t*valp = ((u64)hi \u003c\u003c 32) | lo;\n+\t\t\treturn 0;\n+\t\t}\n+\t\told_lo = lo;\n+\t}\n+\n+\treturn -ETIMEDOUT;\n+}\n+\n static int\n yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,\n \t\t unsigned int num_regs)\ndiff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h\nindex 212e20f71d806..d34240548af7e 100644\n--- a/drivers/net/dsa/motorcomm/smi.h\n+++ b/drivers/net/dsa/motorcomm/smi.h\n@@ -35,6 +35,8 @@ yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)\n \treturn yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);\n }\n \n+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp);\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"
} |
| 2/1 | 2026/09/23 20:59 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies only the Motorcomm YT9215 ethernet switch chip driver in drivers/net/dsa/motorcomm/. This driver controls a physical Ethernet switch ASIC connected via MDIO and matched via Device Tree (\"motorcomm,yt9215\"). It is not emulated in standard QEMU/virtualized environments on amd64 and is structurally unreachable for fuzzing.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit cf0e235c3222ff189bf8867206c248b901b3ff3a
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Sep 23 20:59:35 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
index aeb12cb91f935..5c2d34ec05fc0 100644
--- a/drivers/net/dsa/motorcomm/Makefile
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -2,4 +2,5 @@
obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
yt921x-objs := chip.o
yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o
+yt921x-objs += mib.o
yt921x-objs += smi.o
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 8b0ac5fc8c8b6..292a43f5d442f 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -19,6 +19,7 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/sort.h>
+#include <linux/u64_stats_sync.h>
#include <net/dsa.h>
#include <net/dscp.h>
@@ -27,75 +28,9 @@
#include "chip.h"
#include "leds.h"
+#include "mib.h"
#include "smi.h"
-struct yt921x_mib_desc {
- unsigned int size;
- unsigned int offset;
- const char *name;
-};
-
-#define MIB_DESC(_size, _offset, _name) \
- {_size, _offset, _name}
-
-/* Must agree with yt921x_mib
- *
- * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
- * structured go to their *_stats() methods, but we need their sizes and offsets
- * to perform 32bit MIB overflow wraparound.
- */
-static const struct yt921x_mib_desc yt921x_mib_descs[] = {
- MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
- MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
-
- MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
- MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
-
- MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
- MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
-};
-
struct yt921x_info {
const char *name;
u16 major;
@@ -154,14 +89,6 @@ static const struct yt921x_info yt921x_infos[] = {
#define YT921X_VID_UNWARE 4095
-/* The interval should be small enough to avoid overflow of 32bit MIBs.
- *
- * Until we can read MIBs from stats64 call directly (i.e. sleep
- * there), we have to poll stats more frequently then it is actually needed.
- * For overflow protection, normally, 100 sec interval should have been OK.
- */
-#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ)
-
struct yt921x_reg_mdio {
struct mii_bus *bus;
int addr;
@@ -544,294 +471,6 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
return 0;
}
-/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
-static int yt921x_read_mib(struct yt921x_priv *priv, int port)
-{
- struct yt921x_port *pp = &priv->ports[port];
- struct device *dev = to_device(priv);
- struct yt921x_mib *mib = &pp->mib;
- int res = 0;
-
- /* Reading of yt921x_port::mib is not protected by a lock and it's vain
- * to keep its consistency, since we have to read registers one by one
- * and there is no way to make a snapshot of MIB stats.
- *
- * Writing (by this function only) is and should be protected by
- * reg_lock.
- */
-
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
- u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
- u64 *valp = &((u64 *)mib)[i];
- u32 val0;
- u64 val;
-
- res = yt921x_reg_read(priv, reg, &val0);
- if (res)
- break;
-
- if (desc->size <= 1) {
- u64 old_val = *valp;
-
- val = (old_val & ~(u64)U32_MAX) | val0;
- if (val < old_val)
- val += 1ull << 32;
- } else {
- u32 val1;
-
- res = yt921x_reg_read(priv, reg + 4, &val1);
- if (res)
- break;
- val = ((u64)val1 << 32) | val0;
- }
-
- WRITE_ONCE(*valp, val);
- }
-
- pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
- mib->rx_128_255byte + mib->rx_256_511byte +
- mib->rx_512_1023byte + mib->rx_1024_1518byte +
- mib->rx_jumbo;
- pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
- mib->tx_128_255byte + mib->tx_256_511byte +
- mib->tx_512_1023byte + mib->tx_1024_1518byte +
- mib->tx_jumbo;
-
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
- port, res);
- return res;
-}
-
-static void yt921x_poll_mib(struct work_struct *work)
-{
- struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
- mib_read.work);
- struct yt921x_priv *priv = (void *)(pp - pp->index) -
- offsetof(struct yt921x_priv, ports);
- unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
- int port = pp->index;
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
- if (res)
- delay *= 4;
-
- schedule_delayed_work(&pp->mib_read, delay);
-}
-
-static void
-yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
- uint8_t *data)
-{
- if (stringset != ETH_SS_STATS)
- return;
-
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (desc->name)
- ethtool_puts(&data, desc->name);
- }
-}
-
-static void
-yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
- size_t j;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- j = 0;
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (!desc->name)
- continue;
-
- data[j] = ((u64 *)mib)[i];
- j++;
- }
-}
-
-static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
-{
- int cnt = 0;
-
- if (sset != ETH_SS_STATS)
- return 0;
-
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (desc->name)
- cnt++;
- }
-
- return cnt;
-}
-
-static void
-yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
- struct ethtool_eth_mac_stats *mac_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- mac_stats->FramesTransmittedOK = pp->tx_frames;
- mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
- mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
- mac_stats->FramesReceivedOK = pp->rx_frames;
- mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
- mac_stats->AlignmentErrors = mib->rx_alignment_errors;
- mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
- mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
- mac_stats->LateCollisions = mib->tx_late_collisions;
- mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
- /* mac_stats->FramesLostDueToIntMACXmitError */
- /* mac_stats->CarrierSenseErrors */
- mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
- /* mac_stats->FramesLostDueToIntMACRcvError */
- mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
- mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
- /* mac_stats->FramesWithExcessiveDeferral */
- mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
- mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
- /* mac_stats->InRangeLengthErrors */
- /* mac_stats->OutOfRangeLengthField */
- mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
-}
-
-static void
-yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
- struct ethtool_eth_ctrl_stats *ctrl_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
- ctrl_stats->MACControlFramesReceived = mib->rx_pause;
- /* ctrl_stats->UnsupportedOpcodesReceived */
-}
-
-static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
- { 0, 64 },
- { 65, 127 },
- { 128, 255 },
- { 256, 511 },
- { 512, 1023 },
- { 1024, 1518 },
- { 1519, YT921X_FRAME_SIZE_MAX },
- {}
-};
-
-static void
-yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
- struct ethtool_rmon_stats *rmon_stats,
- const struct ethtool_rmon_hist_range **ranges)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- *ranges = yt921x_rmon_ranges;
-
- rmon_stats->undersize_pkts = mib->rx_undersize_errors;
- rmon_stats->oversize_pkts = mib->rx_oversize_errors;
- rmon_stats->fragments = mib->rx_alignment_errors;
- /* rmon_stats->jabbers */
-
- rmon_stats->hist[0] = mib->rx_64byte;
- rmon_stats->hist[1] = mib->rx_65_127byte;
- rmon_stats->hist[2] = mib->rx_128_255byte;
- rmon_stats->hist[3] = mib->rx_256_511byte;
- rmon_stats->hist[4] = mib->rx_512_1023byte;
- rmon_stats->hist[5] = mib->rx_1024_1518byte;
- rmon_stats->hist[6] = mib->rx_jumbo;
-
- rmon_stats->hist_tx[0] = mib->tx_64byte;
- rmon_stats->hist_tx[1] = mib->tx_65_127byte;
- rmon_stats->hist_tx[2] = mib->tx_128_255byte;
- rmon_stats->hist_tx[3] = mib->tx_256_511byte;
- rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
- rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
- rmon_stats->hist_tx[6] = mib->tx_jumbo;
-}
-
-static void
-yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
- struct rtnl_link_stats64 *stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- stats->rx_length_errors = mib->rx_undersize_errors +
- mib->rx_fragment_errors;
- stats->rx_over_errors = mib->rx_oversize_errors;
- stats->rx_crc_errors = mib->rx_crc_errors;
- stats->rx_frame_errors = mib->rx_alignment_errors;
- /* stats->rx_fifo_errors */
- /* stats->rx_missed_errors */
-
- stats->tx_aborted_errors = mib->tx_aborted_errors;
- /* stats->tx_carrier_errors */
- stats->tx_fifo_errors = mib->tx_undersize_errors;
- /* stats->tx_heartbeat_errors */
- stats->tx_window_errors = mib->tx_late_collisions;
-
- stats->rx_packets = pp->rx_frames;
- stats->tx_packets = pp->tx_frames;
- stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
- stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
- stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
- stats->rx_crc_errors + stats->rx_frame_errors;
- stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
- stats->tx_window_errors;
- stats->rx_dropped = mib->rx_dropped;
- /* stats->tx_dropped */
- stats->multicast = mib->rx_multicast;
- stats->collisions = mib->tx_collisions;
-}
-
-static void
-yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
- struct ethtool_pause_stats *pause_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
-
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- pause_stats->tx_pause_frames = mib->tx_pause;
- pause_stats->rx_pause_frames = mib->rx_pause;
-}
-
static int
yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
{
@@ -3953,11 +3592,14 @@ yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ struct yt921x_mib *pm;
int port = dp->index;
int res;
/* No need to sync; port control block is hold until device remove */
- cancel_delayed_work(&priv->ports[port].mib_read);
+ pm = priv->ports[port].mib;
+ if (pm)
+ cancel_delayed_work(&pm->work);
mutex_lock(&priv->reg_lock);
res = yt921x_port_down(priv, port);
@@ -3976,6 +3618,7 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ struct yt921x_mib *pm;
int port = dp->index;
int res;
@@ -3988,7 +3631,9 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
port, res);
- schedule_delayed_work(&priv->ports[port].mib_read, 0);
+ pm = priv->ports[port].mib;
+ if (pm)
+ schedule_delayed_work(&pm->work, 0);
}
static void
@@ -4108,11 +3753,41 @@ yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
return DSA_TAG_PROTO_YT921X;
}
+static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+
+ /* get_stats64() might not be synchronized with port_teardown(), hold
+ * the MIB block till the device teardown
+ */
+ if (pm)
+ disable_delayed_work_sync(&pm->work);
+}
+
static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct device *dev = to_device(priv);
+ struct yt921x_mib *pm = pp->mib;
int res;
+ if (pm) {
+ enable_delayed_work(&pm->work);
+ } else if (BIT(port) & (priv->info->internal_mask |
+ priv->info->external_mask)) {
+ pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);
+ if (!pm)
+ return -ENOMEM;
+ pp->mib = pm;
+
+ pm->port = pp;
+ u64_stats_init(&pm->syncp);
+ INIT_DELAYED_WORK(&pm->work, yt921x_mib_poll);
+ }
+
mutex_lock(&priv->reg_lock);
res = yt921x_port_setup(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -4667,6 +4342,7 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
/* port */
.get_tag_protocol = yt921x_dsa_get_tag_protocol,
.phylink_get_caps = yt921x_dsa_phylink_get_caps,
+ .port_teardown = yt921x_dsa_port_teardown,
.port_setup = yt921x_dsa_port_setup,
#if IS_ENABLED(CONFIG_DCB)
/* dscp */
@@ -4696,12 +4372,6 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)
if (!priv)
return;
- for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
- struct yt921x_port *pp = &priv->ports[i];
-
- disable_delayed_work_sync(&pp->mib_read);
- }
-
dsa_unregister_switch(&priv->ds);
for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) {
@@ -4749,7 +4419,6 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
struct yt921x_port *pp = &priv->ports[i];
pp->index = i;
- INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
}
ds = &priv->ds;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 83cd454955ddb..b2e39b70bf61e 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -167,69 +167,6 @@
#define YT921X_EEE_CTRL 0xb0000
#define YT921X_EEE_CTRL_ENn(port) BIT(port)
-#define YT921X_MIB_CTRL 0xc0004
-#define YT921X_MIB_CTRL_CLEAN BIT(30)
-#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3)
-#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
-#define YT921X_MIB_CTRL_ONE_PORT BIT(1)
-#define YT921X_MIB_CTRL_ALL_PORT BIT(0)
-#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port))
-#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x))
-#define YT921X_MIB_DATA_RX_BROADCAST 0x00
-#define YT921X_MIB_DATA_RX_PAUSE 0x04
-#define YT921X_MIB_DATA_RX_MULTICAST 0x08
-#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c
-
-#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10
-#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14
-#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18
-#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c
-
-#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20
-#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24
-#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28
-#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c
-
-#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30
-#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34
-/* 0x38: unused */
-#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c
-
-/* 0x40: 64 bytes */
-#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44
-/* 0x48: 64 bytes */
-#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c
-
-#define YT921X_MIB_DATA_RX_DROPPED 0x50
-#define YT921X_MIB_DATA_TX_BROADCAST 0x54
-#define YT921X_MIB_DATA_TX_PAUSE 0x58
-#define YT921X_MIB_DATA_TX_MULTICAST 0x5c
-
-#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60
-#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64
-#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68
-#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c
-
-#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70
-#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74
-#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78
-#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c
-
-/* 0x80: unused */
-#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84
-/* 0x88: 64 bytes */
-#define YT921X_MIB_DATA_TX_COLLISION 0x8c
-
-#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90
-#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94
-#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98
-#define YT921X_MIB_DATA_TX_PKT 0x9c
-
-#define YT921X_MIB_DATA_TX_DEFERRED 0xa0
-#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4
-#define YT921X_MIB_DATA_RX_OAM 0xa8
-#define YT921X_MIB_DATA_TX_OAM 0xac
-
#define YT921X_EDATA_CTRL 0xe0000
#define YT921X_EDATA_CTRL_ADDR_M GENMASK(15, 8)
#define YT921X_EDATA_CTRL_ADDR(x) FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x))
@@ -861,58 +798,6 @@ enum yt921x_fdb_entry_status {
#define yt921x_port_is_internal(port) ((port) < 8)
#define yt921x_port_is_external(port) ((port) == 8 || (port) == 9)
-struct yt921x_mib {
- u64 rx_broadcast;
- u64 rx_pause;
- u64 rx_multicast;
- u64 rx_crc_errors;
-
- u64 rx_alignment_errors;
- u64 rx_undersize_errors;
- u64 rx_fragment_errors;
- u64 rx_64byte;
-
- u64 rx_65_127byte;
- u64 rx_128_255byte;
- u64 rx_256_511byte;
- u64 rx_512_1023byte;
-
- u64 rx_1024_1518byte;
- u64 rx_jumbo;
- u64 rx_good_bytes;
-
- u64 rx_bad_bytes;
- u64 rx_oversize_errors;
-
- u64 rx_dropped;
- u64 tx_broadcast;
- u64 tx_pause;
- u64 tx_multicast;
-
- u64 tx_undersize_errors;
- u64 tx_64byte;
- u64 tx_65_127byte;
- u64 tx_128_255byte;
-
- u64 tx_256_511byte;
- u64 tx_512_1023byte;
- u64 tx_1024_1518byte;
- u64 tx_jumbo;
-
- u64 tx_good_bytes;
- u64 tx_collisions;
-
- u64 tx_aborted_errors;
- u64 tx_multiple_collisions;
- u64 tx_single_collisions;
- u64 tx_good;
-
- u64 tx_deferred;
- u64 tx_late_collisions;
- u64 rx_oam;
- u64 tx_oam;
-};
-
struct yt921x_acl_entry {
u32 key[2];
u32 mask[2];
@@ -939,11 +824,6 @@ struct yt921x_port {
bool hairpin;
bool isolated;
- struct delayed_work mib_read;
- 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;
@@ -953,6 +833,8 @@ struct yt921x_port {
struct yt921x_led *leds[YT921X_LED_GROUP_NUM];
#endif
+
+ struct yt921x_mib *mib;
};
struct yt921x_reg_ops {
@@ -989,4 +871,7 @@ struct yt921x_priv {
struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM];
};
+#define yt921x_port_to_priv(pp) \
+ container_of_const((pp), struct yt921x_priv, ports[(pp)->index])
+
#endif
diff --git a/drivers/net/dsa/motorcomm/leds.c b/drivers/net/dsa/motorcomm/leds.c
index 87a6b9cf60682..dd5b3684250db 100644
--- a/drivers/net/dsa/motorcomm/leds.c
+++ b/drivers/net/dsa/motorcomm/leds.c
@@ -12,8 +12,6 @@
#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)
@@ -321,7 +319,7 @@ yt921x_cled_brightness_set_blocking(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 yt921x_priv *priv = yt921x_port_to_priv(pp);
int res;
mutex_lock(&priv->reg_lock);
@@ -337,7 +335,7 @@ yt921x_cled_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
{
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 yt921x_priv *priv = yt921x_port_to_priv(pp);
int res;
mutex_lock(&priv->reg_lock);
@@ -353,7 +351,7 @@ 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 yt921x_priv *priv = yt921x_port_to_priv(pp);
struct dsa_port *dp;
dp = dsa_to_port(&priv->ds, pp->index);
@@ -374,7 +372,7 @@ yt921x_cled_hw_control_is_supported(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 yt921x_priv *priv = yt921x_port_to_priv(pp);
if (yt921x_led_trigger_is_supported(priv, pp->index, led->group, flags))
return 0;
@@ -386,7 +384,7 @@ 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);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
int res;
mutex_lock(&priv->reg_lock);
@@ -401,7 +399,7 @@ 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);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
int res;
mutex_lock(&priv->reg_lock);
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
new file mode 100644
index 0000000000000..2786d9ec29320
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include "chip.h"
+#include "mib.h"
+#include "smi.h"
+
+struct yt921x_mib_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+};
+
+#define MIB_DESC(_size, _offset, _name) \
+ {_size, _offset, _name}
+
+/* Must agree with yt921x_mib_stats
+ *
+ * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
+ * structured go to their *_stats() methods, but we need their sizes and offsets
+ * to perform 32bit MIB overflow wraparound.
+ */
+static const struct yt921x_mib_desc yt921x_mib_descs[] = {
+ MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
+ MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
+
+ MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
+
+ MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
+};
+
+static_assert(ARRAY_SIZE(yt921x_mib_descs) == YT921X_MIB_STAT_NUM);
+
+/* The interval should be small enough to avoid overflow of 32bit MIBs.
+ *
+ * Until we can read MIBs from stats64 call directly (i.e. sleep
+ * there), we have to poll stats more frequently then it is actually needed.
+ * For overflow protection, normally, 100 sec interval should have been OK.
+ */
+#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ)
+
+#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)
+#define to_device(priv) ((priv)->ds.dev)
+
+/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
+static int yt921x_mib_read(struct yt921x_priv *priv, int port)
+{
+ struct yt921x_port *pp = &priv->ports[port];
+ struct device *dev = to_device(priv);
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+ u64 *buf = pm->data;
+ unsigned long flags;
+ u64 rx_frames;
+ u64 tx_frames;
+ int res = 0;
+
+ mib = &pm->stats;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+ u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
+
+ if (desc->size <= 1) {
+ u64 old_val = buf[i];
+ u32 val0;
+ u64 val;
+
+ res = yt921x_reg_read(priv, reg, &val0);
+ if (res)
+ break;
+
+ val = (old_val & ~(u64)U32_MAX) | val0;
+ if (val < old_val)
+ val += 1ull << 32;
+
+ buf[i] = val;
+ } else {
+ res = yt921x_counter_read(priv, reg, &buf[i]);
+ if (res)
+ break;
+ }
+ }
+
+ flags = u64_stats_update_begin_irqsave(&pm->syncp);
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++)
+ u64_stats_set(&((u64_stats_t *)mib)[i], buf[i]);
+ rx_frames = u64_stats_read(&mib->rx_64byte) +
+ u64_stats_read(&mib->rx_65_127byte) +
+ u64_stats_read(&mib->rx_128_255byte) +
+ u64_stats_read(&mib->rx_256_511byte) +
+ u64_stats_read(&mib->rx_512_1023byte) +
+ u64_stats_read(&mib->rx_1024_1518byte) +
+ u64_stats_read(&mib->rx_jumbo);
+ tx_frames = u64_stats_read(&mib->tx_64byte) +
+ u64_stats_read(&mib->tx_65_127byte) +
+ u64_stats_read(&mib->tx_128_255byte) +
+ u64_stats_read(&mib->tx_256_511byte) +
+ u64_stats_read(&mib->tx_512_1023byte) +
+ u64_stats_read(&mib->tx_1024_1518byte) +
+ u64_stats_read(&mib->tx_jumbo);
+ u64_stats_set(&pm->rx_frames, rx_frames);
+ u64_stats_set(&pm->tx_frames, tx_frames);
+ u64_stats_update_end_irqrestore(&pm->syncp, flags);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
+ port, res);
+ return res;
+}
+
+void yt921x_mib_poll(struct work_struct *work)
+{
+ struct yt921x_mib *pm = container_of_const(work, struct yt921x_mib,
+ work.work);
+ struct yt921x_port *pp = pm->port;
+ unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
+ int port = pp->index;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_mib_read(priv, port);
+ mutex_unlock(&priv->reg_lock);
+ if (res)
+ delay *= 4;
+
+ schedule_delayed_work(&pm->work, delay);
+}
+
+void
+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data)
+{
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (desc->name)
+ ethtool_puts(&data, desc->name);
+ }
+}
+
+void
+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ u64 *buf;
+ size_t j;
+
+ if (!pm)
+ return;
+ buf = pm->data;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ j = 0;
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (!desc->name)
+ continue;
+
+ data[j] = buf[i];
+ j++;
+ }
+
+ mutex_unlock(&priv->reg_lock);
+}
+
+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+ int cnt = 0;
+
+ if (sset != ETH_SS_STATS)
+ return 0;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (desc->name)
+ cnt++;
+ }
+
+ return cnt;
+}
+
+void
+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ mac_stats->FramesTransmittedOK = u64_stats_read(&pm->tx_frames);
+ mac_stats->SingleCollisionFrames = u64_stats_read(&mib->tx_single_collisions);
+ mac_stats->MultipleCollisionFrames = u64_stats_read(&mib->tx_multiple_collisions);
+ mac_stats->FramesReceivedOK = u64_stats_read(&pm->rx_frames);
+ mac_stats->FrameCheckSequenceErrors = u64_stats_read(&mib->rx_crc_errors);
+ mac_stats->AlignmentErrors = u64_stats_read(&mib->rx_alignment_errors);
+ mac_stats->OctetsTransmittedOK = u64_stats_read(&mib->tx_good_bytes);
+ mac_stats->FramesWithDeferredXmissions = u64_stats_read(&mib->tx_deferred);
+ mac_stats->LateCollisions = u64_stats_read(&mib->tx_late_collisions);
+ mac_stats->FramesAbortedDueToXSColls = u64_stats_read(&mib->tx_aborted_errors);
+ /* mac_stats->FramesLostDueToIntMACXmitError */
+ /* mac_stats->CarrierSenseErrors */
+ mac_stats->OctetsReceivedOK = u64_stats_read(&mib->rx_good_bytes);
+ /* mac_stats->FramesLostDueToIntMACRcvError */
+ mac_stats->MulticastFramesXmittedOK = u64_stats_read(&mib->tx_multicast);
+ mac_stats->BroadcastFramesXmittedOK = u64_stats_read(&mib->tx_broadcast);
+ /* mac_stats->FramesWithExcessiveDeferral */
+ mac_stats->MulticastFramesReceivedOK = u64_stats_read(&mib->rx_multicast);
+ mac_stats->BroadcastFramesReceivedOK = u64_stats_read(&mib->rx_broadcast);
+ /* mac_stats->InRangeLengthErrors */
+ /* mac_stats->OutOfRangeLengthField */
+ mac_stats->FrameTooLongErrors = u64_stats_read(&mib->rx_oversize_errors);
+
+ mutex_unlock(&priv->reg_lock);
+}
+
+void
+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ ctrl_stats->MACControlFramesTransmitted = u64_stats_read(&mib->tx_pause);
+ ctrl_stats->MACControlFramesReceived = u64_stats_read(&mib->rx_pause);
+ /* ctrl_stats->UnsupportedOpcodesReceived */
+
+ mutex_unlock(&priv->reg_lock);
+}
+
+static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
+ { 0, 64 },
+ { 65, 127 },
+ { 128, 255 },
+ { 256, 511 },
+ { 512, 1023 },
+ { 1024, 1518 },
+ { 1519, YT921X_FRAME_SIZE_MAX },
+ {}
+};
+
+void
+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ *ranges = yt921x_rmon_ranges;
+
+ rmon_stats->undersize_pkts = u64_stats_read(&mib->rx_undersize_errors);
+ rmon_stats->oversize_pkts = u64_stats_read(&mib->rx_oversize_errors);
+ rmon_stats->fragments = u64_stats_read(&mib->rx_alignment_errors);
+ /* rmon_stats->jabbers */
+
+ rmon_stats->hist[0] = u64_stats_read(&mib->rx_64byte);
+ rmon_stats->hist[1] = u64_stats_read(&mib->rx_65_127byte);
+ rmon_stats->hist[2] = u64_stats_read(&mib->rx_128_255byte);
+ rmon_stats->hist[3] = u64_stats_read(&mib->rx_256_511byte);
+ rmon_stats->hist[4] = u64_stats_read(&mib->rx_512_1023byte);
+ rmon_stats->hist[5] = u64_stats_read(&mib->rx_1024_1518byte);
+ rmon_stats->hist[6] = u64_stats_read(&mib->rx_jumbo);
+
+ rmon_stats->hist_tx[0] = u64_stats_read(&mib->tx_64byte);
+ rmon_stats->hist_tx[1] = u64_stats_read(&mib->tx_65_127byte);
+ rmon_stats->hist_tx[2] = u64_stats_read(&mib->tx_128_255byte);
+ rmon_stats->hist_tx[3] = u64_stats_read(&mib->tx_256_511byte);
+ rmon_stats->hist_tx[4] = u64_stats_read(&mib->tx_512_1023byte);
+ rmon_stats->hist_tx[5] = u64_stats_read(&mib->tx_1024_1518byte);
+ rmon_stats->hist_tx[6] = u64_stats_read(&mib->tx_jumbo);
+
+ mutex_unlock(&priv->reg_lock);
+}
+
+void
+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+ unsigned int start;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ do {
+ start = u64_stats_fetch_begin(&pm->syncp);
+
+ stats->rx_length_errors = u64_stats_read(&mib->rx_undersize_errors) +
+ u64_stats_read(&mib->rx_fragment_errors);
+ stats->rx_over_errors = u64_stats_read(&mib->rx_oversize_errors);
+ stats->rx_crc_errors = u64_stats_read(&mib->rx_crc_errors);
+ stats->rx_frame_errors = u64_stats_read(&mib->rx_alignment_errors);
+ /* stats->rx_fifo_errors */
+ /* stats->rx_missed_errors */
+
+ stats->tx_aborted_errors = u64_stats_read(&mib->tx_aborted_errors);
+ /* stats->tx_carrier_errors */
+ stats->tx_fifo_errors = u64_stats_read(&mib->tx_undersize_errors);
+ /* stats->tx_heartbeat_errors */
+ stats->tx_window_errors = u64_stats_read(&mib->tx_late_collisions);
+
+ stats->rx_packets = u64_stats_read(&pm->rx_frames);
+ stats->tx_packets = u64_stats_read(&pm->tx_frames);
+ stats->rx_bytes = u64_stats_read(&mib->rx_good_bytes) -
+ ETH_FCS_LEN * stats->rx_packets;
+ stats->tx_bytes = u64_stats_read(&mib->tx_good_bytes) -
+ ETH_FCS_LEN * stats->tx_packets;
+ stats->rx_dropped = u64_stats_read(&mib->rx_dropped);
+ /* stats->tx_dropped */
+ stats->multicast = u64_stats_read(&mib->rx_multicast);
+ stats->collisions = u64_stats_read(&mib->tx_collisions);
+ } while (u64_stats_fetch_retry(&pm->syncp, start));
+
+ stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
+ stats->rx_crc_errors + stats->rx_frame_errors;
+ stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
+ stats->tx_window_errors;
+}
+
+void
+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
+ struct ethtool_pause_stats *pause_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+
+ yt921x_mib_read(priv, port);
+
+ pause_stats->tx_pause_frames = u64_stats_read(&mib->tx_pause);
+ pause_stats->rx_pause_frames = u64_stats_read(&mib->rx_pause);
+
+ mutex_unlock(&priv->reg_lock);
+}
diff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h
new file mode 100644
index 0000000000000..55719c29992ba
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mib.h
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_MIB_H
+#define _YT_MIB_H
+
+#include <linux/u64_stats_sync.h>
+
+#include <net/dsa.h>
+
+#define YT921X_MIB_CTRL 0xc0004
+#define YT921X_MIB_CTRL_CLEAN BIT(30)
+#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3)
+#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
+#define YT921X_MIB_CTRL_ONE_PORT BIT(1)
+#define YT921X_MIB_CTRL_ALL_PORT BIT(0)
+#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port))
+#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x))
+#define YT921X_MIB_DATA_RX_BROADCAST 0x00
+#define YT921X_MIB_DATA_RX_PAUSE 0x04
+#define YT921X_MIB_DATA_RX_MULTICAST 0x08
+#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c
+
+#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10
+#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14
+#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18
+#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20
+#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24
+#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28
+#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30
+#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34
+/* 0x38: unused */
+#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c
+
+/* 0x40: 64 bytes */
+#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44
+/* 0x48: 64 bytes */
+#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c
+
+#define YT921X_MIB_DATA_RX_DROPPED 0x50
+#define YT921X_MIB_DATA_TX_BROADCAST 0x54
+#define YT921X_MIB_DATA_TX_PAUSE 0x58
+#define YT921X_MIB_DATA_TX_MULTICAST 0x5c
+
+#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60
+#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64
+#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68
+#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c
+
+#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70
+#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74
+#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78
+#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c
+
+/* 0x80: unused */
+#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84
+/* 0x88: 64 bytes */
+#define YT921X_MIB_DATA_TX_COLLISION 0x8c
+
+#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90
+#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94
+#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98
+#define YT921X_MIB_DATA_TX_PKT 0x9c
+
+#define YT921X_MIB_DATA_TX_DEFERRED 0xa0
+#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4
+#define YT921X_MIB_DATA_RX_OAM 0xa8
+#define YT921X_MIB_DATA_TX_OAM 0xac
+
+struct yt921x_mib_stats {
+ u64_stats_t rx_broadcast;
+ u64_stats_t rx_pause;
+ u64_stats_t rx_multicast;
+ u64_stats_t rx_crc_errors;
+
+ u64_stats_t rx_alignment_errors;
+ u64_stats_t rx_undersize_errors;
+ u64_stats_t rx_fragment_errors;
+ u64_stats_t rx_64byte;
+
+ u64_stats_t rx_65_127byte;
+ u64_stats_t rx_128_255byte;
+ u64_stats_t rx_256_511byte;
+ u64_stats_t rx_512_1023byte;
+
+ u64_stats_t rx_1024_1518byte;
+ u64_stats_t rx_jumbo;
+ u64_stats_t rx_good_bytes;
+
+ u64_stats_t rx_bad_bytes;
+ u64_stats_t rx_oversize_errors;
+
+ u64_stats_t rx_dropped;
+ u64_stats_t tx_broadcast;
+ u64_stats_t tx_pause;
+ u64_stats_t tx_multicast;
+
+ u64_stats_t tx_undersize_errors;
+ u64_stats_t tx_64byte;
+ u64_stats_t tx_65_127byte;
+ u64_stats_t tx_128_255byte;
+
+ u64_stats_t tx_256_511byte;
+ u64_stats_t tx_512_1023byte;
+ u64_stats_t tx_1024_1518byte;
+ u64_stats_t tx_jumbo;
+
+ u64_stats_t tx_good_bytes;
+ u64_stats_t tx_collisions;
+
+ u64_stats_t tx_aborted_errors;
+ u64_stats_t tx_multiple_collisions;
+ u64_stats_t tx_single_collisions;
+ u64_stats_t tx_good;
+
+ u64_stats_t tx_deferred;
+ u64_stats_t tx_late_collisions;
+ u64_stats_t rx_oam;
+ u64_stats_t tx_oam;
+};
+
+#define YT921X_MIB_STAT_NUM (sizeof(struct yt921x_mib_stats) / sizeof(u64_stats_t))
+
+struct yt921x_mib {
+ struct yt921x_port *port;
+
+ struct delayed_work work;
+ struct u64_stats_sync syncp;
+ /* protected by syncp OR priv->reg_lock */
+ struct yt921x_mib_stats stats;
+ u64_stats_t rx_frames;
+ u64_stats_t tx_frames;
+ /* protected by priv->reg_lock */
+ u64 data[YT921X_MIB_STAT_NUM];
+};
+
+void yt921x_mib_poll(struct work_struct *work);
+void
+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data);
+void
+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset);
+void
+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats);
+void
+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats);
+void
+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges);
+void
+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *stats);
+void
+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
+ struct ethtool_pause_stats *pause_stats);
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c
index bf3adfd64165a..27086602997bb 100644
--- a/drivers/net/dsa/motorcomm/smi.c
+++ b/drivers/net/dsa/motorcomm/smi.c
@@ -63,6 +63,36 @@ int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
return yt921x_reg_write(priv, reg, u);
}
+/* Reliably read a 64bit counter */
+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp)
+{
+ u32 old_lo;
+ int res;
+ u32 hi;
+ u32 lo;
+
+ res = yt921x_reg_read(priv, reg, &old_lo);
+ if (res)
+ return res;
+
+ for (int i = 0; i < 16; i++) {
+ res = yt921x_reg_read(priv, reg + 4, &hi);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, reg, &lo);
+ if (res)
+ return res;
+
+ if (lo >= old_lo) {
+ *valp = ((u64)hi << 32) | lo;
+ return 0;
+ }
+ old_lo = lo;
+ }
+
+ return -ETIMEDOUT;
+}
+
static int
yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
unsigned int num_regs)
diff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h
index 212e20f71d806..d34240548af7e 100644
--- a/drivers/net/dsa/motorcomm/smi.h
+++ b/drivers/net/dsa/motorcomm/smi.h
@@ -35,6 +35,8 @@ 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);
}
+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp);
+
/* 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.
|
| 3/2 | 2026/09/23 20:59 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24807 output: 35 thoughts: 272 Thoughts: **Analyzing the Motorcomm YT921X Driver Patch** Okay, so this patch is clearly focused on the Motorcomm YT921X Ethernet switch driver, specifically within the `drivers/net/dsa/motorcomm/` directory. I see it's touching the `Makefile`, `chip.c`, `chip.h`, `leds.c`, `mib.c`, `mib.h`, `smi.c`, and `smi.h` files. That indicates some pretty significant changes, probably touching core driver functionality, MIB handling, and SMI communication. Given this is a DSA driver, it's designed for Distributed Switch Architectures. My immediate concern is, can I readily access this hardware for testing? I need to figure out if I can get to it in a standard virtualized environment like QEMU or using something like syzkaller. If not, it could make debugging tricky. To get a better handle on the hardware interaction, I need to understand the bus it probes on. Let's see if the Kconfig provides any clues. I’ll either use `grepper` to search the codebase or I'll look directly at the Kconfig files in both `drivers/net/dsa/motorcomm/` and `drivers/net/dsa/` directories to see how this driver is configured and what it depends on. |
| 4/2 | 2026/09/23 20:59 | tool | grepper |
0mArgs:{
"Expression": "NET_DSA_YT921X",
"PathPrefix": "drivers/net/dsa"
}Results: {
"Output": "drivers/net/dsa/motorcomm/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\ndrivers/net/dsa/motorcomm/Kconfig:2:config NET_DSA_YT921X\ndrivers/net/dsa/motorcomm/Kconfig-3-\ttristate \"Motorcomm YT9215 ethernet switch chip support\"\n--\ndrivers/net/dsa/motorcomm/Kconfig-9-\ndrivers/net/dsa/motorcomm/Kconfig:10:config NET_DSA_YT921X_LEDS\ndrivers/net/dsa/motorcomm/Kconfig-11-\tbool \"LED support for Motorcomm YT9215\"\ndrivers/net/dsa/motorcomm/Kconfig-12-\tdefault y\ndrivers/net/dsa/motorcomm/Kconfig:13:\tdepends on NET_DSA_YT921X\ndrivers/net/dsa/motorcomm/Kconfig:14:\tdepends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_YT921X\ndrivers/net/dsa/motorcomm/Kconfig-15-\thelp\n--\ndrivers/net/dsa/motorcomm/Makefile-1-# SPDX-License-Identifier: GPL-2.0\ndrivers/net/dsa/motorcomm/Makefile:2:obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o\ndrivers/net/dsa/motorcomm/Makefile-3-yt921x-objs := chip.o\ndrivers/net/dsa/motorcomm/Makefile:4:yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o\ndrivers/net/dsa/motorcomm/Makefile-5-yt921x-objs += mib.o\n--\ndrivers/net/dsa/motorcomm/chip.c=3902=static void yt921x_dsa_teardown(struct dsa_switch *ds)\ndrivers/net/dsa/motorcomm/chip.c-3903-{\ndrivers/net/dsa/motorcomm/chip.c:3904:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.c-3905-\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n--\ndrivers/net/dsa/motorcomm/chip.c=4221=static int yt921x_dsa_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-4266-\ndrivers/net/dsa/motorcomm/chip.c:4267:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.c-4268-\tres = yt921x_leds_setup(priv);\n--\ndrivers/net/dsa/motorcomm/chip.h=821=struct yt921x_port {\n--\ndrivers/net/dsa/motorcomm/chip.h-826-\ndrivers/net/dsa/motorcomm/chip.h:827:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/chip.h-828-\tunsigned char led_duty;\n--\ndrivers/net/dsa/motorcomm/leds.h=96=struct yt921x_led {\n--\ndrivers/net/dsa/motorcomm/leds.h-101-\ndrivers/net/dsa/motorcomm/leds.h:102:#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)\ndrivers/net/dsa/motorcomm/leds.h-103-\n"
} |
| 5/2 | 2026/09/23 20:59 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26371 output: 38 thoughts: 18 Thoughts: Here's a summary of my thought process, as requested: **Diving into the Motorcomm DSA Kconfig** Okay, so I need to get a handle on the Motorcomm DSA configuration. My first move is to check the `drivers/net/dsa/motorcomm/Kconfig` file. That's the logical starting point – the Kconfig file should contain all the configuration options related to the Motorcomm DSA driver, defining the dependencies, default values, and help text. I'm expecting to find the core driver configuration along with any specific features or hardware support related to Motorcomm's DSA implementations. I'll need to understand how they've structured their configuration options, what dependencies they've set up with other parts of the kernel, and how well documented these options are. This will tell me a lot about the design philosophy and the potential level of customization available. I'm hoping to quickly identify the key variables I need to understand better. This Kconfig file will be the launchpad for my deeper understanding. |
| 6/2 | 2026/09/23 20:59 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/Kconfig",
"FirstLine": 1,
"LineCount": 30
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tconfig NET_DSA_YT921X\n 3:\t\ttristate \"Motorcomm YT9215 ethernet switch chip support\"\n 4:\t\tselect NET_DSA_TAG_YT921X\n 5:\t\tselect NET_IEEE8021Q_HELPERS if DCB\n 6:\t\thelp\n 7:\t\t This enables support for the Motorcomm YT9215 ethernet switch\n 8:\t\t chip.\n 9:\t\n 10:\tconfig NET_DSA_YT921X_LEDS\n 11:\t\tbool \"LED support for Motorcomm YT9215\"\n 12:\t\tdefault y\n 13:\t\tdepends on NET_DSA_YT921X\n 14:\t\tdepends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_YT921X\n 15:\t\thelp\n 16:\t\t This enables support for controlling the LEDs attached to the\n 17:\t\t Motorcomm YT9215 switch chips.\n"
} |
| 7/2 | 2026/09/23 20:59 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26919 output: 39 thoughts: 36 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing `yt921x` Device Registration** Alright, let's dive in. My immediate thought is to understand the integration of `yt921x` into the system – specifically, how it's interfacing with the hardware bus. I need to figure out where it's being registered, how it's being probed. I'll start by searching for the standard driver initialization routines. I expect this to be MDIO, so I will start by searching for either a `module_mdio_driver` declaration, or the probe function within that driver structure. Finding this will give me the entry point and, critically, how the device is identified and connected. |
| 8/2 | 2026/09/23 20:59 | tool | grepper |
0mArgs:{
"Expression": "yt921x_mdio_probe",
"PathPrefix": "drivers/net/dsa/motorcomm"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4368=static void yt921x_mdio_remove(struct mdio_device *mdiodev)\n--\ndrivers/net/dsa/motorcomm/chip.c-4393-\ndrivers/net/dsa/motorcomm/chip.c:4394:static int yt921x_mdio_probe(struct mdio_device *mdiodev)\ndrivers/net/dsa/motorcomm/chip.c-4395-{\n--\ndrivers/net/dsa/motorcomm/chip.c=4447=static struct mdio_driver yt921x_mdio_driver = {\ndrivers/net/dsa/motorcomm/chip.c:4448:\t.probe = yt921x_mdio_probe,\ndrivers/net/dsa/motorcomm/chip.c-4449-\t.remove = yt921x_mdio_remove,\n"
} |
| 9/2 | 2026/09/23 20:59 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27404 output: 42 thoughts: 38 Thoughts: Okay, here's my assessment of the situation: **Investigating the `yt921x_mdio_driver` Registration** Alright, let's get down to brass tacks. I need to understand how the `yt921x_mdio_driver` is being registered within this system. My first instinct is to dig directly into the code and trace the execution path. So, I'm going to jump into `chip.c` and zero in on that area. Based on previous experience with similar drivers, line 4447 sounds like a likely spot to begin my investigation. I'm expecting to find the `module_init` or similar registration function call there. Let's see what we can find. |
| 10/2 | 2026/09/23 20:59 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4445,
"LineCount": 25
}Results: {
"Contents": "4445:\tMODULE_DEVICE_TABLE(of, yt921x_of_match);\n4446:\t\n4447:\tstatic struct mdio_driver yt921x_mdio_driver = {\n4448:\t\t.probe = yt921x_mdio_probe,\n4449:\t\t.remove = yt921x_mdio_remove,\n4450:\t\t.shutdown = yt921x_mdio_shutdown,\n4451:\t\t.mdiodrv.driver = {\n4452:\t\t\t.name = YT921X_NAME,\n4453:\t\t\t.of_match_table = yt921x_of_match,\n4454:\t\t},\n4455:\t};\n4456:\t\n4457:\tmdio_module_driver(yt921x_mdio_driver);\n4458:\t\n4459:\tMODULE_AUTHOR(\"David Yang \u003cmmyangfl@gmail.com\u003e\");\n4460:\tMODULE_DESCRIPTION(\"Driver for Motorcomm YT921x Switch\");\n4461:\tMODULE_LICENSE(\"GPL\");\n"
} |
| 11/2 | 2026/09/23 20:59 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27946 output: 29 thoughts: 535 Thoughts: **Assessing the Fuzzability of the MDIO Driver** Okay, so I'm looking at this `mdio_driver yt921x_mdio_driver`, which is matched through Device Tree using `yt921x_of_match`. My immediate concern, as always when evaluating fuzzing potential, is reachability. Can I actually get this device probed and running in a standard virtualized environment like QEMU or even syzkaller, without needing specific physical hardware? The first thing that pops into my head is, what is syzkaller's MDIO support? Does it instantiate MDIO devices? Does it understand Device Tree setups on amd64 architectures? I need to know if the target can even be instantiated on my testing platform. Then I am reminded of the CORE TRIAGE PHILOSOPHY guide, especially point number one: Reachability is key. Fuzzing is a complete waste of effort on unreachable code. If it doesn't execute in GCE or QEMU, forget about it. Section 2 is very clear about the negative criteria for fuzzing: "Structurally unreachable hardware." This is where I start to get a bit nervous. Is an MDIO driver or a DSA switch driver considered "structurally unreachable hardware"? I need to clarify this. Aha! Let's clarify what this "Motorcomm YT9215" is. A quick search... It is an Ethernet switch chip, connected via MDIO/SMI bus, and matched via Device Tree, as expected: `MODULE_DEVICE_TABLE(of, yt921x_of_match);` and `mdio_module_driver(yt921x_mdio_driver);`. The big question now is this: can syzkaller or userspace somehow *create* MDIO devices or emulate an MDIO bus with `yt921x`? That's what I need to figure out. I'm going to scan the kernel tree for any user-accessible interface that can instantiate an `mdio_device` or `mdio_driver`. This will be the key to determining whether this module is even worth pursuing. |
| 12/2 | 2026/09/23 20:59 | tool | grepper |
0mArgs:{
"Expression": "mdio_device_create",
"PathPrefix": "drivers/net"
}Results: {
"Output": "drivers/net/dsa/dsa_loop.c=446=static int __init dsa_loop_create_switch_mdiodev(void)\n--\ndrivers/net/dsa/dsa_loop.c-466-\ndrivers/net/dsa/dsa_loop.c:467:\tswitch_mdiodev = mdio_device_create(bus, 31);\ndrivers/net/dsa/dsa_loop.c-468-\tif (IS_ERR(switch_mdiodev))\n--\ndrivers/net/mdio/of_mdio.c=52=static int of_mdiobus_register_device(struct mii_bus *mdio,\n--\ndrivers/net/mdio/of_mdio.c-58-\ndrivers/net/mdio/of_mdio.c:59:\tmdiodev = mdio_device_create(mdio, addr);\ndrivers/net/mdio/of_mdio.c-60-\tif (IS_ERR(mdiodev))\n--\ndrivers/net/pcs/pcs-lynx.c=310=struct phylink_pcs *lynx_pcs_create_mdiodev(struct mii_bus *bus, int addr)\n--\ndrivers/net/pcs/pcs-lynx.c-314-\ndrivers/net/pcs/pcs-lynx.c:315:\tmdio = mdio_device_create(bus, addr);\ndrivers/net/pcs/pcs-lynx.c-316-\tif (IS_ERR(mdio))\n--\ndrivers/net/pcs/pcs-xpcs-plat.c=337=static int xpcs_plat_init_dev(struct dw_xpcs_plat *pxpcs)\n--\ndrivers/net/pcs/pcs-xpcs-plat.c-343-\t/* There is a single memory-mapped DW XPCS device */\ndrivers/net/pcs/pcs-xpcs-plat.c:344:\tmdiodev = mdio_device_create(pxpcs-\u003ebus, 0);\ndrivers/net/pcs/pcs-xpcs-plat.c-345-\tif (IS_ERR(mdiodev))\n--\ndrivers/net/pcs/pcs-xpcs.c=1621=struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr)\n--\ndrivers/net/pcs/pcs-xpcs.c-1625-\ndrivers/net/pcs/pcs-xpcs.c:1626:\tmdiodev = mdio_device_create(bus, addr);\ndrivers/net/pcs/pcs-xpcs.c-1627-\tif (IS_ERR(mdiodev))\n--\ndrivers/net/phy/air_en8811h.c=1131=static int an8811hb_probe(struct phy_device *phydev)\n--\ndrivers/net/phy/air_en8811h.c-1150-\t */\ndrivers/net/phy/air_en8811h.c:1151:\tmdiodev = mdio_device_create(phydev-\u003emdio.bus,\ndrivers/net/phy/air_en8811h.c-1152-\t\t\t\t phydev-\u003emdio.addr + EN8811H_PBUS_ADDR_OFFS);\n--\ndrivers/net/phy/mdio_device.c=113=static void mdio_device_release(struct device *dev)\n--\ndrivers/net/phy/mdio_device.c-118-\ndrivers/net/phy/mdio_device.c:119:struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)\ndrivers/net/phy/mdio_device.c-120-{\n--\ndrivers/net/phy/mdio_device.c-142-}\ndrivers/net/phy/mdio_device.c:143:EXPORT_SYMBOL(mdio_device_create);\ndrivers/net/phy/mdio_device.c-144-\n"
} |
| 13/2 | 2026/09/23 20:59 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29991 output: 38 thoughts: 84 Thoughts: Here's my analysis of the situation: **Comparative Driver Analysis** Okay, so I'm looking at `dsa_loop.c` versus `chip.c`. My initial thought is that `dsa_loop.c` is clearly a mock driver, a loopback setup designed for testing DSA functionality – that's expected. It's designed to simulate the DSA interface without real hardware. Then, I see `yt921x`, which immediately screams "specific hardware." It's a Motorcomm YT9215 switch driver – so this is our target. Now the question is, how does this driver work? Let me check the `yt921x_of_match` table. I want to see how the device is identified and how it is bound to the driver. This match table will tell me everything, for now. |
| 14/2 | 2026/09/23 21:00 | tool | grepper |
0mArgs:{
"Expression": "yt921x_of_match",
"PathPrefix": "drivers/net/dsa/motorcomm"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=4394=static int yt921x_mdio_probe(struct mdio_device *mdiodev)\n--\ndrivers/net/dsa/motorcomm/chip.c-4440-\ndrivers/net/dsa/motorcomm/chip.c:4441:static const struct of_device_id yt921x_of_match[] = {\ndrivers/net/dsa/motorcomm/chip.c-4442-\t{ .compatible = \"motorcomm,yt9215\" },\n--\ndrivers/net/dsa/motorcomm/chip.c-4444-};\ndrivers/net/dsa/motorcomm/chip.c:4445:MODULE_DEVICE_TABLE(of, yt921x_of_match);\ndrivers/net/dsa/motorcomm/chip.c-4446-\ndrivers/net/dsa/motorcomm/chip.c=4447=static struct mdio_driver yt921x_mdio_driver = {\n--\ndrivers/net/dsa/motorcomm/chip.c-4452-\t\t.name = YT921X_NAME,\ndrivers/net/dsa/motorcomm/chip.c:4453:\t\t.of_match_table = yt921x_of_match,\ndrivers/net/dsa/motorcomm/chip.c-4454-\t},\n"
} |
| 15/2 | 2026/09/23 21:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30660 output: 42 thoughts: 117 Thoughts: **Analyzing the `yt921x_of_match` Structure and Driver Binding** Okay, so I see `yt921x_of_match` declares `.compatible = "motorcomm,yt9215"`. That immediately tells me this driver is specifically designed to bind to MDIO devices declared in the Device Tree that *also* have that "motorcomm,yt9215" compatibility string. This is the first place I'd look to understand the intended hardware target. Now, let's see what actually happens during the probe stage. Time to dive into `yt921x_mdio_probe(struct mdio_device *mdiodev)`. I'll be checking `drivers/net/dsa/motorcomm/chip.c` for the implementation. |
| 16/2 | 2026/09/23 21:00 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4390,
"LineCount": 55
}Results: {
"Contents": "4390:\t\t}\n4391:\t\tmutex_destroy(\u0026priv-\u003ereg_lock);\n4392:\t}\n4393:\t\n4394:\tstatic int yt921x_mdio_probe(struct mdio_device *mdiodev)\n4395:\t{\n4396:\t\tstruct device *dev = \u0026mdiodev-\u003edev;\n4397:\t\tstruct yt921x_reg_mdio *mdio;\n4398:\t\tstruct yt921x_priv *priv;\n4399:\t\tstruct dsa_switch *ds;\n4400:\t\n4401:\t\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n4402:\t\tif (!priv)\n4403:\t\t\treturn -ENOMEM;\n4404:\t\n4405:\t\tmdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);\n4406:\t\tif (!mdio)\n4407:\t\t\treturn -ENOMEM;\n4408:\t\n4409:\t\tmdio-\u003ebus = mdiodev-\u003ebus;\n4410:\t\tmdio-\u003eaddr = mdiodev-\u003eaddr;\n4411:\t\tmdio-\u003eswitchid = 0;\n4412:\t\n4413:\t\tmutex_init(\u0026priv-\u003ereg_lock);\n4414:\t\n4415:\t\tpriv-\u003ereg_ops = \u0026yt921x_reg_ops_mdio;\n4416:\t\tpriv-\u003ereg_ctx = mdio;\n4417:\t\n4418:\t\tfor (size_t i = 0; i \u003c ARRAY_SIZE(priv-\u003eports); i++) {\n4419:\t\t\tstruct yt921x_port *pp = \u0026priv-\u003eports[i];\n4420:\t\n4421:\t\t\tpp-\u003eindex = i;\n4422:\t\t}\n4423:\t\n4424:\t\tds = \u0026priv-\u003eds;\n4425:\t\tds-\u003edev = dev;\n4426:\t\tds-\u003eassisted_learning_on_cpu_port = true;\n4427:\t\tds-\u003edscp_prio_mapping_is_global = true;\n4428:\t\tds-\u003epriv = priv;\n4429:\t\tds-\u003eops = \u0026yt921x_dsa_switch_ops;\n4430:\t\tds-\u003eageing_time_min = 1 * 5000;\n4431:\t\tds-\u003eageing_time_max = U16_MAX * 5000;\n4432:\t\tds-\u003ephylink_mac_ops = \u0026yt921x_phylink_mac_ops;\n4433:\t\tds-\u003enum_lag_ids = YT921X_LAG_NUM;\n4434:\t\tds-\u003enum_ports = YT921X_PORT_NUM;\n4435:\t\n4436:\t\tmdiodev_set_drvdata(mdiodev, priv);\n4437:\t\n4438:\t\treturn dsa_register_switch(ds);\n4439:\t}\n4440:\t\n4441:\tstatic const struct of_device_id yt921x_of_match[] = {\n4442:\t\t{ .compatible = \"motorcomm,yt9215\" },\n4443:\t\t{}\n4444:\t};\n"
} |
| 17/2 | 2026/09/23 21:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31876 output: 42 thoughts: 101 Thoughts: Here's my understanding of the situation, tailored for my expert perspective: **Analyzing the yt921x Driver Initialization** Okay, so I'm looking at the `yt921x_mdio_probe` function. I see it registers a DSA switch using `dsa_register_switch(ds)`. That's standard DSA initialization. Now, where things get interesting is the connection to the hardware itself. The `yt921x_dsa_setup` function is where the driver interacts with the physical switch. Specifically, it's using `priv->reg_ops->read` and presumably other `reg_ops` calls for communication. The crucial point here is the connection to the MDIO interface. `read` suggests that these are the registers the switch is using for MDIO, so this is how the driver will configure and manage the switch's functionality. Now, I need to dig deeper into `yt921x_dsa_setup`. That's where the real magic, or potential issues, are going to manifest. Let's see exactly what's going on during that setup process. This is where I'll get the real feel for the driver's MDIO register interactions and how it's setting up the switch's ports and features. |
| 18/2 | 2026/09/23 21:00 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 4220,
"LineCount": 45
}Results: {
"Contents": "4220:\t\n4221:\tstatic int yt921x_dsa_setup(struct dsa_switch *ds)\n4222:\t{\n4223:\t\tstruct yt921x_priv *priv = to_yt921x_priv(ds);\n4224:\t\tstruct device *dev = to_device(priv);\n4225:\t\tstruct device_node *np = dev-\u003eof_node;\n4226:\t\tstruct device_node *child;\n4227:\t\tint res;\n4228:\t\n4229:\t\tmutex_lock(\u0026priv-\u003ereg_lock);\n4230:\t\tres = yt921x_chip_reset(priv);\n4231:\t\tmutex_unlock(\u0026priv-\u003ereg_lock);\n4232:\t\n4233:\t\tif (res)\n4234:\t\t\treturn res;\n4235:\t\n4236:\t\t/* Register the internal mdio bus. Nodes for internal ports should have\n4237:\t\t * proper phy-handle pointing to their PHYs. Not enabling the internal\n4238:\t\t * bus is possible, though pretty wired, if internal ports are not used.\n4239:\t\t */\n4240:\t\tchild = of_get_child_by_name(np, \"mdio\");\n4241:\t\tif (child) {\n4242:\t\t\tres = yt921x_mbus_int_init(priv, child);\n4243:\t\t\tof_node_put(child);\n4244:\t\t\tif (res)\n4245:\t\t\t\treturn res;\n4246:\t\t}\n4247:\t\n4248:\t\t/* External mdio bus is optional */\n4249:\t\tchild = of_get_child_by_name(np, \"mdio-external\");\n4250:\t\tif (child) {\n4251:\t\t\tres = yt921x_mbus_ext_init(priv, child);\n4252:\t\t\tof_node_put(child);\n4253:\t\t\tif (res)\n4254:\t\t\t\treturn res;\n4255:\t\n4256:\t\t\tdev_err(dev, \"Untested external mdio bus\\n\");\n4257:\t\t\treturn -ENODEV;\n4258:\t\t}\n4259:\t\n4260:\t\tmutex_lock(\u0026priv-\u003ereg_lock);\n4261:\t\tres = yt921x_chip_setup(priv);\n4262:\t\tmutex_unlock(\u0026priv-\u003ereg_lock);\n4263:\t\n4264:\t\tif (res)\n"
} |
| 19/2 | 2026/09/23 21:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32975 output: 38 thoughts: 20 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Analyzing the `yt921x_chip_reset` Function** Okay, so I'm looking at `yt921x_chip_reset`, and the initial clue here is the name itself. It's obviously designed for a hardware reset, specifically related to a `yt921x` chip – immediately, I'm thinking specific hardware. The core functionality seems pretty straightforward; it's reading chip information. The immediate implications are clear, I would be looking for some form of identification of the chip. What are my questions? First, is this initialization information, validation data, or simply diagnostic output? Knowing which will dictate my further process and priorities. How is the data being read? Memory-mapped I/O? SPI? I2C? Then, what is the structure of this “chip info”? What registers are being read? What do the read values *mean*? I’m immediately expecting that the information retrieved provides the ability to handle a known set of problems, as well as handle different versions of the hardware. The code should incorporate error checking and graceful failure scenarios, but if it doesn't, it might indicate that there is an existing known issue, or it needs to be updated. It could also reveal the chip revision, identifying the silicon stepping. Based on the returned data, the function likely branches the reset sequence based on what’s found. |
| 20/2 | 2026/09/23 21:00 | tool | grepper |
0mArgs:{
"Expression": "yt921x_chip_reset",
"PathPrefix": "drivers/net/dsa/motorcomm"
}Results: {
"Output": "drivers/net/dsa/motorcomm/chip.c=3911=static int yt921x_chip_detect(struct yt921x_priv *priv)\n--\ndrivers/net/dsa/motorcomm/chip.c-3977-\ndrivers/net/dsa/motorcomm/chip.c:3978:static int yt921x_chip_reset(struct yt921x_priv *priv)\ndrivers/net/dsa/motorcomm/chip.c-3979-{\n--\ndrivers/net/dsa/motorcomm/chip.c=4221=static int yt921x_dsa_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/motorcomm/chip.c-4229-\tmutex_lock(\u0026priv-\u003ereg_lock);\ndrivers/net/dsa/motorcomm/chip.c:4230:\tres = yt921x_chip_reset(priv);\ndrivers/net/dsa/motorcomm/chip.c-4231-\tmutex_unlock(\u0026priv-\u003ereg_lock);\n"
} |
| 21/2 | 2026/09/23 21:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33620 output: 42 thoughts: 15 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind the user is an expert: **Investigating the `yt921x_chip_detect` Function** Alright, time to get to it. Let's see... the mental checklist kicks in: I need to start by examining the `yt921x_chip_detect` function. It's the logical starting point. Presumably, it's responsible for identifying the specific chipset we're dealing with – vital for any subsequent configuration or operation. I'll need to drill down into its implementation to understand how it's going about the detection process. Is it using device IDs, probing registers, or employing some more advanced identification method? A quick look at the source should illuminate the key aspects. Knowing the detection method will help me troubleshoot if there are any issues at any step later. |
| 22/2 | 2026/09/23 21:00 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/motorcomm/chip.c",
"FirstLine": 3910,
"LineCount": 40
}Results: {
"Contents": "3910:\t\n3911:\tstatic int yt921x_chip_detect(struct yt921x_priv *priv)\n3912:\t{\n3913:\t\tstruct device *dev = to_device(priv);\n3914:\t\tconst struct yt921x_info *info;\n3915:\t\tu8 extmode;\n3916:\t\tu32 chipid;\n3917:\t\tu32 major;\n3918:\t\tu32 mode;\n3919:\t\tu32 val;\n3920:\t\tint res;\n3921:\t\n3922:\t\tres = yt921x_reg_read(priv, YT921X_CHIP_ID, \u0026chipid);\n3923:\t\tif (res)\n3924:\t\t\treturn res;\n3925:\t\n3926:\t\tmajor = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);\n3927:\t\n3928:\t\tfor (info = yt921x_infos; info-\u003ename; info++)\n3929:\t\t\tif (info-\u003emajor == major)\n3930:\t\t\t\tbreak;\n3931:\t\tif (!info-\u003ename) {\n3932:\t\t\tdev_err(dev, \"Unexpected chipid 0x%x\\n\", chipid);\n3933:\t\t\treturn -ENODEV;\n3934:\t\t}\n3935:\t\n3936:\t\tres = yt921x_reg_read(priv, YT921X_CHIP_MODE, \u0026mode);\n3937:\t\tif (res)\n3938:\t\t\treturn res;\n3939:\t\tres = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, \u0026extmode);\n3940:\t\tif (res)\n3941:\t\t\treturn res;\n3942:\t\n3943:\t\tfor (; info-\u003ename; info++)\n3944:\t\t\tif (info-\u003emajor == major \u0026\u0026 info-\u003emode == mode \u0026\u0026\n3945:\t\t\t info-\u003eextmode == extmode)\n3946:\t\t\t\tbreak;\n3947:\t\tif (!info-\u003ename) {\n3948:\t\t\tdev_err(dev,\n3949:\t\t\t\t\"Unsupported chipid 0x%x with chipmode 0x%x 0x%x\\n\",\n"
} |
| 23/2 | 2026/09/23 21:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34494 output: 36 thoughts: 393 Thoughts: **My Assessment of the Motorcomm YT9215 and WorthFuzzing** Okay, so the Motorcomm YT9215 is basically a standard Ethernet switch ASIC, akin to those from Realtek or Marvell, and it communicates via MDIO. Let's dig into Section 2 of this... whatever it is. Ah, right, the `WorthFuzzing` criteria. It's saying we should only return `false` if the code modifications fall into specific, very constrained categories. The critical one here is "structurally unreachable hardware," specifically mentioning PCIe switches, SmartNICs, GPU drivers – the stuff that *absolutely* needs physical hardware that QEMU can't emulate. But hang on… DSA switches! Do vendor-specific DSA switches count as "structurally unreachable"? The wording is a little ambiguous. Can we instantiate a DSA switch without actual hardware? Maybe through a device tree overlay or a mock setup? Aha! There's `dsa_loop`. That’s a mock DSA switch driver, right? It can either create a fixed MDIO bus or use an MDIO mock. Could userspace or syzkaller, for instance, bind the `yt921x` driver to `dsa_loop` or another similar mock? That’s the key question. Let's see if we can get syzkaller or userspace to bind to `yt921x`. Based on my understanding, it looks like `yt921x` needs a couple of things: an MDIO bus device and a device tree node with the `compatible = "motorcomm,yt9215"` property. I see... even if we enabled Device Tree overlay configuration (via `CONFIG_OF_CONFIGFS`), which is standard practice for embedded devices, on amd64 (x86_64) that's normally not even how things are done. ACPI is used! And even if `CONFIG_OF` *is* enabled on x86, it still needs that physical MDIO bus. So it can't be created as a pure software artifact on x86. Hmm, okay. Time to check the Kconfig file for `NET_DSA_YT921X`. That might shed some light. |
| 24/2 | 2026/09/23 21:00 | tool | grepper |
0mArgs:{
"Expression": "config NET_DSA_YT921X",
"PathPrefix": "drivers/net/dsa"
}Results: {
"Output": "drivers/net/dsa/motorcomm/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\ndrivers/net/dsa/motorcomm/Kconfig:2:config NET_DSA_YT921X\ndrivers/net/dsa/motorcomm/Kconfig-3-\ttristate \"Motorcomm YT9215 ethernet switch chip support\"\n--\ndrivers/net/dsa/motorcomm/Kconfig-9-\ndrivers/net/dsa/motorcomm/Kconfig:10:config NET_DSA_YT921X_LEDS\ndrivers/net/dsa/motorcomm/Kconfig-11-\tbool \"LED support for Motorcomm YT9215\"\n"
} |
| 25/2 | 2026/09/23 21:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35582 output: 28 thoughts: 15 Thoughts: Here's a first-person summary, tailored for an expert audience, based on the provided thought: **Checking DSA Kconfig** Okay, so I need to understand something about the DSA (Distributed Switch Architecture) driver configuration. My first move is to go directly to `drivers/net/dsa/Kconfig`. This is where the core configuration options related to DSA should reside. It's the central point for defining dependencies, features, and default values that ultimately shape how DSA devices are compiled into the kernel. I'm expecting to find the standard `config` symbols, `depends on` clauses, and maybe some help text detailing how various DSA-compliant hardware is handled. By examining this file, I can quickly grasp the fundamental building blocks of the DSA configuration and how different components interact. From here, I can trace dependencies to see how those symbols tie into more specific hardware or functionality, or I can start to understand the range of supported devices. It’s the obvious starting point for any in-depth investigation. |
| 26/2 | 2026/09/23 21:00 | tool | grepper |
0mArgs:{
"Expression": "motorcomm",
"PathPrefix": "drivers/net/dsa"
}Results: {
"Output": "drivers/net/dsa/Kconfig=83=source \"drivers/net/dsa/microchip/Kconfig\"\ndrivers/net/dsa/Kconfig-84-\ndrivers/net/dsa/Kconfig:85:source \"drivers/net/dsa/motorcomm/Kconfig\"\ndrivers/net/dsa/Kconfig-86-\n--\ndrivers/net/dsa/Makefile=20=obj-y\t\t\t\t+= microchip/\ndrivers/net/dsa/Makefile:21:obj-y\t\t\t\t+= motorcomm/\ndrivers/net/dsa/Makefile-22-obj-y\t\t\t\t+= mv88e6xxx/\n--\ndrivers/net/dsa/motorcomm/chip.c=4441=static const struct of_device_id yt921x_of_match[] = {\ndrivers/net/dsa/motorcomm/chip.c:4442:\t{ .compatible = \"motorcomm,yt9215\" },\ndrivers/net/dsa/motorcomm/chip.c-4443-\t{}\n"
} |
| 27/2 | 2026/09/23 21:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36103 output: 111 thoughts: 1343 Thoughts: **Analysis of Motorcomm YT9215 Driver Reachability for Fuzzing** Okay, let's break this down. My initial assessment points towards this patch being a no-go for fuzzing. I need to be absolutely certain, given my understanding of the triage philosophy. First, I'm noting the context – we're dealing with drivers for physical Ethernet switch ASICs, specifically those that use DSA (Distributed Switch Architecture) within the kernel. I've scanned the relevant driver landscape and none of these physical chips (microchip, motorcomm, mv88e6xxx, sja1105, b53, lan9303, hirschmann) are emulated in QEMU or accessible in standard virtualized setups. Now, let's focus on the Motorcomm YT9215. A quick check confirms that this is a specific Ethernet switch chip made by Motorcomm, a company known for PHYs and switches. It communicates with the host system over MDIO. Crucially, the standard QEMU instances we use (x86_64, which is amd64 as specified) emulate devices like e1000, virtio-net, and rtl8139, but not the Motorcomm YT9215. There's no emulation, no virtual bus, and therefore, no way for syzkaller or any fuzzing setup to interact with this hardware. The driver itself provides a further confirmation of this. It's an `mdio_driver` that's matched via Device Tree with "motorcomm,yt9215". The driver has a probe function (`yt921x_mdio_probe`) that reads registers via MDIO, specifically looking for `YT921X_CHIP_ID` to verify the chip's presence. This probe *requires* the physical hardware to respond correctly. There are no USB, PCI, or platform driver bindings that could be spoofed with USB gadget or platform device, which reinforces its dependence on physical MDIO interaction. I have to carefully review the guidelines again. The reachability gate is the primary thing here. Code is fuzzed only if it can be executed in standard virtualized environments. I need to ensure this is the case here. This patch is looking structurally unreachable because it requires a physical piece of hardware. The code is only modifying `drivers/net/dsa/motorcomm/`. Looking at `git diff` confirms that it’s all within that directory: Makefiles, `.c` and `.h` files for the chip, LEDs, MIB, and SMI. So, it's pretty clear: the patch modifies *only* the Motorcomm YT9215 Ethernet switch driver, a driver that *requires* the physical YT9215 switch ASIC connected over MDIO. It cannot be probed or executed in standard virtualized environments (QEMU, GCE) on amd64. Therefore, the modified code is *structurally unreachable hardware*. I'm confident in calling it. `WorthFuzzing: false` `Reasoning: The patch only modifies the Motorcomm YT9215 ethernet switch driver (drivers/net/dsa/motorcomm/). This driver requires a physical Motorcomm YT9215 switch ASIC connected via MDIO and is matched via Device Tree, which is structurally unreachable in standard virtualized fuzzing environments (QEMU/GCE) on amd64.` `EnableConfigs: []` `FocusSymbols: []` Time to submit the results. |
| 28/2 | 2026/09/23 21:00 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch modifies only the Motorcomm YT9215 ethernet switch chip driver in drivers/net/dsa/motorcomm/. This driver controls a physical Ethernet switch ASIC connected via MDIO and matched via Device Tree (\"motorcomm,yt9215\"). It is not emulated in standard QEMU/virtualized environments on amd64 and is structurally unreachable for fuzzing.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies only the Motorcomm YT9215 ethernet switch chip driver in drivers/net/dsa/motorcomm/. This driver controls a physical Ethernet switch ASIC connected via MDIO and matched via Device Tree (\"motorcomm,yt9215\"). It is not emulated in standard QEMU/virtualized environments on amd64 and is structurally unreachable for fuzzing.",
"WorthFuzzing": false
} |
| 29/1 | 2026/09/23 21:00 | 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) |
|---|