Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Broadcom tag support
0004  *
0005  * Copyright (C) 2014 Broadcom Corporation
0006  */
0007 
0008 #include <linux/dsa/brcm.h>
0009 #include <linux/etherdevice.h>
0010 #include <linux/list.h>
0011 #include <linux/slab.h>
0012 
0013 #include "dsa_priv.h"
0014 
0015 /* Legacy Broadcom tag (6 bytes) */
0016 #define BRCM_LEG_TAG_LEN    6
0017 
0018 /* Type fields */
0019 /* 1st byte in the tag */
0020 #define BRCM_LEG_TYPE_HI    0x88
0021 /* 2nd byte in the tag */
0022 #define BRCM_LEG_TYPE_LO    0x74
0023 
0024 /* Tag fields */
0025 /* 3rd byte in the tag */
0026 #define BRCM_LEG_UNICAST    (0 << 5)
0027 #define BRCM_LEG_MULTICAST  (1 << 5)
0028 #define BRCM_LEG_EGRESS     (2 << 5)
0029 #define BRCM_LEG_INGRESS    (3 << 5)
0030 
0031 /* 6th byte in the tag */
0032 #define BRCM_LEG_PORT_ID    (0xf)
0033 
0034 /* Newer Broadcom tag (4 bytes) */
0035 #define BRCM_TAG_LEN    4
0036 
0037 /* Tag is constructed and deconstructed using byte by byte access
0038  * because the tag is placed after the MAC Source Address, which does
0039  * not make it 4-bytes aligned, so this might cause unaligned accesses
0040  * on most systems where this is used.
0041  */
0042 
0043 /* Ingress and egress opcodes */
0044 #define BRCM_OPCODE_SHIFT   5
0045 #define BRCM_OPCODE_MASK    0x7
0046 
0047 /* Ingress fields */
0048 /* 1st byte in the tag */
0049 #define BRCM_IG_TC_SHIFT    2
0050 #define BRCM_IG_TC_MASK     0x7
0051 /* 2nd byte in the tag */
0052 #define BRCM_IG_TE_MASK     0x3
0053 #define BRCM_IG_TS_SHIFT    7
0054 /* 3rd byte in the tag */
0055 #define BRCM_IG_DSTMAP2_MASK    1
0056 #define BRCM_IG_DSTMAP1_MASK    0xff
0057 
0058 /* Egress fields */
0059 
0060 /* 2nd byte in the tag */
0061 #define BRCM_EG_CID_MASK    0xff
0062 
0063 /* 3rd byte in the tag */
0064 #define BRCM_EG_RC_MASK     0xff
0065 #define  BRCM_EG_RC_RSVD    (3 << 6)
0066 #define  BRCM_EG_RC_EXCEPTION   (1 << 5)
0067 #define  BRCM_EG_RC_PROT_SNOOP  (1 << 4)
0068 #define  BRCM_EG_RC_PROT_TERM   (1 << 3)
0069 #define  BRCM_EG_RC_SWITCH  (1 << 2)
0070 #define  BRCM_EG_RC_MAC_LEARN   (1 << 1)
0071 #define  BRCM_EG_RC_MIRROR  (1 << 0)
0072 #define BRCM_EG_TC_SHIFT    5
0073 #define BRCM_EG_TC_MASK     0x7
0074 #define BRCM_EG_PID_MASK    0x1f
0075 
0076 #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM) || \
0077     IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
0078 
0079 static struct sk_buff *brcm_tag_xmit_ll(struct sk_buff *skb,
0080                     struct net_device *dev,
0081                     unsigned int offset)
0082 {
0083     struct dsa_port *dp = dsa_slave_to_port(dev);
0084     u16 queue = skb_get_queue_mapping(skb);
0085     u8 *brcm_tag;
0086 
0087     /* The Ethernet switch we are interfaced with needs packets to be at
0088      * least 64 bytes (including FCS) otherwise they will be discarded when
0089      * they enter the switch port logic. When Broadcom tags are enabled, we
0090      * need to make sure that packets are at least 68 bytes
0091      * (including FCS and tag) because the length verification is done after
0092      * the Broadcom tag is stripped off the ingress packet.
0093      *
0094      * Let dsa_slave_xmit() free the SKB
0095      */
0096     if (__skb_put_padto(skb, ETH_ZLEN + BRCM_TAG_LEN, false))
0097         return NULL;
0098 
0099     skb_push(skb, BRCM_TAG_LEN);
0100 
0101     if (offset)
0102         dsa_alloc_etype_header(skb, BRCM_TAG_LEN);
0103 
0104     brcm_tag = skb->data + offset;
0105 
0106     /* Set the ingress opcode, traffic class, tag enforcement is
0107      * deprecated
0108      */
0109     brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) |
0110                ((queue & BRCM_IG_TC_MASK) << BRCM_IG_TC_SHIFT);
0111     brcm_tag[1] = 0;
0112     brcm_tag[2] = 0;
0113     if (dp->index == 8)
0114         brcm_tag[2] = BRCM_IG_DSTMAP2_MASK;
0115     brcm_tag[3] = (1 << dp->index) & BRCM_IG_DSTMAP1_MASK;
0116 
0117     /* Now tell the master network device about the desired output queue
0118      * as well
0119      */
0120     skb_set_queue_mapping(skb, BRCM_TAG_SET_PORT_QUEUE(dp->index, queue));
0121 
0122     return skb;
0123 }
0124 
0125 /* Frames with this tag have one of these two layouts:
0126  * -----------------------------------
0127  * | MAC DA | MAC SA | 4b tag | Type | DSA_TAG_PROTO_BRCM
0128  * -----------------------------------
0129  * -----------------------------------
0130  * | 4b tag | MAC DA | MAC SA | Type | DSA_TAG_PROTO_BRCM_PREPEND
0131  * -----------------------------------
0132  * In both cases, at receive time, skb->data points 2 bytes before the actual
0133  * Ethernet type field and we have an offset of 4bytes between where skb->data
0134  * and where the payload starts. So the same low-level receive function can be
0135  * used.
0136  */
0137 static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb,
0138                        struct net_device *dev,
0139                        unsigned int offset)
0140 {
0141     int source_port;
0142     u8 *brcm_tag;
0143 
0144     if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN)))
0145         return NULL;
0146 
0147     brcm_tag = skb->data - offset;
0148 
0149     /* The opcode should never be different than 0b000 */
0150     if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK))
0151         return NULL;
0152 
0153     /* We should never see a reserved reason code without knowing how to
0154      * handle it
0155      */
0156     if (unlikely(brcm_tag[2] & BRCM_EG_RC_RSVD))
0157         return NULL;
0158 
0159     /* Locate which port this is coming from */
0160     source_port = brcm_tag[3] & BRCM_EG_PID_MASK;
0161 
0162     skb->dev = dsa_master_find_slave(dev, 0, source_port);
0163     if (!skb->dev)
0164         return NULL;
0165 
0166     /* Remove Broadcom tag and update checksum */
0167     skb_pull_rcsum(skb, BRCM_TAG_LEN);
0168 
0169     dsa_default_offload_fwd_mark(skb);
0170 
0171     return skb;
0172 }
0173 #endif
0174 
0175 #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM)
0176 static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb,
0177                      struct net_device *dev)
0178 {
0179     /* Build the tag after the MAC Source Address */
0180     return brcm_tag_xmit_ll(skb, dev, 2 * ETH_ALEN);
0181 }
0182 
0183 
0184 static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev)
0185 {
0186     struct sk_buff *nskb;
0187 
0188     /* skb->data points to the EtherType, the tag is right before it */
0189     nskb = brcm_tag_rcv_ll(skb, dev, 2);
0190     if (!nskb)
0191         return nskb;
0192 
0193     dsa_strip_etype_header(skb, BRCM_TAG_LEN);
0194 
0195     return nskb;
0196 }
0197 
0198 static const struct dsa_device_ops brcm_netdev_ops = {
0199     .name   = "brcm",
0200     .proto  = DSA_TAG_PROTO_BRCM,
0201     .xmit   = brcm_tag_xmit,
0202     .rcv    = brcm_tag_rcv,
0203     .needed_headroom = BRCM_TAG_LEN,
0204 };
0205 
0206 DSA_TAG_DRIVER(brcm_netdev_ops);
0207 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM);
0208 #endif
0209 
0210 #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY)
0211 static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb,
0212                      struct net_device *dev)
0213 {
0214     struct dsa_port *dp = dsa_slave_to_port(dev);
0215     u8 *brcm_tag;
0216 
0217     /* The Ethernet switch we are interfaced with needs packets to be at
0218      * least 64 bytes (including FCS) otherwise they will be discarded when
0219      * they enter the switch port logic. When Broadcom tags are enabled, we
0220      * need to make sure that packets are at least 70 bytes
0221      * (including FCS and tag) because the length verification is done after
0222      * the Broadcom tag is stripped off the ingress packet.
0223      *
0224      * Let dsa_slave_xmit() free the SKB
0225      */
0226     if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false))
0227         return NULL;
0228 
0229     skb_push(skb, BRCM_LEG_TAG_LEN);
0230 
0231     dsa_alloc_etype_header(skb, BRCM_LEG_TAG_LEN);
0232 
0233     brcm_tag = skb->data + 2 * ETH_ALEN;
0234 
0235     /* Broadcom tag type */
0236     brcm_tag[0] = BRCM_LEG_TYPE_HI;
0237     brcm_tag[1] = BRCM_LEG_TYPE_LO;
0238 
0239     /* Broadcom tag value */
0240     brcm_tag[2] = BRCM_LEG_EGRESS;
0241     brcm_tag[3] = 0;
0242     brcm_tag[4] = 0;
0243     brcm_tag[5] = dp->index & BRCM_LEG_PORT_ID;
0244 
0245     return skb;
0246 }
0247 
0248 static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb,
0249                     struct net_device *dev)
0250 {
0251     int source_port;
0252     u8 *brcm_tag;
0253 
0254     if (unlikely(!pskb_may_pull(skb, BRCM_LEG_PORT_ID)))
0255         return NULL;
0256 
0257     brcm_tag = dsa_etype_header_pos_rx(skb);
0258 
0259     source_port = brcm_tag[5] & BRCM_LEG_PORT_ID;
0260 
0261     skb->dev = dsa_master_find_slave(dev, 0, source_port);
0262     if (!skb->dev)
0263         return NULL;
0264 
0265     /* Remove Broadcom tag and update checksum */
0266     skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN);
0267 
0268     dsa_default_offload_fwd_mark(skb);
0269 
0270     dsa_strip_etype_header(skb, BRCM_LEG_TAG_LEN);
0271 
0272     return skb;
0273 }
0274 
0275 static const struct dsa_device_ops brcm_legacy_netdev_ops = {
0276     .name = "brcm-legacy",
0277     .proto = DSA_TAG_PROTO_BRCM_LEGACY,
0278     .xmit = brcm_leg_tag_xmit,
0279     .rcv = brcm_leg_tag_rcv,
0280     .needed_headroom = BRCM_LEG_TAG_LEN,
0281 };
0282 
0283 DSA_TAG_DRIVER(brcm_legacy_netdev_ops);
0284 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY);
0285 #endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY */
0286 
0287 #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
0288 static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb,
0289                          struct net_device *dev)
0290 {
0291     /* tag is prepended to the packet */
0292     return brcm_tag_xmit_ll(skb, dev, 0);
0293 }
0294 
0295 static struct sk_buff *brcm_tag_rcv_prepend(struct sk_buff *skb,
0296                         struct net_device *dev)
0297 {
0298     /* tag is prepended to the packet */
0299     return brcm_tag_rcv_ll(skb, dev, ETH_HLEN);
0300 }
0301 
0302 static const struct dsa_device_ops brcm_prepend_netdev_ops = {
0303     .name   = "brcm-prepend",
0304     .proto  = DSA_TAG_PROTO_BRCM_PREPEND,
0305     .xmit   = brcm_tag_xmit_prepend,
0306     .rcv    = brcm_tag_rcv_prepend,
0307     .needed_headroom = BRCM_TAG_LEN,
0308 };
0309 
0310 DSA_TAG_DRIVER(brcm_prepend_netdev_ops);
0311 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_PREPEND);
0312 #endif
0313 
0314 static struct dsa_tag_driver *dsa_tag_driver_array[] =  {
0315 #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM)
0316     &DSA_TAG_DRIVER_NAME(brcm_netdev_ops),
0317 #endif
0318 #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY)
0319     &DSA_TAG_DRIVER_NAME(brcm_legacy_netdev_ops),
0320 #endif
0321 #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
0322     &DSA_TAG_DRIVER_NAME(brcm_prepend_netdev_ops),
0323 #endif
0324 };
0325 
0326 module_dsa_tag_drivers(dsa_tag_driver_array);
0327 
0328 MODULE_LICENSE("GPL");