1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2019 NXP Semiconductors
3 */
4 #include <soc/mscc/ocelot.h>
5 #include <linux/packing.h>
6 #include "dsa_priv.h"
7
8 /* The CPU injection header and the CPU extraction header can have 3 types of
9 * prefixes: long, short and no prefix. The format of the header itself is the
10 * same in all 3 cases.
11 *
12 * Extraction with long prefix:
13 *
14 * +-------------------+-------------------+------+------+------------+-------+
15 * | ff:ff:ff:ff:ff:ff | ff:ff:ff:ff:ff:ff | 8880 | 000a | extraction | frame |
16 * | | | | | header | |
17 * +-------------------+-------------------+------+------+------------+-------+
18 * 48 bits 48 bits 16 bits 16 bits 128 bits
19 *
20 * Extraction with short prefix:
21 *
22 * +------+------+------------+-------+
23 * | 8880 | 000a | extraction | frame |
24 * | | | header | |
25 * +------+------+------------+-------+
26 * 16 bits 16 bits 128 bits
27 *
28 * Extraction with no prefix:
29 *
30 * +------------+-------+
31 * | extraction | frame |
32 * | header | |
33 * +------------+-------+
34 * 128 bits
35 *
36 *
37 * Injection with long prefix:
38 *
39 * +-------------------+-------------------+------+------+------------+-------+
40 * | any dmac | any smac | 8880 | 000a | injection | frame |
41 * | | | | | header | |
42 * +-------------------+-------------------+------+------+------------+-------+
43 * 48 bits 48 bits 16 bits 16 bits 128 bits
44 *
45 * Injection with short prefix:
46 *
47 * +------+------+------------+-------+
48 * | 8880 | 000a | injection | frame |
49 * | | | header | |
50 * +------+------+------------+-------+
51 * 16 bits 16 bits 128 bits
52 *
53 * Injection with no prefix:
54 *
55 * +------------+-------+
56 * | injection | frame |
57 * | header | |
58 * +------------+-------+
59 * 128 bits
60 *
61 * The injection header looks like this (network byte order, bit 127
62 * is part of lowest address byte in memory, bit 0 is part of highest
63 * address byte):
64 *
65 * +------+------+------+------+------+------+------+------+
66 * 127:120 |BYPASS| MASQ | MASQ_PORT |REW_OP|REW_OP|
67 * +------+------+------+------+------+------+------+------+
68 * 119:112 | REW_OP |
69 * +------+------+------+------+------+------+------+------+
70 * 111:104 | REW_VAL |
71 * +------+------+------+------+------+------+------+------+
72 * 103: 96 | REW_VAL |
73 * +------+------+------+------+------+------+------+------+
74 * 95: 88 | REW_VAL |
75 * +------+------+------+------+------+------+------+------+
76 * 87: 80 | REW_VAL |
77 * +------+------+------+------+------+------+------+------+
78 * 79: 72 | RSV |
79 * +------+------+------+------+------+------+------+------+
80 * 71: 64 | RSV | DEST |
81 * +------+------+------+------+------+------+------+------+
82 * 63: 56 | DEST |
83 * +------+------+------+------+------+------+------+------+
84 * 55: 48 | RSV |
85 * +------+------+------+------+------+------+------+------+
86 * 47: 40 | RSV | SRC_PORT | RSV |TFRM_TIMER|
87 * +------+------+------+------+------+------+------+------+
88 * 39: 32 | TFRM_TIMER | RSV |
89 * +------+------+------+------+------+------+------+------+
90 * 31: 24 | RSV | DP | POP_CNT | CPUQ |
91 * +------+------+------+------+------+------+------+------+
92 * 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
93 * +------+------+------+------+------+------+------+------+
94 * 15: 8 | PCP | DEI | VID |
95 * +------+------+------+------+------+------+------+------+
96 * 7: 0 | VID |
97 * +------+------+------+------+------+------+------+------+
98 *
99 * And the extraction header looks like this:
100 *
101 * +------+------+------+------+------+------+------+------+
102 * 127:120 | RSV | REW_OP |
103 * +------+------+------+------+------+------+------+------+
104 * 119:112 | REW_OP | REW_VAL |
105 * +------+------+------+------+------+------+------+------+
106 * 111:104 | REW_VAL |
107 * +------+------+------+------+------+------+------+------+
108 * 103: 96 | REW_VAL |
109 * +------+------+------+------+------+------+------+------+
110 * 95: 88 | REW_VAL |
111 * +------+------+------+------+------+------+------+------+
112 * 87: 80 | REW_VAL | LLEN |
113 * +------+------+------+------+------+------+------+------+
114 * 79: 72 | LLEN | WLEN |
115 * +------+------+------+------+------+------+------+------+
116 * 71: 64 | WLEN | RSV |
117 * +------+------+------+------+------+------+------+------+
118 * 63: 56 | RSV |
119 * +------+------+------+------+------+------+------+------+
120 * 55: 48 | RSV |
121 * +------+------+------+------+------+------+------+------+
122 * 47: 40 | RSV | SRC_PORT | ACL_ID |
123 * +------+------+------+------+------+------+------+------+
124 * 39: 32 | ACL_ID | RSV | SFLOW_ID |
125 * +------+------+------+------+------+------+------+------+
126 * 31: 24 |ACL_HIT| DP | LRN_FLAGS | CPUQ |
127 * +------+------+------+------+------+------+------+------+
128 * 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
129 * +------+------+------+------+------+------+------+------+
130 * 15: 8 | PCP | DEI | VID |
131 * +------+------+------+------+------+------+------+------+
132 * 7: 0 | VID |
133 * +------+------+------+------+------+------+------+------+
134 */
135
ocelot_xmit(struct sk_buff * skb,struct net_device * netdev)136 static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
137 struct net_device *netdev)
138 {
139 struct dsa_port *dp = dsa_slave_to_port(netdev);
140 struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
141 struct dsa_switch *ds = dp->ds;
142 struct ocelot *ocelot = ds->priv;
143 struct ocelot_port *ocelot_port;
144 u8 *prefix, *injection;
145 u64 qos_class, rew_op;
146 int err;
147
148 err = skb_cow_head(skb, OCELOT_TOTAL_TAG_LEN);
149 if (unlikely(err < 0)) {
150 netdev_err(netdev, "Cannot make room for tag.\n");
151 return NULL;
152 }
153
154 ocelot_port = ocelot->ports[dp->index];
155
156 injection = skb_push(skb, OCELOT_TAG_LEN);
157
158 prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN);
159
160 memcpy(prefix, ocelot_port->xmit_template, OCELOT_TOTAL_TAG_LEN);
161
162 /* Fix up the fields which are not statically determined
163 * in the template
164 */
165 qos_class = skb->priority;
166 packing(injection, &qos_class, 19, 17, OCELOT_TAG_LEN, PACK, 0);
167
168 /* TX timestamping was requested */
169 if (clone) {
170 rew_op = ocelot_port->ptp_cmd;
171 /* Retrieve timestamp ID populated inside skb->cb[0] of the
172 * clone by ocelot_port_add_txtstamp_skb
173 */
174 if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
175 rew_op |= clone->cb[0] << 3;
176
177 packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
178 }
179
180 return skb;
181 }
182
ocelot_rcv(struct sk_buff * skb,struct net_device * netdev,struct packet_type * pt)183 static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
184 struct net_device *netdev,
185 struct packet_type *pt)
186 {
187 struct dsa_port *cpu_dp = netdev->dsa_ptr;
188 struct dsa_switch *ds = cpu_dp->ds;
189 struct ocelot *ocelot = ds->priv;
190 u64 src_port, qos_class;
191 u64 vlan_tci, tag_type;
192 u8 *start = skb->data;
193 u8 *extraction;
194 u16 vlan_tpid;
195
196 /* Revert skb->data by the amount consumed by the DSA master,
197 * so it points to the beginning of the frame.
198 */
199 skb_push(skb, ETH_HLEN);
200 /* We don't care about the short prefix, it is just for easy entrance
201 * into the DSA master's RX filter. Discard it now by moving it into
202 * the headroom.
203 */
204 skb_pull(skb, OCELOT_SHORT_PREFIX_LEN);
205 /* And skb->data now points to the extraction frame header.
206 * Keep a pointer to it.
207 */
208 extraction = skb->data;
209 /* Now the EFH is part of the headroom as well */
210 skb_pull(skb, OCELOT_TAG_LEN);
211 /* Reset the pointer to the real MAC header */
212 skb_reset_mac_header(skb);
213 skb_reset_mac_len(skb);
214 /* And move skb->data to the correct location again */
215 skb_pull(skb, ETH_HLEN);
216
217 /* Remove from inet csum the extraction header */
218 skb_postpull_rcsum(skb, start, OCELOT_TOTAL_TAG_LEN);
219
220 packing(extraction, &src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0);
221 packing(extraction, &qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0);
222 packing(extraction, &tag_type, 16, 16, OCELOT_TAG_LEN, UNPACK, 0);
223 packing(extraction, &vlan_tci, 15, 0, OCELOT_TAG_LEN, UNPACK, 0);
224
225 skb->dev = dsa_master_find_slave(netdev, 0, src_port);
226 if (!skb->dev)
227 /* The switch will reflect back some frames sent through
228 * sockets opened on the bare DSA master. These will come back
229 * with src_port equal to the index of the CPU port, for which
230 * there is no slave registered. So don't print any error
231 * message here (ignore and drop those frames).
232 */
233 return NULL;
234
235 skb->offload_fwd_mark = 1;
236 skb->priority = qos_class;
237
238 /* Ocelot switches copy frames unmodified to the CPU. However, it is
239 * possible for the user to request a VLAN modification through
240 * VCAP_IS1_ACT_VID_REPLACE_ENA. In this case, what will happen is that
241 * the VLAN ID field from the Extraction Header gets updated, but the
242 * 802.1Q header does not (the classified VLAN only becomes visible on
243 * egress through the "port tag" of front-panel ports).
244 * So, for traffic extracted by the CPU, we want to pick up the
245 * classified VLAN and manually replace the existing 802.1Q header from
246 * the packet with it, so that the operating system is always up to
247 * date with the result of tc-vlan actions.
248 * NOTE: In VLAN-unaware mode, we don't want to do that, we want the
249 * frame to remain unmodified, because the classified VLAN is always
250 * equal to the pvid of the ingress port and should not be used for
251 * processing.
252 */
253 vlan_tpid = tag_type ? ETH_P_8021AD : ETH_P_8021Q;
254
255 if (ocelot->ports[src_port]->vlan_aware &&
256 eth_hdr(skb)->h_proto == htons(vlan_tpid)) {
257 u16 dummy_vlan_tci;
258
259 skb_push_rcsum(skb, ETH_HLEN);
260 __skb_vlan_pop(skb, &dummy_vlan_tci);
261 skb_pull_rcsum(skb, ETH_HLEN);
262 __vlan_hwaccel_put_tag(skb, htons(vlan_tpid), vlan_tci);
263 }
264
265 return skb;
266 }
267
268 static const struct dsa_device_ops ocelot_netdev_ops = {
269 .name = "ocelot",
270 .proto = DSA_TAG_PROTO_OCELOT,
271 .xmit = ocelot_xmit,
272 .rcv = ocelot_rcv,
273 .overhead = OCELOT_TOTAL_TAG_LEN,
274 .promisc_on_master = true,
275 };
276
277 MODULE_LICENSE("GPL v2");
278 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT);
279
280 module_dsa_tag_driver(ocelot_netdev_ops);
281