Commit | Line | Data |
---|---|---|
dfedd3b6 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
8b8010fb WH |
2 | /* |
3 | * net/dsa/tag_ksz.c - Microchip KSZ Switch tag format handling | |
4 | * Copyright (c) 2017 Microchip Technology | |
8b8010fb WH |
5 | */ |
6 | ||
7 | #include <linux/etherdevice.h> | |
8 | #include <linux/list.h> | |
9 | #include <linux/slab.h> | |
10 | #include <net/dsa.h> | |
11 | #include "dsa_priv.h" | |
12 | ||
bafe9ba7 TH |
13 | /* Typically only one byte is used for tail tag. */ |
14 | #define KSZ_EGRESS_TAG_LEN 1 | |
88b573af | 15 | #define KSZ_INGRESS_TAG_LEN 1 |
8b8010fb | 16 | |
bafe9ba7 TH |
17 | static struct sk_buff *ksz_common_xmit(struct sk_buff *skb, |
18 | struct net_device *dev, int len) | |
8b8010fb | 19 | { |
8b8010fb WH |
20 | struct sk_buff *nskb; |
21 | int padlen; | |
8b8010fb WH |
22 | |
23 | padlen = (skb->len >= ETH_ZLEN) ? 0 : ETH_ZLEN - skb->len; | |
24 | ||
bafe9ba7 | 25 | if (skb_tailroom(skb) >= padlen + len) { |
49716679 FF |
26 | /* Let dsa_slave_xmit() free skb */ |
27 | if (__skb_put_padto(skb, skb->len + padlen, false)) | |
e71cb9e0 VD |
28 | return NULL; |
29 | ||
8b8010fb WH |
30 | nskb = skb; |
31 | } else { | |
32 | nskb = alloc_skb(NET_IP_ALIGN + skb->len + | |
bafe9ba7 | 33 | padlen + len, GFP_ATOMIC); |
fe47d563 | 34 | if (!nskb) |
8b8010fb | 35 | return NULL; |
8b8010fb WH |
36 | skb_reserve(nskb, NET_IP_ALIGN); |
37 | ||
38 | skb_reset_mac_header(nskb); | |
39 | skb_set_network_header(nskb, | |
40 | skb_network_header(skb) - skb->head); | |
41 | skb_set_transport_header(nskb, | |
42 | skb_transport_header(skb) - skb->head); | |
43 | skb_copy_and_csum_dev(skb, skb_put(nskb, skb->len)); | |
e71cb9e0 | 44 | |
49716679 FF |
45 | /* Let skb_put_padto() free nskb, and let dsa_slave_xmit() free |
46 | * skb | |
47 | */ | |
48 | if (skb_put_padto(nskb, nskb->len + padlen)) | |
e71cb9e0 | 49 | return NULL; |
e71cb9e0 | 50 | |
2b33bc8a | 51 | consume_skb(skb); |
8b8010fb WH |
52 | } |
53 | ||
8b8010fb WH |
54 | return nskb; |
55 | } | |
56 | ||
bafe9ba7 TH |
57 | static struct sk_buff *ksz_common_rcv(struct sk_buff *skb, |
58 | struct net_device *dev, | |
59 | unsigned int port, unsigned int len) | |
8b8010fb | 60 | { |
bafe9ba7 TH |
61 | skb->dev = dsa_master_find_slave(dev, 0, port); |
62 | if (!skb->dev) | |
63 | return NULL; | |
8b8010fb | 64 | |
bafe9ba7 | 65 | pskb_trim_rcsum(skb, skb->len - len); |
8b8010fb | 66 | |
cbd72b48 TH |
67 | skb->offload_fwd_mark = true; |
68 | ||
bafe9ba7 TH |
69 | return skb; |
70 | } | |
3775b1b7 | 71 | |
bafe9ba7 TH |
72 | /* |
73 | * For Ingress (Host -> KSZ9477), 2 bytes are added before FCS. | |
74 | * --------------------------------------------------------------------------- | |
75 | * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes) | |
76 | * --------------------------------------------------------------------------- | |
77 | * tag0 : Prioritization (not used now) | |
78 | * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5) | |
79 | * | |
80 | * For Egress (KSZ9477 -> Host), 1 byte is added before FCS. | |
81 | * --------------------------------------------------------------------------- | |
82 | * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes) | |
83 | * --------------------------------------------------------------------------- | |
84 | * tag0 : zero-based value represents port | |
85 | * (eg, 0x00=port1, 0x02=port3, 0x06=port7) | |
86 | */ | |
87 | ||
88 | #define KSZ9477_INGRESS_TAG_LEN 2 | |
89 | #define KSZ9477_PTP_TAG_LEN 4 | |
90 | #define KSZ9477_PTP_TAG_INDICATION 0x80 | |
91 | ||
92 | #define KSZ9477_TAIL_TAG_OVERRIDE BIT(9) | |
93 | #define KSZ9477_TAIL_TAG_LOOKUP BIT(10) | |
94 | ||
95 | static struct sk_buff *ksz9477_xmit(struct sk_buff *skb, | |
96 | struct net_device *dev) | |
97 | { | |
98 | struct dsa_port *dp = dsa_slave_to_port(dev); | |
99 | struct sk_buff *nskb; | |
100 | u16 *tag; | |
101 | u8 *addr; | |
102 | ||
103 | nskb = ksz_common_xmit(skb, dev, KSZ9477_INGRESS_TAG_LEN); | |
104 | if (!nskb) | |
8b8010fb WH |
105 | return NULL; |
106 | ||
bafe9ba7 TH |
107 | /* Tag encoding */ |
108 | tag = skb_put(nskb, KSZ9477_INGRESS_TAG_LEN); | |
109 | addr = skb_mac_header(nskb); | |
8b8010fb | 110 | |
bafe9ba7 | 111 | *tag = BIT(dp->index); |
8a75b9d4 MV |
112 | |
113 | if (is_link_local_ether_addr(addr)) | |
114 | *tag |= KSZ9477_TAIL_TAG_OVERRIDE; | |
115 | ||
bafe9ba7 TH |
116 | *tag = cpu_to_be16(*tag); |
117 | ||
118 | return nskb; | |
119 | } | |
120 | ||
121 | static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev, | |
122 | struct packet_type *pt) | |
123 | { | |
124 | /* Tag decoding */ | |
125 | u8 *tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN; | |
126 | unsigned int port = tag[0] & 7; | |
127 | unsigned int len = KSZ_EGRESS_TAG_LEN; | |
128 | ||
129 | /* Extra 4-bytes PTP timestamp */ | |
130 | if (tag[0] & KSZ9477_PTP_TAG_INDICATION) | |
131 | len += KSZ9477_PTP_TAG_LEN; | |
132 | ||
133 | return ksz_common_rcv(skb, dev, port, len); | |
8b8010fb WH |
134 | } |
135 | ||
39d6b96f | 136 | const struct dsa_device_ops ksz9477_netdev_ops = { |
875138f8 | 137 | .name = "ksz9477", |
bafe9ba7 TH |
138 | .xmit = ksz9477_xmit, |
139 | .rcv = ksz9477_rcv, | |
140 | .overhead = KSZ9477_INGRESS_TAG_LEN, | |
8b8010fb | 141 | }; |
88b573af | 142 | |
0b42f033 AL |
143 | MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477); |
144 | ||
88b573af TH |
145 | #define KSZ9893_TAIL_TAG_OVERRIDE BIT(5) |
146 | #define KSZ9893_TAIL_TAG_LOOKUP BIT(6) | |
147 | ||
148 | static struct sk_buff *ksz9893_xmit(struct sk_buff *skb, | |
149 | struct net_device *dev) | |
150 | { | |
151 | struct dsa_port *dp = dsa_slave_to_port(dev); | |
152 | struct sk_buff *nskb; | |
153 | u8 *addr; | |
154 | u8 *tag; | |
155 | ||
156 | nskb = ksz_common_xmit(skb, dev, KSZ_INGRESS_TAG_LEN); | |
157 | if (!nskb) | |
158 | return NULL; | |
159 | ||
160 | /* Tag encoding */ | |
161 | tag = skb_put(nskb, KSZ_INGRESS_TAG_LEN); | |
162 | addr = skb_mac_header(nskb); | |
163 | ||
164 | *tag = BIT(dp->index); | |
165 | ||
166 | if (is_link_local_ether_addr(addr)) | |
167 | *tag |= KSZ9893_TAIL_TAG_OVERRIDE; | |
168 | ||
169 | return nskb; | |
170 | } | |
171 | ||
172 | const struct dsa_device_ops ksz9893_netdev_ops = { | |
875138f8 | 173 | .name = "ksz9893", |
88b573af TH |
174 | .xmit = ksz9893_xmit, |
175 | .rcv = ksz9477_rcv, | |
176 | .overhead = KSZ_INGRESS_TAG_LEN, | |
177 | }; | |
0b42f033 | 178 | |
f18bba50 | 179 | MODULE_LICENSE("GPL"); |
0b42f033 | 180 | MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893); |