1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * DSA Special Tag for MaxLinear 862xx switch chips 4 * 5 * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org> 6 * Copyright (C) 2024 MaxLinear Inc. 7 */ 8 9 #include <linux/bitops.h> 10 #include <linux/etherdevice.h> 11 #include <linux/skbuff.h> 12 #include <net/dsa.h> 13 #include "tag.h" 14 15 #define MXL862_NAME "mxl862xx" 16 17 #define MXL862_HEADER_LEN 8 18 19 /* Word 0 -> EtherType */ 20 21 /* Word 2 */ 22 #define MXL862_SUBIF_ID GENMASK(4, 0) 23 24 /* Word 3 */ 25 #define MXL862_IGP_EGP GENMASK(3, 0) 26 27 static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb, 28 struct net_device *dev) 29 { 30 struct dsa_port *dp = dsa_user_to_port(dev); 31 struct dsa_port *cpu_dp = dp->cpu_dp; 32 unsigned int cpu_port, sub_interface; 33 __be16 *mxl862_tag; 34 35 cpu_port = cpu_dp->index; 36 37 /* target port sub-interface ID relative to the CPU port */ 38 sub_interface = dp->index + 16 - cpu_port; 39 40 /* provide additional space 'MXL862_HEADER_LEN' bytes */ 41 skb_push(skb, MXL862_HEADER_LEN); 42 43 /* shift MAC address to the beginning of the enlarged buffer, 44 * releasing the space required for DSA tag (between MAC address and 45 * Ethertype) 46 */ 47 dsa_alloc_etype_header(skb, MXL862_HEADER_LEN); 48 49 /* special tag ingress (from the perspective of the switch) */ 50 mxl862_tag = dsa_etype_header_pos_tx(skb); 51 mxl862_tag[0] = htons(ETH_P_MXLGSW); 52 mxl862_tag[1] = 0; 53 mxl862_tag[2] = htons(FIELD_PREP(MXL862_SUBIF_ID, sub_interface)); 54 mxl862_tag[3] = htons(FIELD_PREP(MXL862_IGP_EGP, cpu_port)); 55 56 return skb; 57 } 58 59 static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, 60 struct net_device *dev) 61 { 62 __be16 *mxl862_tag; 63 int port; 64 65 if (unlikely(!pskb_may_pull(skb, MXL862_HEADER_LEN))) { 66 dev_warn_ratelimited(&dev->dev, "Cannot pull SKB, packet dropped\n"); 67 return NULL; 68 } 69 70 mxl862_tag = dsa_etype_header_pos_rx(skb); 71 72 if (unlikely(mxl862_tag[0] != htons(ETH_P_MXLGSW))) { 73 dev_warn_ratelimited(&dev->dev, 74 "Invalid special tag marker, packet dropped, tag: %8ph\n", 75 mxl862_tag); 76 return NULL; 77 } 78 79 /* Get source port information */ 80 port = FIELD_GET(MXL862_IGP_EGP, ntohs(mxl862_tag[3])); 81 skb->dev = dsa_conduit_find_user(dev, 0, port); 82 if (unlikely(!skb->dev)) { 83 dev_warn_ratelimited(&dev->dev, 84 "Invalid source port, packet dropped, tag: %8ph\n", 85 mxl862_tag); 86 return NULL; 87 } 88 89 /* remove the MxL862xx special tag between the MAC addresses and the 90 * current ethertype field. 91 */ 92 skb_pull_rcsum(skb, MXL862_HEADER_LEN); 93 dsa_strip_etype_header(skb, MXL862_HEADER_LEN); 94 95 return skb; 96 } 97 98 static const struct dsa_device_ops mxl862_netdev_ops = { 99 .name = MXL862_NAME, 100 .proto = DSA_TAG_PROTO_MXL862, 101 .xmit = mxl862_tag_xmit, 102 .rcv = mxl862_tag_rcv, 103 .needed_headroom = MXL862_HEADER_LEN, 104 }; 105 106 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862, MXL862_NAME); 107 MODULE_DESCRIPTION("DSA tag driver for MaxLinear MxL862xx switches"); 108 MODULE_LICENSE("GPL"); 109 110 module_dsa_tag_driver(mxl862_netdev_ops); 111