xref: /linux/net/dsa/tag_mt7628.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
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