]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: dsa: tag_yt921x: add support for Motorcomm YT921x tags
authorDavid Yang <mmyangfl@gmail.com>
Fri, 17 Oct 2025 06:08:54 +0000 (14:08 +0800)
committerJakub Kicinski <kuba@kernel.org>
Wed, 22 Oct 2025 01:25:30 +0000 (18:25 -0700)
Add support for Motorcomm YT921x tags, which includes a proper
configurable ethertype field (default to 0x9988).

Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20251017060859.326450-3-mmyangfl@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/dsa.h
include/uapi/linux/if_ether.h
net/dsa/Kconfig
net/dsa/Makefile
net/dsa/tag_yt921x.c [new file with mode: 0644]

index d73ea08800660ebf562481551a3790a65f84de90..67762fdaf3c7a552691ca2739ae32a0b88337de0 100644 (file)
@@ -55,6 +55,7 @@ struct tc_action;
 #define DSA_TAG_PROTO_LAN937X_VALUE            27
 #define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE      28
 #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE    29
+#define DSA_TAG_PROTO_YT921X_VALUE             30
 
 enum dsa_tag_protocol {
        DSA_TAG_PROTO_NONE              = DSA_TAG_PROTO_NONE_VALUE,
@@ -87,6 +88,7 @@ enum dsa_tag_protocol {
        DSA_TAG_PROTO_RZN1_A5PSW        = DSA_TAG_PROTO_RZN1_A5PSW_VALUE,
        DSA_TAG_PROTO_LAN937X           = DSA_TAG_PROTO_LAN937X_VALUE,
        DSA_TAG_PROTO_VSC73XX_8021Q     = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE,
+       DSA_TAG_PROTO_YT921X            = DSA_TAG_PROTO_YT921X_VALUE,
 };
 
 struct dsa_switch;
index 69e0457eb2000dfda7b96fd4b1daff93f7970f9f..cfd200c87e5ea30fd579a390e24f70e9c611e471 100644 (file)
 #define ETH_P_QINQ1    0x9100          /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_QINQ2    0x9200          /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_QINQ3    0x9300          /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_YT921X   0x9988          /* Motorcomm YT921x DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_EDSA     0xDADA          /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_DSA_8021Q        0xDADB          /* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_DSA_A5PSW        0xE001          /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */
index 869cbe57162f9d78569ff7f042df49cf27c5629b..6b94028b1fcc237496e9b4d499dac9bde2f4db2f 100644 (file)
@@ -190,4 +190,10 @@ config NET_DSA_TAG_XRS700X
          Say Y or M if you want to enable support for tagging frames for
          Arrow SpeedChips XRS700x switches that use a single byte tag trailer.
 
+config NET_DSA_TAG_YT921X
+       tristate "Tag driver for Motorcomm YT921x switches"
+       help
+         Say Y or M if you want to enable support for tagging frames for
+         Motorcomm YT921x switches.
+
 endif
index 555c07cfeb71285b1904bb5ffa0ec8702c1267f8..4b011a1d5c87ebbe65adcf38927d79560e12aec1 100644 (file)
@@ -39,6 +39,7 @@ obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
 obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
 obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) += tag_vsc73xx_8021q.o
 obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o
