1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2026, Joris Vaisvila <joey@tinyisr.com>
4 * MT7628 switch tag support
5 */
6
7 #include <linux/etherdevice.h>
8 #include <linux/dsa/8021q.h>
9 #include <net/dsa.h>
10
11 #include "tag.h"
12
13 /*
14 * The MT7628 tag is encoded in the VLAN TPID field.
15 * On TX the lower 6 bits encode the destination port bitmask.
16 * On RX the lower 3 bits encode the source port number.
17 *
18 * The switch hardware will not modify the TPID of an incoming packet if it is
19 * already VLAN tagged. To work around this the switch is configured to always
20 * append a tag_8021q standalone VLAN tag for each port. That means we can
21 * safely strip the outer VLAN tag after parsing it.
22 *
23 * A VLAN tag is constructed on egress to target the standalone VLAN and
24 * destination port.
25 */
26
27 #define MT7628_TAG_NAME "mt7628"
28
29 #define MT7628_TAG_TX_PORT GENMASK(5, 0)
30 #define MT7628_TAG_RX_PORT GENMASK(2, 0)
31 #define MT7628_TAG_LEN 4
32
mt7628_tag_xmit(struct sk_buff * skb,struct net_device * dev)33 static struct sk_buff *mt7628_tag_xmit(struct sk_buff *skb,
34 struct net_device *dev)
35 {
36 struct dsa_port *dp;
37 u16 xmit_vlan;
38 __be16 *tag;
39
40 dp = dsa_user_to_port(dev);
41 xmit_vlan = dsa_tag_8021q_standalone_vid(dp);
42
43 skb_push(skb, MT7628_TAG_LEN);
44 dsa_alloc_etype_header(skb, MT7628_TAG_LEN);
45
46 tag = dsa_etype_header_pos_tx(skb);
47
48 tag[0] = htons(ETH_P_8021Q |
49 FIELD_PREP(MT7628_TAG_TX_PORT,
50 dsa_xmit_port_mask(skb, dev)));
51 tag[1] = htons(xmit_vlan);
52
53 return skb;
54 }
55
mt7628_tag_rcv(struct sk_buff * skb,struct net_device * dev)56 static struct sk_buff *mt7628_tag_rcv(struct sk_buff *skb,
57 struct net_device *dev)
58 {
59 __be16 *phdr;
60
61 if (unlikely(!pskb_may_pull(skb, MT7628_TAG_LEN))) {
62 kfree_skb(skb);
63 return NULL;
64 }
65
66 phdr = dsa_etype_header_pos_rx(skb);
67 skb->dev =
68 dsa_conduit_find_user(dev, 0,
69 FIELD_GET(MT7628_TAG_RX_PORT, ntohs(*phdr)));
70 if (!skb->dev) {
71 kfree_skb(skb);
72 return NULL;
73 }
74
75 skb_pull_rcsum(skb, MT7628_TAG_LEN);
76 dsa_strip_etype_header(skb, MT7628_TAG_LEN);
77 dsa_default_offload_fwd_mark(skb);
78 return skb;
79 }
80
81 static const struct dsa_device_ops mt7628_tag_ops = {
82 .name = MT7628_TAG_NAME,
83 .proto = DSA_TAG_PROTO_MT7628,
84 .xmit = mt7628_tag_xmit,
85 .rcv = mt7628_tag_rcv,
86 .needed_headroom = MT7628_TAG_LEN,
87 };
88
89 module_dsa_tag_driver(mt7628_tag_ops);
90
91 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MT7628, MT7628_TAG_NAME);
92 MODULE_DESCRIPTION("DSA tag driver for MT7628 switch");
93 MODULE_LICENSE("GPL");
94