Blame view

net/dsa/tag_brcm.c 5.76 KB
dfedd3b62   Andrew Lunn   dsa: Add SPDX hea...
1
  // SPDX-License-Identifier: GPL-2.0+
5037d532b   Florian Fainelli   net: dsa: add Bro...
2
3
4
5
  /*
   * Broadcom tag support
   *
   * Copyright (C) 2014 Broadcom Corporation
5037d532b   Florian Fainelli   net: dsa: add Bro...
6
7
8
9
   */
  
  #include <linux/etherdevice.h>
  #include <linux/list.h>
5037d532b   Florian Fainelli   net: dsa: add Bro...
10
  #include <linux/slab.h>
ea5dd34be   Vivien Didelot   net: dsa: include...
11

5037d532b   Florian Fainelli   net: dsa: add Bro...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  #include "dsa_priv.h"
  
  /* This tag length is 4 bytes, older ones were 6 bytes, we do not
   * handle them
   */
  #define BRCM_TAG_LEN	4
  
  /* Tag is constructed and desconstructed using byte by byte access
   * because the tag is placed after the MAC Source Address, which does
   * not make it 4-bytes aligned, so this might cause unaligned accesses
   * on most systems where this is used.
   */
  
  /* Ingress and egress opcodes */
  #define BRCM_OPCODE_SHIFT	5
  #define BRCM_OPCODE_MASK	0x7
  
  /* Ingress fields */
  /* 1st byte in the tag */
  #define BRCM_IG_TC_SHIFT	2
  #define BRCM_IG_TC_MASK		0x7
  /* 2nd byte in the tag */
  #define BRCM_IG_TE_MASK		0x3
  #define BRCM_IG_TS_SHIFT	7
  /* 3rd byte in the tag */
  #define BRCM_IG_DSTMAP2_MASK	1
  #define BRCM_IG_DSTMAP1_MASK	0xff
  
  /* Egress fields */
  
  /* 2nd byte in the tag */
  #define BRCM_EG_CID_MASK	0xff
  
  /* 3rd byte in the tag */
  #define BRCM_EG_RC_MASK		0xff
  #define  BRCM_EG_RC_RSVD	(3 << 6)
  #define  BRCM_EG_RC_EXCEPTION	(1 << 5)
  #define  BRCM_EG_RC_PROT_SNOOP	(1 << 4)
  #define  BRCM_EG_RC_PROT_TERM	(1 << 3)
  #define  BRCM_EG_RC_SWITCH	(1 << 2)
  #define  BRCM_EG_RC_MAC_LEARN	(1 << 1)
  #define  BRCM_EG_RC_MIRROR	(1 << 0)
  #define BRCM_EG_TC_SHIFT	5
  #define BRCM_EG_TC_MASK		0x7
  #define BRCM_EG_PID_MASK	0x1f
3aa475e19   Andrew Lunn   dsa: tag_brcm: Av...
57
58
  #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM) || \
  	IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
59
60
61
  static struct sk_buff *brcm_tag_xmit_ll(struct sk_buff *skb,
  					struct net_device *dev,
  					unsigned int offset)
5037d532b   Florian Fainelli   net: dsa: add Bro...
62
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
63
  	struct dsa_port *dp = dsa_slave_to_port(dev);
0f15b0986   Florian Fainelli   net: dsa: tag_brc...
64
  	u16 queue = skb_get_queue_mapping(skb);
5037d532b   Florian Fainelli   net: dsa: add Bro...
65
  	u8 *brcm_tag;
5037d532b   Florian Fainelli   net: dsa: add Bro...
66
  	if (skb_cow_head(skb, BRCM_TAG_LEN) < 0)
fe47d5630   Vivien Didelot   net: dsa: factor ...
67
  		return NULL;
5037d532b   Florian Fainelli   net: dsa: add Bro...
68

bf08c3408   Florian Fainelli   net: dsa: Move pa...
69
70
71
72
73
74
75
76
77
78
79
  	/* The Ethernet switch we are interfaced with needs packets to be at
  	 * least 64 bytes (including FCS) otherwise they will be discarded when
  	 * they enter the switch port logic. When Broadcom tags are enabled, we
  	 * need to make sure that packets are at least 68 bytes
  	 * (including FCS and tag) because the length verification is done after
  	 * the Broadcom tag is stripped off the ingress packet.
  	 *
  	 * Let dsa_slave_xmit() free the SKB
  	 */
  	if (__skb_put_padto(skb, ETH_ZLEN + BRCM_TAG_LEN, false))
  		return NULL;