+obj-$(CONFIG_NET_DSA_TAG_YT921X) += tag_yt921x.o
 
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c
new file mode 100644 (file)
index 0000000..995da44
--- /dev/null
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x Switch Extended CPU Port Tagging
+ *
+ * Copyright (c) 2025 David Yang <mmyangfl@gmail.com>
+ *
+ * +----+----+-------+-----+----+---------
+ * | DA | SA | TagET | Tag | ET | Payload ...
+ * +----+----+-------+-----+----+---------
+ *   6    6      2      6    2       N
+ *
+ * Tag Ethertype: CPU_TAG_TPID_TPID (default: ETH_P_YT921X = 0x9988)
+ *   * Hardcoded for the moment, but still configurable. Discuss it if there
+ *     are conflicts somewhere and/or you want to change it for some reason.
+ * Tag:
+ *   2: VLAN Tag
+ *   2: Rx Port
+ *     15b: Rx Port Valid
+ *     14b-11b: Rx Port
+ *     10b-0b: Cmd?
+ *   2: Tx Port(s)
+ *     15b: Tx Port(s) Valid
+ *     10b-0b: Tx Port(s) Mask
+ */
+
+#include <linux/etherdevice.h>
+
+#include "tag.h"
+
+#define YT921X_TAG_NAME        "yt921x"
+
+#define YT921X_TAG_LEN 8
+
+#define YT921X_TAG_PORT_EN             BIT(15)
+#define YT921X_TAG_RX_PORT_M           GENMASK(14, 11)
+#define YT921X_TAG_RX_CMD_M            GENMASK(10, 0)
+#define  YT921X_TAG_RX_CMD(x)                  FIELD_PREP(YT921X_TAG_RX_CMD_M, (x))
+#define  YT921X_TAG_RX_CMD_FORWARDED           0x80
+#define  YT921X_TAG_RX_CMD_UNK_UCAST           0xb2
+#define  YT921X_TAG_RX_CMD_UNK_MCAST           0xb4
+#define YT921X_TAG_TX_PORTS_M          GENMASK(10, 0)
+#define YT921X_TAG_TX_PORTn(port)      BIT(port)
+
+static struct sk_buff *
+yt921x_tag_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct dsa_port *dp = dsa_user_to_port(netdev);
+       unsigned int port = dp->index;
+       __be16 *tag;
+       u16 tx;
+
+       skb_push(skb, YT921X_TAG_LEN);
+       dsa_alloc_etype_header(skb, YT921X_TAG_LEN);
+
+       tag = dsa_etype_header_pos_tx(skb);
+
+       tag[0] = htons(ETH_P_YT921X);
+       /* VLAN tag unrelated when TX */
+       tag[1] = 0;
+       tag[2] = 0;
+       tx = YT921X_TAG_PORT_EN | YT921X_TAG_TX_PORTn(port);
+       tag[3] = htons(tx);
+
+       return skb;
+}
+
+static struct sk_buff *
+yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev)
+{
+       unsigned int port;
+       __be16 *tag;
+       u16 cmd;
+       u16 rx;
+
+       if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN)))
+               return NULL;
+
+       tag = dsa_etype_header_pos_rx(skb);
+
+       if (unlikely(tag[0] != htons(ETH_P_YT921X))) {
+               dev_warn_ratelimited(&netdev->dev,
+                                    "Unexpected EtherType 0x%04x\n",
+                                    ntohs(tag[0]));
+               return NULL;
+       }
+
+       /* Locate which port this is coming from */
+       rx = ntohs(tag[2]);
+       if (unlikely((rx & YT921X_TAG_PORT_EN) == 0)) {
+               dev_warn_ratelimited(&netdev->dev,
+                                    "Unexpected rx tag 0x%04x\n", rx);
+               return NULL;
+       }
+
+       port = FIELD_GET(YT921X_TAG_RX_PORT_M, rx);
+       skb->dev = dsa_conduit_find_user(netdev, 0, port);
+       if (unlikely(!skb->dev)) {
+               dev_warn_ratelimited(&netdev->dev,
+                                    "Couldn't decode source port %u\n", port);
+               return NULL;
+       }
+
+       cmd = FIELD_GET(YT921X_TAG_RX_CMD_M, rx);
+       switch (cmd) {
+       case YT921X_TAG_RX_CMD_FORWARDED:
+               /* Already forwarded by hardware */
+               dsa_default_offload_fwd_mark(skb);
+               break;
+       case YT921X_TAG_RX_CMD_UNK_UCAST:
+       case YT921X_TAG_RX_CMD_UNK_MCAST:
+               /* NOTE: hardware doesn't distinguish between TRAP (copy to CPU
+                * only) and COPY (forward and copy to CPU). In order to perform
+                * a soft switch, NEVER use COPY action in the switch driver.
+                */
+               break;
+       default:
+               dev_warn_ratelimited(&netdev->dev,
+                                    "Unexpected rx cmd 0x%02x\n", cmd);
+               break;
+       }
+
+       /* Remove YT921x tag and update checksum */
+       skb_pull_rcsum(skb, YT921X_TAG_LEN);
+       dsa_strip_etype_header(skb, YT921X_TAG_LEN);
+
+       return skb;
+}
+
+static const struct dsa_device_ops yt921x_netdev_ops = {
+       .name   = YT921X_TAG_NAME,
+       .proto  = DSA_TAG_PROTO_YT921X,
+       .xmit   = yt921x_tag_xmit,
+       .rcv    = yt921x_tag_rcv,
+       .needed_headroom = YT921X_TAG_LEN,
+};
+
+MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT921x switches");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT921X, YT921X_TAG_NAME);
+
+module_dsa_tag_driver(yt921x_netdev_ops);