5037d532b   Florian Fainelli   net: dsa: add Bro...
80
  	skb_push(skb, BRCM_TAG_LEN);
f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
81
82
  	if (offset)
  		memmove(skb->data, skb->data + BRCM_TAG_LEN, offset);
5037d532b   Florian Fainelli   net: dsa: add Bro...
83

f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
84
  	brcm_tag = skb->data + offset;
5037d532b   Florian Fainelli   net: dsa: add Bro...
85
86
87
88
89
  
  	/* Set the ingress opcode, traffic class, tag enforcment is
  	 * deprecated
  	 */
  	brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) |
0f15b0986   Florian Fainelli   net: dsa: tag_brc...
90
  		       ((queue & BRCM_IG_TC_MASK) << BRCM_IG_TC_SHIFT);
5037d532b   Florian Fainelli   net: dsa: add Bro...
91
92
  	brcm_tag[1] = 0;
  	brcm_tag[2] = 0;
d945097bb   Vivien Didelot   net: dsa: add sla...
93
  	if (dp->index == 8)
5037d532b   Florian Fainelli   net: dsa: add Bro...
94
  		brcm_tag[2] = BRCM_IG_DSTMAP2_MASK;
d945097bb   Vivien Didelot   net: dsa: add sla...
95
  	brcm_tag[3] = (1 << dp->index) & BRCM_IG_DSTMAP1_MASK;
5037d532b   Florian Fainelli   net: dsa: add Bro...
96

0a5f14ce6   Florian Fainelli   net: dsa: tag_brc...
97
98
99
  	/* Now tell the master network device about the desired output queue
  	 * as well
  	 */
d945097bb   Vivien Didelot   net: dsa: add sla...
100
  	skb_set_queue_mapping(skb, BRCM_TAG_SET_PORT_QUEUE(dp->index, queue));
0a5f14ce6   Florian Fainelli   net: dsa: tag_brc...
101

4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
102
  	return skb;
5037d532b   Florian Fainelli   net: dsa: add Bro...
103
  }
f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
104
105
106
107
  static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb,
  				       struct net_device *dev,
  				       struct packet_type *pt,
  				       unsigned int offset)
5037d532b   Florian Fainelli   net: dsa: add Bro...
108
  {
5037d532b   Florian Fainelli   net: dsa: add Bro...
109
110
  	int source_port;
  	u8 *brcm_tag;
5037d532b   Florian Fainelli   net: dsa: add Bro...
111
  	if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN)))
547097958   Vivien Didelot   net: dsa: remove ...
112
  		return NULL;
5037d532b   Florian Fainelli   net: dsa: add Bro...
113

f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
114
  	brcm_tag = skb->data - offset;
5037d532b   Florian Fainelli   net: dsa: add Bro...
115
116
117
  
  	/* The opcode should never be different than 0b000 */
  	if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK))
547097958   Vivien Didelot   net: dsa: remove ...
118
  		return NULL;
5037d532b   Florian Fainelli   net: dsa: add Bro...
119
120
121
122
  
  	/* We should never see a reserved reason code without knowing how to
  	 * handle it
  	 */
82272db84   Florian Fainelli   net: dsa: Drop WA...
123
  	if (unlikely(brcm_tag[2] & BRCM_EG_RC_RSVD))
547097958   Vivien Didelot   net: dsa: remove ...
124
  		return NULL;
5037d532b   Florian Fainelli   net: dsa: add Bro...
125
126
127
  
  	/* Locate which port this is coming from */
  	source_port = brcm_tag[3] & BRCM_EG_PID_MASK;
2231c43b5   Vivien Didelot   net: dsa: rename ...
128
  	skb->dev = dsa_master_find_slave(dev, 0, source_port);
3775b1b7f   Vivien Didelot   net: dsa: add mas...
129
  	if (!skb->dev)
547097958   Vivien Didelot   net: dsa: remove ...
130
  		return NULL;
5037d532b   Florian Fainelli   net: dsa: add Bro...
131
132
133
  
  	/* Remove Broadcom tag and update checksum */
  	skb_pull_rcsum(skb, BRCM_TAG_LEN);
60e975088   Florian Fainelli   net: dsa: Fix dup...
134
  	skb->offload_fwd_mark = 1;
f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
135
136
  	return skb;
  }
3aa475e19   Andrew Lunn   dsa: tag_brcm: Av...
137
  #endif
f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
138

3aa475e19   Andrew Lunn   dsa: tag_brcm: Av...
139
  #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM)
b74b70c44   Florian Fainelli   net: dsa: Support...
140
141
142
143
144
145
  static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb,
  				     struct net_device *dev)
  {
  	/* Build the tag after the MAC Source Address */
  	return brcm_tag_xmit_ll(skb, dev, 2 * ETH_ALEN);
  }
f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
146
147
148
149
150
151
152
153
154
  static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev,
  				    struct packet_type *pt)
  {
  	struct sk_buff *nskb;
  
  	/* skb->data points to the EtherType, the tag is right before it */
  	nskb = brcm_tag_rcv_ll(skb, dev, pt, 2);
  	if (!nskb)
  		return nskb;
5037d532b   Florian Fainelli   net: dsa: add Bro...
155
  	/* Move the Ethernet DA and SA */
f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
156
157
  	memmove(nskb->data - ETH_HLEN,
  		nskb->data - ETH_HLEN - BRCM_TAG_LEN,
5037d532b   Florian Fainelli   net: dsa: add Bro...
158
  		2 * ETH_ALEN);
f7c39e3d1   Florian Fainelli   net: dsa: tag_brc...
159
  	return nskb;
5037d532b   Florian Fainelli   net: dsa: add Bro...
160
  }
f81a43e8d   Andrew Lunn   dsa: Cleanup unne...
161
  static const struct dsa_device_ops brcm_netdev_ops = {
875138f81   Andrew Lunn   dsa: Move tagger ...
162
  	.name	= "brcm",
056eed2fb   Andrew Lunn   dsa: Add TAG prot...
163
  	.proto	= DSA_TAG_PROTO_BRCM,
5037d532b   Florian Fainelli   net: dsa: add Bro...
164
165
  	.xmit	= brcm_tag_xmit,
  	.rcv	= brcm_tag_rcv,
a5dd30877   Andrew Lunn   net: dsa: Add ove...
166
  	.overhead = BRCM_TAG_LEN,
5037d532b   Florian Fainelli   net: dsa: add Bro...
167
  };
0b42f0336   Andrew Lunn   dsa: Add MODULE_A...
168

d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
169
  DSA_TAG_DRIVER(brcm_netdev_ops);
0b42f0336   Andrew Lunn   dsa: Add MODULE_A...
170
  MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM);
b74b70c44   Florian Fainelli   net: dsa: Support...
171
  #endif
3aa475e19   Andrew Lunn   dsa: tag_brcm: Av...
172
  #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
b74b70c44   Florian Fainelli   net: dsa: Support...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb,
  					     struct net_device *dev)
  {
  	/* tag is prepended to the packet */
  	return brcm_tag_xmit_ll(skb, dev, 0);
  }
  
  static struct sk_buff *brcm_tag_rcv_prepend(struct sk_buff *skb,
  					    struct net_device *dev,
  					    struct packet_type *pt)
  {
  	/* tag is prepended to the packet */
  	return brcm_tag_rcv_ll(skb, dev, pt, ETH_HLEN);
  }
f81a43e8d   Andrew Lunn   dsa: Cleanup unne...
187
  static const struct dsa_device_ops brcm_prepend_netdev_ops = {
875138f81   Andrew Lunn   dsa: Move tagger ...
188
  	.name	= "brcm-prepend",
056eed2fb   Andrew Lunn   dsa: Add TAG prot...
189
  	.proto	= DSA_TAG_PROTO_BRCM_PREPEND,
b74b70c44   Florian Fainelli   net: dsa: Support...
190
191
  	.xmit	= brcm_tag_xmit_prepend,
  	.rcv	= brcm_tag_rcv_prepend,
a5dd30877   Andrew Lunn   net: dsa: Add ove...
192
  	.overhead = BRCM_TAG_LEN,
b74b70c44   Florian Fainelli   net: dsa: Support...
193
  };
0b42f0336   Andrew Lunn   dsa: Add MODULE_A...
194

d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
195
  DSA_TAG_DRIVER(brcm_prepend_netdev_ops);
0b42f0336   Andrew Lunn   dsa: Add MODULE_A...
196
  MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_PREPEND);
b96a54154   YueHaibing   dsa: tag_brcm: Fi...
197
  #endif
d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
198
199
200
201
202
203
204
205
206
207
208
209
210
  
  static struct dsa_tag_driver *dsa_tag_driver_array[] =	{
  #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM)
  	&DSA_TAG_DRIVER_NAME(brcm_netdev_ops),
  #endif
  #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
  	&DSA_TAG_DRIVER_NAME(brcm_prepend_netdev_ops),
  #endif
  };
  
  module_dsa_tag_drivers(dsa_tag_driver_array);
  
  MODULE_LICENSE("GPL");