xref: /linux/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1091810dbSJose Abreu // SPDX-License-Identifier: GPL-2.0
2091810dbSJose Abreu /*
3091810dbSJose Abreu  * Copyright (c) 2019 Synopsys, Inc. and/or its affiliates.
4091810dbSJose Abreu  * stmmac Selftests Support
5091810dbSJose Abreu  *
6091810dbSJose Abreu  * Author: Jose Abreu <joabreu@synopsys.com>
7091810dbSJose Abreu  */
8091810dbSJose Abreu 
9eeb9d745SJose Abreu #include <linux/bitrev.h>
10091810dbSJose Abreu #include <linux/completion.h>
11eeb9d745SJose Abreu #include <linux/crc32.h>
12091810dbSJose Abreu #include <linux/ethtool.h>
13091810dbSJose Abreu #include <linux/ip.h>
14091810dbSJose Abreu #include <linux/phy.h>
15091810dbSJose Abreu #include <linux/udp.h>
16ccfc639aSJose Abreu #include <net/pkt_cls.h>
1728c1cf73SJose Abreu #include <net/pkt_sched.h>
18091810dbSJose Abreu #include <net/tcp.h>
19091810dbSJose Abreu #include <net/udp.h>
20ccfc639aSJose Abreu #include <net/tc_act/tc_gact.h>
21091810dbSJose Abreu #include "stmmac.h"
22091810dbSJose Abreu 
23091810dbSJose Abreu struct stmmachdr {
24091810dbSJose Abreu 	__be32 version;
25091810dbSJose Abreu 	__be64 magic;
26091810dbSJose Abreu 	u8 id;
27091810dbSJose Abreu } __packed;
28091810dbSJose Abreu 
29091810dbSJose Abreu #define STMMAC_TEST_PKT_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \
30091810dbSJose Abreu 			      sizeof(struct stmmachdr))
31091810dbSJose Abreu #define STMMAC_TEST_PKT_MAGIC	0xdeadcafecafedeadULL
32091810dbSJose Abreu #define STMMAC_LB_TIMEOUT	msecs_to_jiffies(200)
33091810dbSJose Abreu 
34091810dbSJose Abreu struct stmmac_packet_attrs {
35091810dbSJose Abreu 	int vlan;
36091810dbSJose Abreu 	int vlan_id_in;
37091810dbSJose Abreu 	int vlan_id_out;
38091810dbSJose Abreu 	unsigned char *src;
3976660757SJakub Kicinski 	const unsigned char *dst;
40091810dbSJose Abreu 	u32 ip_src;
41091810dbSJose Abreu 	u32 ip_dst;
42091810dbSJose Abreu 	int tcp;
43091810dbSJose Abreu 	int sport;
44091810dbSJose Abreu 	int dport;
45091810dbSJose Abreu 	u32 exp_hash;
46091810dbSJose Abreu 	int dont_wait;
47091810dbSJose Abreu 	int timeout;
48091810dbSJose Abreu 	int size;
49427849e8SJose Abreu 	int max_size;
50091810dbSJose Abreu 	int remove_sa;
51091810dbSJose Abreu 	u8 id;
528180d579SJose Abreu 	int sarc;
53427849e8SJose Abreu 	u16 queue_mapping;
5428c1cf73SJose Abreu 	u64 timestamp;
55091810dbSJose Abreu };
56091810dbSJose Abreu 
57091810dbSJose Abreu static u8 stmmac_test_next_id;
58091810dbSJose Abreu 
stmmac_test_get_udp_skb(struct stmmac_priv * priv,struct stmmac_packet_attrs * attr)59091810dbSJose Abreu static struct sk_buff *stmmac_test_get_udp_skb(struct stmmac_priv *priv,
60091810dbSJose Abreu 					       struct stmmac_packet_attrs *attr)
61091810dbSJose Abreu {
62091810dbSJose Abreu 	struct sk_buff *skb = NULL;
63091810dbSJose Abreu 	struct udphdr *uhdr = NULL;
64091810dbSJose Abreu 	struct tcphdr *thdr = NULL;
65091810dbSJose Abreu 	struct stmmachdr *shdr;
66091810dbSJose Abreu 	struct ethhdr *ehdr;
67091810dbSJose Abreu 	struct iphdr *ihdr;
68091810dbSJose Abreu 	int iplen, size;
69091810dbSJose Abreu 
70091810dbSJose Abreu 	size = attr->size + STMMAC_TEST_PKT_SIZE;
71091810dbSJose Abreu 	if (attr->vlan) {
72091810dbSJose Abreu 		size += 4;
73091810dbSJose Abreu 		if (attr->vlan > 1)
74091810dbSJose Abreu 			size += 4;
75091810dbSJose Abreu 	}
76091810dbSJose Abreu 
77091810dbSJose Abreu 	if (attr->tcp)
78091810dbSJose Abreu 		size += sizeof(struct tcphdr);
79091810dbSJose Abreu 	else
80091810dbSJose Abreu 		size += sizeof(struct udphdr);
81091810dbSJose Abreu 
82427849e8SJose Abreu 	if (attr->max_size && (attr->max_size > size))
83427849e8SJose Abreu 		size = attr->max_size;
84427849e8SJose Abreu 
850b9f932eSJose Abreu 	skb = netdev_alloc_skb(priv->dev, size);
86091810dbSJose Abreu 	if (!skb)
87091810dbSJose Abreu 		return NULL;
88091810dbSJose Abreu 
89091810dbSJose Abreu 	prefetchw(skb->data);
90091810dbSJose Abreu 
91091810dbSJose Abreu 	if (attr->vlan > 1)
92091810dbSJose Abreu 		ehdr = skb_push(skb, ETH_HLEN + 8);
93091810dbSJose Abreu 	else if (attr->vlan)
94091810dbSJose Abreu 		ehdr = skb_push(skb, ETH_HLEN + 4);
95091810dbSJose Abreu 	else if (attr->remove_sa)
96091810dbSJose Abreu 		ehdr = skb_push(skb, ETH_HLEN - 6);
97091810dbSJose Abreu 	else
98091810dbSJose Abreu 		ehdr = skb_push(skb, ETH_HLEN);
99091810dbSJose Abreu 	skb_reset_mac_header(skb);
100091810dbSJose Abreu 
101091810dbSJose Abreu 	skb_set_network_header(skb, skb->len);
102091810dbSJose Abreu 	ihdr = skb_put(skb, sizeof(*ihdr));
103091810dbSJose Abreu 
104091810dbSJose Abreu 	skb_set_transport_header(skb, skb->len);
105091810dbSJose Abreu 	if (attr->tcp)
106091810dbSJose Abreu 		thdr = skb_put(skb, sizeof(*thdr));
107091810dbSJose Abreu 	else
108091810dbSJose Abreu 		uhdr = skb_put(skb, sizeof(*uhdr));
109091810dbSJose Abreu 
110091810dbSJose Abreu 	if (!attr->remove_sa)
111091810dbSJose Abreu 		eth_zero_addr(ehdr->h_source);
112091810dbSJose Abreu 	eth_zero_addr(ehdr->h_dest);
113091810dbSJose Abreu 	if (attr->src && !attr->remove_sa)
114091810dbSJose Abreu 		ether_addr_copy(ehdr->h_source, attr->src);
115091810dbSJose Abreu 	if (attr->dst)
116091810dbSJose Abreu 		ether_addr_copy(ehdr->h_dest, attr->dst);
117091810dbSJose Abreu 
118091810dbSJose Abreu 	if (!attr->remove_sa) {
119091810dbSJose Abreu 		ehdr->h_proto = htons(ETH_P_IP);
120091810dbSJose Abreu 	} else {
121091810dbSJose Abreu 		__be16 *ptr = (__be16 *)ehdr;
122091810dbSJose Abreu 
123091810dbSJose Abreu 		/* HACK */
124091810dbSJose Abreu 		ptr[3] = htons(ETH_P_IP);
125091810dbSJose Abreu 	}
126091810dbSJose Abreu 
127091810dbSJose Abreu 	if (attr->vlan) {
1282d135deaSJose Abreu 		__be16 *tag, *proto;
129091810dbSJose Abreu 
130091810dbSJose Abreu 		if (!attr->remove_sa) {
131091810dbSJose Abreu 			tag = (void *)ehdr + ETH_HLEN;
132091810dbSJose Abreu 			proto = (void *)ehdr + (2 * ETH_ALEN);
133091810dbSJose Abreu 		} else {
134091810dbSJose Abreu 			tag = (void *)ehdr + ETH_HLEN - 6;
135091810dbSJose Abreu 			proto = (void *)ehdr + ETH_ALEN;
136091810dbSJose Abreu 		}
137091810dbSJose Abreu 
138091810dbSJose Abreu 		proto[0] = htons(ETH_P_8021Q);
139091810dbSJose Abreu 		tag[0] = htons(attr->vlan_id_out);
140091810dbSJose Abreu 		tag[1] = htons(ETH_P_IP);
141091810dbSJose Abreu 		if (attr->vlan > 1) {
142091810dbSJose Abreu 			proto[0] = htons(ETH_P_8021AD);
143091810dbSJose Abreu 			tag[1] = htons(ETH_P_8021Q);
144091810dbSJose Abreu 			tag[2] = htons(attr->vlan_id_in);
145091810dbSJose Abreu 			tag[3] = htons(ETH_P_IP);
146091810dbSJose Abreu 		}
147091810dbSJose Abreu 	}
148091810dbSJose Abreu 
149091810dbSJose Abreu 	if (attr->tcp) {
150091810dbSJose Abreu 		thdr->source = htons(attr->sport);
151091810dbSJose Abreu 		thdr->dest = htons(attr->dport);
152091810dbSJose Abreu 		thdr->doff = sizeof(struct tcphdr) / 4;
153091810dbSJose Abreu 		thdr->check = 0;
154091810dbSJose Abreu 	} else {
155091810dbSJose Abreu 		uhdr->source = htons(attr->sport);
156091810dbSJose Abreu 		uhdr->dest = htons(attr->dport);
157091810dbSJose Abreu 		uhdr->len = htons(sizeof(*shdr) + sizeof(*uhdr) + attr->size);
158427849e8SJose Abreu 		if (attr->max_size)
159427849e8SJose Abreu 			uhdr->len = htons(attr->max_size -
160427849e8SJose Abreu 					  (sizeof(*ihdr) + sizeof(*ehdr)));
161091810dbSJose Abreu 		uhdr->check = 0;
162091810dbSJose Abreu 	}
163091810dbSJose Abreu 
164091810dbSJose Abreu 	ihdr->ihl = 5;
165091810dbSJose Abreu 	ihdr->ttl = 32;
166091810dbSJose Abreu 	ihdr->version = 4;
167091810dbSJose Abreu 	if (attr->tcp)
168091810dbSJose Abreu 		ihdr->protocol = IPPROTO_TCP;
169091810dbSJose Abreu 	else
170091810dbSJose Abreu 		ihdr->protocol = IPPROTO_UDP;
171091810dbSJose Abreu 	iplen = sizeof(*ihdr) + sizeof(*shdr) + attr->size;
172091810dbSJose Abreu 	if (attr->tcp)
173091810dbSJose Abreu 		iplen += sizeof(*thdr);
174091810dbSJose Abreu 	else
175091810dbSJose Abreu 		iplen += sizeof(*uhdr);
176427849e8SJose Abreu 
177427849e8SJose Abreu 	if (attr->max_size)
178427849e8SJose Abreu 		iplen = attr->max_size - sizeof(*ehdr);
179427849e8SJose Abreu 
180091810dbSJose Abreu 	ihdr->tot_len = htons(iplen);
181091810dbSJose Abreu 	ihdr->frag_off = 0;
1824647e021SJose Abreu 	ihdr->saddr = htonl(attr->ip_src);
183091810dbSJose Abreu 	ihdr->daddr = htonl(attr->ip_dst);
184091810dbSJose Abreu 	ihdr->tos = 0;
185091810dbSJose Abreu 	ihdr->id = 0;
186091810dbSJose Abreu 	ip_send_check(ihdr);
187091810dbSJose Abreu 
188091810dbSJose Abreu 	shdr = skb_put(skb, sizeof(*shdr));
189091810dbSJose Abreu 	shdr->version = 0;
190091810dbSJose Abreu 	shdr->magic = cpu_to_be64(STMMAC_TEST_PKT_MAGIC);
191091810dbSJose Abreu 	attr->id = stmmac_test_next_id;
192091810dbSJose Abreu 	shdr->id = stmmac_test_next_id++;
193091810dbSJose Abreu 
194091810dbSJose Abreu 	if (attr->size)
195091810dbSJose Abreu 		skb_put(skb, attr->size);
196427849e8SJose Abreu 	if (attr->max_size && (attr->max_size > skb->len))
197427849e8SJose Abreu 		skb_put(skb, attr->max_size - skb->len);
198091810dbSJose Abreu 
199091810dbSJose Abreu 	skb->csum = 0;
200091810dbSJose Abreu 	skb->ip_summed = CHECKSUM_PARTIAL;
201091810dbSJose Abreu 	if (attr->tcp) {
202091810dbSJose Abreu 		thdr->check = ~tcp_v4_check(skb->len, ihdr->saddr, ihdr->daddr, 0);
203091810dbSJose Abreu 		skb->csum_start = skb_transport_header(skb) - skb->head;
204091810dbSJose Abreu 		skb->csum_offset = offsetof(struct tcphdr, check);
205091810dbSJose Abreu 	} else {
206091810dbSJose Abreu 		udp4_hwcsum(skb, ihdr->saddr, ihdr->daddr);
207091810dbSJose Abreu 	}
208091810dbSJose Abreu 
209091810dbSJose Abreu 	skb->protocol = htons(ETH_P_IP);
210091810dbSJose Abreu 	skb->pkt_type = PACKET_HOST;
211091810dbSJose Abreu 	skb->dev = priv->dev;
212091810dbSJose Abreu 
21328c1cf73SJose Abreu 	if (attr->timestamp)
21428c1cf73SJose Abreu 		skb->tstamp = ns_to_ktime(attr->timestamp);
21528c1cf73SJose Abreu 
216091810dbSJose Abreu 	return skb;
217091810dbSJose Abreu }
218091810dbSJose Abreu 
stmmac_test_get_arp_skb(struct stmmac_priv * priv,struct stmmac_packet_attrs * attr)2195e3fb0a6SJose Abreu static struct sk_buff *stmmac_test_get_arp_skb(struct stmmac_priv *priv,
2205e3fb0a6SJose Abreu 					       struct stmmac_packet_attrs *attr)
2215e3fb0a6SJose Abreu {
2225e3fb0a6SJose Abreu 	__be32 ip_src = htonl(attr->ip_src);
2235e3fb0a6SJose Abreu 	__be32 ip_dst = htonl(attr->ip_dst);
2245e3fb0a6SJose Abreu 	struct sk_buff *skb = NULL;
2255e3fb0a6SJose Abreu 
2265e3fb0a6SJose Abreu 	skb = arp_create(ARPOP_REQUEST, ETH_P_ARP, ip_dst, priv->dev, ip_src,
2275e3fb0a6SJose Abreu 			 NULL, attr->src, attr->dst);
2285e3fb0a6SJose Abreu 	if (!skb)
2295e3fb0a6SJose Abreu 		return NULL;
2305e3fb0a6SJose Abreu 
2315e3fb0a6SJose Abreu 	skb->pkt_type = PACKET_HOST;
2325e3fb0a6SJose Abreu 	skb->dev = priv->dev;
2335e3fb0a6SJose Abreu 
2345e3fb0a6SJose Abreu 	return skb;
2355e3fb0a6SJose Abreu }
2365e3fb0a6SJose Abreu 
237091810dbSJose Abreu struct stmmac_test_priv {
238091810dbSJose Abreu 	struct stmmac_packet_attrs *packet;
239091810dbSJose Abreu 	struct packet_type pt;
240091810dbSJose Abreu 	struct completion comp;
241091810dbSJose Abreu 	int double_vlan;
242091810dbSJose Abreu 	int vlan_id;
243091810dbSJose Abreu 	int ok;
244091810dbSJose Abreu };
245091810dbSJose Abreu 
stmmac_test_loopback_validate(struct sk_buff * skb,struct net_device * ndev,struct packet_type * pt,struct net_device * orig_ndev)246091810dbSJose Abreu static int stmmac_test_loopback_validate(struct sk_buff *skb,
247091810dbSJose Abreu 					 struct net_device *ndev,
248091810dbSJose Abreu 					 struct packet_type *pt,
249091810dbSJose Abreu 					 struct net_device *orig_ndev)
250091810dbSJose Abreu {
251091810dbSJose Abreu 	struct stmmac_test_priv *tpriv = pt->af_packet_priv;
25276660757SJakub Kicinski 	const unsigned char *dst = tpriv->packet->dst;
2530b9f932eSJose Abreu 	unsigned char *src = tpriv->packet->src;
254091810dbSJose Abreu 	struct stmmachdr *shdr;
255091810dbSJose Abreu 	struct ethhdr *ehdr;
256091810dbSJose Abreu 	struct udphdr *uhdr;
257091810dbSJose Abreu 	struct tcphdr *thdr;
258091810dbSJose Abreu 	struct iphdr *ihdr;
259091810dbSJose Abreu 
260091810dbSJose Abreu 	skb = skb_unshare(skb, GFP_ATOMIC);
261091810dbSJose Abreu 	if (!skb)
262091810dbSJose Abreu 		goto out;
263091810dbSJose Abreu 
264091810dbSJose Abreu 	if (skb_linearize(skb))
265091810dbSJose Abreu 		goto out;
266091810dbSJose Abreu 	if (skb_headlen(skb) < (STMMAC_TEST_PKT_SIZE - ETH_HLEN))
267091810dbSJose Abreu 		goto out;
268091810dbSJose Abreu 
269091810dbSJose Abreu 	ehdr = (struct ethhdr *)skb_mac_header(skb);
2700b9f932eSJose Abreu 	if (dst) {
2710b9f932eSJose Abreu 		if (!ether_addr_equal_unaligned(ehdr->h_dest, dst))
272091810dbSJose Abreu 			goto out;
273091810dbSJose Abreu 	}
2748180d579SJose Abreu 	if (tpriv->packet->sarc) {
2750b9f932eSJose Abreu 		if (!ether_addr_equal_unaligned(ehdr->h_source, ehdr->h_dest))
2768180d579SJose Abreu 			goto out;
2770b9f932eSJose Abreu 	} else if (src) {
2780b9f932eSJose Abreu 		if (!ether_addr_equal_unaligned(ehdr->h_source, src))
279091810dbSJose Abreu 			goto out;
280091810dbSJose Abreu 	}
281091810dbSJose Abreu 
282091810dbSJose Abreu 	ihdr = ip_hdr(skb);
283091810dbSJose Abreu 	if (tpriv->double_vlan)
284091810dbSJose Abreu 		ihdr = (struct iphdr *)(skb_network_header(skb) + 4);
285091810dbSJose Abreu 
286091810dbSJose Abreu 	if (tpriv->packet->tcp) {
287091810dbSJose Abreu 		if (ihdr->protocol != IPPROTO_TCP)
288091810dbSJose Abreu 			goto out;
289091810dbSJose Abreu 
290091810dbSJose Abreu 		thdr = (struct tcphdr *)((u8 *)ihdr + 4 * ihdr->ihl);
291091810dbSJose Abreu 		if (thdr->dest != htons(tpriv->packet->dport))
292091810dbSJose Abreu 			goto out;
293091810dbSJose Abreu 
294091810dbSJose Abreu 		shdr = (struct stmmachdr *)((u8 *)thdr + sizeof(*thdr));
295091810dbSJose Abreu 	} else {
296091810dbSJose Abreu 		if (ihdr->protocol != IPPROTO_UDP)
297091810dbSJose Abreu 			goto out;
298091810dbSJose Abreu 
299091810dbSJose Abreu 		uhdr = (struct udphdr *)((u8 *)ihdr + 4 * ihdr->ihl);
300091810dbSJose Abreu 		if (uhdr->dest != htons(tpriv->packet->dport))
301091810dbSJose Abreu 			goto out;
302091810dbSJose Abreu 
303091810dbSJose Abreu 		shdr = (struct stmmachdr *)((u8 *)uhdr + sizeof(*uhdr));
304091810dbSJose Abreu 	}
305091810dbSJose Abreu 
306091810dbSJose Abreu 	if (shdr->magic != cpu_to_be64(STMMAC_TEST_PKT_MAGIC))
307091810dbSJose Abreu 		goto out;
308091810dbSJose Abreu 	if (tpriv->packet->exp_hash && !skb->hash)
309091810dbSJose Abreu 		goto out;
310091810dbSJose Abreu 	if (tpriv->packet->id != shdr->id)
311091810dbSJose Abreu 		goto out;
312091810dbSJose Abreu 
313091810dbSJose Abreu 	tpriv->ok = true;
314091810dbSJose Abreu 	complete(&tpriv->comp);
315091810dbSJose Abreu out:
316091810dbSJose Abreu 	kfree_skb(skb);
317091810dbSJose Abreu 	return 0;
318091810dbSJose Abreu }
319091810dbSJose Abreu 
__stmmac_test_loopback(struct stmmac_priv * priv,struct stmmac_packet_attrs * attr)320091810dbSJose Abreu static int __stmmac_test_loopback(struct stmmac_priv *priv,
321091810dbSJose Abreu 				  struct stmmac_packet_attrs *attr)
322091810dbSJose Abreu {
323091810dbSJose Abreu 	struct stmmac_test_priv *tpriv;
324091810dbSJose Abreu 	struct sk_buff *skb = NULL;
325091810dbSJose Abreu 	int ret = 0;
326091810dbSJose Abreu 
327091810dbSJose Abreu 	tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL);
328091810dbSJose Abreu 	if (!tpriv)
329091810dbSJose Abreu 		return -ENOMEM;
330091810dbSJose Abreu 
331091810dbSJose Abreu 	tpriv->ok = false;
332091810dbSJose Abreu 	init_completion(&tpriv->comp);
333091810dbSJose Abreu 
334091810dbSJose Abreu 	tpriv->pt.type = htons(ETH_P_IP);
335091810dbSJose Abreu 	tpriv->pt.func = stmmac_test_loopback_validate;
336091810dbSJose Abreu 	tpriv->pt.dev = priv->dev;
337091810dbSJose Abreu 	tpriv->pt.af_packet_priv = tpriv;
338091810dbSJose Abreu 	tpriv->packet = attr;
33994e18382SJose Abreu 
34094e18382SJose Abreu 	if (!attr->dont_wait)
341091810dbSJose Abreu 		dev_add_pack(&tpriv->pt);
342091810dbSJose Abreu 
343091810dbSJose Abreu 	skb = stmmac_test_get_udp_skb(priv, attr);
344091810dbSJose Abreu 	if (!skb) {
345091810dbSJose Abreu 		ret = -ENOMEM;
346091810dbSJose Abreu 		goto cleanup;
347091810dbSJose Abreu 	}
348091810dbSJose Abreu 
34905373e31SJose Abreu 	ret = dev_direct_xmit(skb, attr->queue_mapping);
350091810dbSJose Abreu 	if (ret)
351091810dbSJose Abreu 		goto cleanup;
352091810dbSJose Abreu 
353091810dbSJose Abreu 	if (attr->dont_wait)
354091810dbSJose Abreu 		goto cleanup;
355091810dbSJose Abreu 
356091810dbSJose Abreu 	if (!attr->timeout)
357091810dbSJose Abreu 		attr->timeout = STMMAC_LB_TIMEOUT;
358091810dbSJose Abreu 
359091810dbSJose Abreu 	wait_for_completion_timeout(&tpriv->comp, attr->timeout);
36095133210SJose Abreu 	ret = tpriv->ok ? 0 : -ETIMEDOUT;
361091810dbSJose Abreu 
362091810dbSJose Abreu cleanup:
36394e18382SJose Abreu 	if (!attr->dont_wait)
364091810dbSJose Abreu 		dev_remove_pack(&tpriv->pt);
365091810dbSJose Abreu 	kfree(tpriv);
366091810dbSJose Abreu 	return ret;
367091810dbSJose Abreu }
368091810dbSJose Abreu 
stmmac_test_mac_loopback(struct stmmac_priv * priv)369091810dbSJose Abreu static int stmmac_test_mac_loopback(struct stmmac_priv *priv)
370091810dbSJose Abreu {
371091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
372091810dbSJose Abreu 
373091810dbSJose Abreu 	attr.dst = priv->dev->dev_addr;
374091810dbSJose Abreu 	return __stmmac_test_loopback(priv, &attr);
375091810dbSJose Abreu }
376091810dbSJose Abreu 
stmmac_test_phy_loopback(struct stmmac_priv * priv)377091810dbSJose Abreu static int stmmac_test_phy_loopback(struct stmmac_priv *priv)
378091810dbSJose Abreu {
379091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
380091810dbSJose Abreu 	int ret;
381091810dbSJose Abreu 
382091810dbSJose Abreu 	if (!priv->dev->phydev)
383e0fa433dSJose Abreu 		return -EOPNOTSUPP;
384091810dbSJose Abreu 
385091810dbSJose Abreu 	ret = phy_loopback(priv->dev->phydev, true);
386091810dbSJose Abreu 	if (ret)
387091810dbSJose Abreu 		return ret;
388091810dbSJose Abreu 
389091810dbSJose Abreu 	attr.dst = priv->dev->dev_addr;
390091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
391091810dbSJose Abreu 
392091810dbSJose Abreu 	phy_loopback(priv->dev->phydev, false);
393091810dbSJose Abreu 	return ret;
394091810dbSJose Abreu }
395091810dbSJose Abreu 
stmmac_test_mmc(struct stmmac_priv * priv)396091810dbSJose Abreu static int stmmac_test_mmc(struct stmmac_priv *priv)
397091810dbSJose Abreu {
398091810dbSJose Abreu 	struct stmmac_counters initial, final;
399091810dbSJose Abreu 	int ret;
400091810dbSJose Abreu 
401091810dbSJose Abreu 	memset(&initial, 0, sizeof(initial));
402091810dbSJose Abreu 	memset(&final, 0, sizeof(final));
403091810dbSJose Abreu 
404091810dbSJose Abreu 	if (!priv->dma_cap.rmon)
405091810dbSJose Abreu 		return -EOPNOTSUPP;
406091810dbSJose Abreu 
407091810dbSJose Abreu 	/* Save previous results into internal struct */
408091810dbSJose Abreu 	stmmac_mmc_read(priv, priv->mmcaddr, &priv->mmc);
409091810dbSJose Abreu 
410091810dbSJose Abreu 	ret = stmmac_test_mac_loopback(priv);
411091810dbSJose Abreu 	if (ret)
412091810dbSJose Abreu 		return ret;
413091810dbSJose Abreu 
414091810dbSJose Abreu 	/* These will be loopback results so no need to save them */
415091810dbSJose Abreu 	stmmac_mmc_read(priv, priv->mmcaddr, &final);
416091810dbSJose Abreu 
417091810dbSJose Abreu 	/*
418091810dbSJose Abreu 	 * The number of MMC counters available depends on HW configuration
419091810dbSJose Abreu 	 * so we just use this one to validate the feature. I hope there is
420091810dbSJose Abreu 	 * not a version without this counter.
421091810dbSJose Abreu 	 */
422091810dbSJose Abreu 	if (final.mmc_tx_framecount_g <= initial.mmc_tx_framecount_g)
423091810dbSJose Abreu 		return -EINVAL;
424091810dbSJose Abreu 
425091810dbSJose Abreu 	return 0;
426091810dbSJose Abreu }
427091810dbSJose Abreu 
stmmac_test_eee(struct stmmac_priv * priv)428091810dbSJose Abreu static int stmmac_test_eee(struct stmmac_priv *priv)
429091810dbSJose Abreu {
430091810dbSJose Abreu 	struct stmmac_extra_stats *initial, *final;
431091810dbSJose Abreu 	int retries = 10;
432091810dbSJose Abreu 	int ret;
433091810dbSJose Abreu 
434091810dbSJose Abreu 	if (!priv->dma_cap.eee || !priv->eee_active)
435091810dbSJose Abreu 		return -EOPNOTSUPP;
436091810dbSJose Abreu 
437091810dbSJose Abreu 	initial = kzalloc(sizeof(*initial), GFP_KERNEL);
438091810dbSJose Abreu 	if (!initial)
439091810dbSJose Abreu 		return -ENOMEM;
440091810dbSJose Abreu 
441091810dbSJose Abreu 	final = kzalloc(sizeof(*final), GFP_KERNEL);
442091810dbSJose Abreu 	if (!final) {
443091810dbSJose Abreu 		ret = -ENOMEM;
444091810dbSJose Abreu 		goto out_free_initial;
445091810dbSJose Abreu 	}
446091810dbSJose Abreu 
447091810dbSJose Abreu 	memcpy(initial, &priv->xstats, sizeof(*initial));
448091810dbSJose Abreu 
449091810dbSJose Abreu 	ret = stmmac_test_mac_loopback(priv);
450091810dbSJose Abreu 	if (ret)
451091810dbSJose Abreu 		goto out_free_final;
452091810dbSJose Abreu 
453091810dbSJose Abreu 	/* We have no traffic in the line so, sooner or later it will go LPI */
454091810dbSJose Abreu 	while (--retries) {
455091810dbSJose Abreu 		memcpy(final, &priv->xstats, sizeof(*final));
456091810dbSJose Abreu 
457091810dbSJose Abreu 		if (final->irq_tx_path_in_lpi_mode_n >
458091810dbSJose Abreu 		    initial->irq_tx_path_in_lpi_mode_n)
459091810dbSJose Abreu 			break;
460091810dbSJose Abreu 		msleep(100);
461091810dbSJose Abreu 	}
462091810dbSJose Abreu 
463091810dbSJose Abreu 	if (!retries) {
464091810dbSJose Abreu 		ret = -ETIMEDOUT;
465091810dbSJose Abreu 		goto out_free_final;
466091810dbSJose Abreu 	}
467091810dbSJose Abreu 
468091810dbSJose Abreu 	if (final->irq_tx_path_in_lpi_mode_n <=
469091810dbSJose Abreu 	    initial->irq_tx_path_in_lpi_mode_n) {
470091810dbSJose Abreu 		ret = -EINVAL;
471091810dbSJose Abreu 		goto out_free_final;
472091810dbSJose Abreu 	}
473091810dbSJose Abreu 
474091810dbSJose Abreu 	if (final->irq_tx_path_exit_lpi_mode_n <=
475091810dbSJose Abreu 	    initial->irq_tx_path_exit_lpi_mode_n) {
476091810dbSJose Abreu 		ret = -EINVAL;
477091810dbSJose Abreu 		goto out_free_final;
478091810dbSJose Abreu 	}
479091810dbSJose Abreu 
480091810dbSJose Abreu out_free_final:
481091810dbSJose Abreu 	kfree(final);
482091810dbSJose Abreu out_free_initial:
483091810dbSJose Abreu 	kfree(initial);
484091810dbSJose Abreu 	return ret;
485091810dbSJose Abreu }
486091810dbSJose Abreu 
stmmac_filter_check(struct stmmac_priv * priv)487091810dbSJose Abreu static int stmmac_filter_check(struct stmmac_priv *priv)
488091810dbSJose Abreu {
489091810dbSJose Abreu 	if (!(priv->dev->flags & IFF_PROMISC))
490091810dbSJose Abreu 		return 0;
491091810dbSJose Abreu 
492091810dbSJose Abreu 	netdev_warn(priv->dev, "Test can't be run in promiscuous mode!\n");
493091810dbSJose Abreu 	return -EOPNOTSUPP;
494091810dbSJose Abreu }
495091810dbSJose Abreu 
stmmac_hash_check(struct stmmac_priv * priv,unsigned char * addr)496eeb9d745SJose Abreu static bool stmmac_hash_check(struct stmmac_priv *priv, unsigned char *addr)
497eeb9d745SJose Abreu {
498eeb9d745SJose Abreu 	int mc_offset = 32 - priv->hw->mcast_bits_log2;
499eeb9d745SJose Abreu 	struct netdev_hw_addr *ha;
500eeb9d745SJose Abreu 	u32 hash, hash_nr;
501eeb9d745SJose Abreu 
502eeb9d745SJose Abreu 	/* First compute the hash for desired addr */
503eeb9d745SJose Abreu 	hash = bitrev32(~crc32_le(~0, addr, 6)) >> mc_offset;
504eeb9d745SJose Abreu 	hash_nr = hash >> 5;
505eeb9d745SJose Abreu 	hash = 1 << (hash & 0x1f);
506eeb9d745SJose Abreu 
507eeb9d745SJose Abreu 	/* Now, check if it collides with any existing one */
508eeb9d745SJose Abreu 	netdev_for_each_mc_addr(ha, priv->dev) {
509eeb9d745SJose Abreu 		u32 nr = bitrev32(~crc32_le(~0, ha->addr, ETH_ALEN)) >> mc_offset;
510eeb9d745SJose Abreu 		if (((nr >> 5) == hash_nr) && ((1 << (nr & 0x1f)) == hash))
511eeb9d745SJose Abreu 			return false;
512eeb9d745SJose Abreu 	}
513eeb9d745SJose Abreu 
514eeb9d745SJose Abreu 	/* No collisions, address is good to go */
515eeb9d745SJose Abreu 	return true;
516eeb9d745SJose Abreu }
517eeb9d745SJose Abreu 
stmmac_perfect_check(struct stmmac_priv * priv,unsigned char * addr)518eeb9d745SJose Abreu static bool stmmac_perfect_check(struct stmmac_priv *priv, unsigned char *addr)
519eeb9d745SJose Abreu {
520eeb9d745SJose Abreu 	struct netdev_hw_addr *ha;
521eeb9d745SJose Abreu 
522eeb9d745SJose Abreu 	/* Check if it collides with any existing one */
523eeb9d745SJose Abreu 	netdev_for_each_uc_addr(ha, priv->dev) {
524eeb9d745SJose Abreu 		if (!memcmp(ha->addr, addr, ETH_ALEN))
525eeb9d745SJose Abreu 			return false;
526eeb9d745SJose Abreu 	}
527eeb9d745SJose Abreu 
528eeb9d745SJose Abreu 	/* No collisions, address is good to go */
529eeb9d745SJose Abreu 	return true;
530eeb9d745SJose Abreu }
531eeb9d745SJose Abreu 
stmmac_test_hfilt(struct stmmac_priv * priv)532091810dbSJose Abreu static int stmmac_test_hfilt(struct stmmac_priv *priv)
533091810dbSJose Abreu {
534eeb9d745SJose Abreu 	unsigned char gd_addr[ETH_ALEN] = {0xf1, 0xee, 0xdd, 0xcc, 0xbb, 0xaa};
535eeb9d745SJose Abreu 	unsigned char bd_addr[ETH_ALEN] = {0xf1, 0xff, 0xff, 0xff, 0xff, 0xff};
536091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
537eeb9d745SJose Abreu 	int ret, tries = 256;
538091810dbSJose Abreu 
539091810dbSJose Abreu 	ret = stmmac_filter_check(priv);
540091810dbSJose Abreu 	if (ret)
541091810dbSJose Abreu 		return ret;
542091810dbSJose Abreu 
543b870b0f8SJose Abreu 	if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins)
544b870b0f8SJose Abreu 		return -EOPNOTSUPP;
545b870b0f8SJose Abreu 
546eeb9d745SJose Abreu 	while (--tries) {
547eeb9d745SJose Abreu 		/* We only need to check the bd_addr for collisions */
548eeb9d745SJose Abreu 		bd_addr[ETH_ALEN - 1] = tries;
549eeb9d745SJose Abreu 		if (stmmac_hash_check(priv, bd_addr))
550eeb9d745SJose Abreu 			break;
551eeb9d745SJose Abreu 	}
552eeb9d745SJose Abreu 
553eeb9d745SJose Abreu 	if (!tries)
554eeb9d745SJose Abreu 		return -EOPNOTSUPP;
555eeb9d745SJose Abreu 
556091810dbSJose Abreu 	ret = dev_mc_add(priv->dev, gd_addr);
557091810dbSJose Abreu 	if (ret)
558091810dbSJose Abreu 		return ret;
559091810dbSJose Abreu 
560091810dbSJose Abreu 	attr.dst = gd_addr;
561091810dbSJose Abreu 
562091810dbSJose Abreu 	/* Shall receive packet */
563091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
564091810dbSJose Abreu 	if (ret)
565091810dbSJose Abreu 		goto cleanup;
566091810dbSJose Abreu 
567091810dbSJose Abreu 	attr.dst = bd_addr;
568091810dbSJose Abreu 
569091810dbSJose Abreu 	/* Shall NOT receive packet */
570091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
57195133210SJose Abreu 	ret = ret ? 0 : -EINVAL;
572091810dbSJose Abreu 
573091810dbSJose Abreu cleanup:
574091810dbSJose Abreu 	dev_mc_del(priv->dev, gd_addr);
575091810dbSJose Abreu 	return ret;
576091810dbSJose Abreu }
577091810dbSJose Abreu 
stmmac_test_pfilt(struct stmmac_priv * priv)578091810dbSJose Abreu static int stmmac_test_pfilt(struct stmmac_priv *priv)
579091810dbSJose Abreu {
580eeb9d745SJose Abreu 	unsigned char gd_addr[ETH_ALEN] = {0xf0, 0x01, 0x44, 0x55, 0x66, 0x77};
581eeb9d745SJose Abreu 	unsigned char bd_addr[ETH_ALEN] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff};
582091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
583eeb9d745SJose Abreu 	int ret, tries = 256;
584091810dbSJose Abreu 
585091810dbSJose Abreu 	if (stmmac_filter_check(priv))
586091810dbSJose Abreu 		return -EOPNOTSUPP;
587eeb9d745SJose Abreu 	if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries)
588eeb9d745SJose Abreu 		return -EOPNOTSUPP;
589eeb9d745SJose Abreu 
590eeb9d745SJose Abreu 	while (--tries) {
591eeb9d745SJose Abreu 		/* We only need to check the bd_addr for collisions */
592eeb9d745SJose Abreu 		bd_addr[ETH_ALEN - 1] = tries;
593eeb9d745SJose Abreu 		if (stmmac_perfect_check(priv, bd_addr))
594eeb9d745SJose Abreu 			break;
595eeb9d745SJose Abreu 	}
596eeb9d745SJose Abreu 
597eeb9d745SJose Abreu 	if (!tries)
598eeb9d745SJose Abreu 		return -EOPNOTSUPP;
599091810dbSJose Abreu 
600091810dbSJose Abreu 	ret = dev_uc_add(priv->dev, gd_addr);
601091810dbSJose Abreu 	if (ret)
602091810dbSJose Abreu 		return ret;
603091810dbSJose Abreu 
604091810dbSJose Abreu 	attr.dst = gd_addr;
605091810dbSJose Abreu 
606091810dbSJose Abreu 	/* Shall receive packet */
607091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
608091810dbSJose Abreu 	if (ret)
609091810dbSJose Abreu 		goto cleanup;
610091810dbSJose Abreu 
611091810dbSJose Abreu 	attr.dst = bd_addr;
612091810dbSJose Abreu 
613091810dbSJose Abreu 	/* Shall NOT receive packet */
614091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
61595133210SJose Abreu 	ret = ret ? 0 : -EINVAL;
616091810dbSJose Abreu 
617091810dbSJose Abreu cleanup:
618091810dbSJose Abreu 	dev_uc_del(priv->dev, gd_addr);
619091810dbSJose Abreu 	return ret;
620091810dbSJose Abreu }
621091810dbSJose Abreu 
stmmac_test_mcfilt(struct stmmac_priv * priv)622091810dbSJose Abreu static int stmmac_test_mcfilt(struct stmmac_priv *priv)
623091810dbSJose Abreu {
624eeb9d745SJose Abreu 	unsigned char uc_addr[ETH_ALEN] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff};
625eeb9d745SJose Abreu 	unsigned char mc_addr[ETH_ALEN] = {0xf1, 0xff, 0xff, 0xff, 0xff, 0xff};
626091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
627eeb9d745SJose Abreu 	int ret, tries = 256;
628091810dbSJose Abreu 
629091810dbSJose Abreu 	if (stmmac_filter_check(priv))
630091810dbSJose Abreu 		return -EOPNOTSUPP;
631eeb9d745SJose Abreu 	if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries)
632b870b0f8SJose Abreu 		return -EOPNOTSUPP;
63308c96543SJose Abreu 	if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins)
63408c96543SJose Abreu 		return -EOPNOTSUPP;
635091810dbSJose Abreu 
636eeb9d745SJose Abreu 	while (--tries) {
637eeb9d745SJose Abreu 		/* We only need to check the mc_addr for collisions */
638eeb9d745SJose Abreu 		mc_addr[ETH_ALEN - 1] = tries;
639eeb9d745SJose Abreu 		if (stmmac_hash_check(priv, mc_addr))
640eeb9d745SJose Abreu 			break;
641eeb9d745SJose Abreu 	}
642eeb9d745SJose Abreu 
643eeb9d745SJose Abreu 	if (!tries)
644eeb9d745SJose Abreu 		return -EOPNOTSUPP;
645091810dbSJose Abreu 
646091810dbSJose Abreu 	ret = dev_uc_add(priv->dev, uc_addr);
647091810dbSJose Abreu 	if (ret)
648eeb9d745SJose Abreu 		return ret;
649091810dbSJose Abreu 
650091810dbSJose Abreu 	attr.dst = uc_addr;
651091810dbSJose Abreu 
652091810dbSJose Abreu 	/* Shall receive packet */
653091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
654091810dbSJose Abreu 	if (ret)
655091810dbSJose Abreu 		goto cleanup;
656091810dbSJose Abreu 
657091810dbSJose Abreu 	attr.dst = mc_addr;
658091810dbSJose Abreu 
659091810dbSJose Abreu 	/* Shall NOT receive packet */
660091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
66195133210SJose Abreu 	ret = ret ? 0 : -EINVAL;
662091810dbSJose Abreu 
663091810dbSJose Abreu cleanup:
664091810dbSJose Abreu 	dev_uc_del(priv->dev, uc_addr);
665091810dbSJose Abreu 	return ret;
666091810dbSJose Abreu }
667091810dbSJose Abreu 
stmmac_test_ucfilt(struct stmmac_priv * priv)668091810dbSJose Abreu static int stmmac_test_ucfilt(struct stmmac_priv *priv)
669091810dbSJose Abreu {
670eeb9d745SJose Abreu 	unsigned char uc_addr[ETH_ALEN] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff};
671eeb9d745SJose Abreu 	unsigned char mc_addr[ETH_ALEN] = {0xf1, 0xff, 0xff, 0xff, 0xff, 0xff};
672091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
673eeb9d745SJose Abreu 	int ret, tries = 256;
674091810dbSJose Abreu 
675091810dbSJose Abreu 	if (stmmac_filter_check(priv))
676091810dbSJose Abreu 		return -EOPNOTSUPP;
67708c96543SJose Abreu 	if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries)
67808c96543SJose Abreu 		return -EOPNOTSUPP;
679eeb9d745SJose Abreu 	if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins)
680b870b0f8SJose Abreu 		return -EOPNOTSUPP;
681091810dbSJose Abreu 
682eeb9d745SJose Abreu 	while (--tries) {
683eeb9d745SJose Abreu 		/* We only need to check the uc_addr for collisions */
684eeb9d745SJose Abreu 		uc_addr[ETH_ALEN - 1] = tries;
685eeb9d745SJose Abreu 		if (stmmac_perfect_check(priv, uc_addr))
686eeb9d745SJose Abreu 			break;
687eeb9d745SJose Abreu 	}
688eeb9d745SJose Abreu 
689eeb9d745SJose Abreu 	if (!tries)
690eeb9d745SJose Abreu 		return -EOPNOTSUPP;
691091810dbSJose Abreu 
692091810dbSJose Abreu 	ret = dev_mc_add(priv->dev, mc_addr);
693091810dbSJose Abreu 	if (ret)
694eeb9d745SJose Abreu 		return ret;
695091810dbSJose Abreu 
696091810dbSJose Abreu 	attr.dst = mc_addr;
697091810dbSJose Abreu 
698091810dbSJose Abreu 	/* Shall receive packet */
699091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
700091810dbSJose Abreu 	if (ret)
701091810dbSJose Abreu 		goto cleanup;
702091810dbSJose Abreu 
703091810dbSJose Abreu 	attr.dst = uc_addr;
704091810dbSJose Abreu 
705091810dbSJose Abreu 	/* Shall NOT receive packet */
706091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
70795133210SJose Abreu 	ret = ret ? 0 : -EINVAL;
708091810dbSJose Abreu 
709091810dbSJose Abreu cleanup:
710091810dbSJose Abreu 	dev_mc_del(priv->dev, mc_addr);
711091810dbSJose Abreu 	return ret;
712091810dbSJose Abreu }
713091810dbSJose Abreu 
stmmac_test_flowctrl_validate(struct sk_buff * skb,struct net_device * ndev,struct packet_type * pt,struct net_device * orig_ndev)714091810dbSJose Abreu static int stmmac_test_flowctrl_validate(struct sk_buff *skb,
715091810dbSJose Abreu 					 struct net_device *ndev,
716091810dbSJose Abreu 					 struct packet_type *pt,
717091810dbSJose Abreu 					 struct net_device *orig_ndev)
718091810dbSJose Abreu {
719091810dbSJose Abreu 	struct stmmac_test_priv *tpriv = pt->af_packet_priv;
720091810dbSJose Abreu 	struct ethhdr *ehdr;
721091810dbSJose Abreu 
722091810dbSJose Abreu 	ehdr = (struct ethhdr *)skb_mac_header(skb);
7230b9f932eSJose Abreu 	if (!ether_addr_equal_unaligned(ehdr->h_source, orig_ndev->dev_addr))
724091810dbSJose Abreu 		goto out;
725091810dbSJose Abreu 	if (ehdr->h_proto != htons(ETH_P_PAUSE))
726091810dbSJose Abreu 		goto out;
727091810dbSJose Abreu 
728091810dbSJose Abreu 	tpriv->ok = true;
729091810dbSJose Abreu 	complete(&tpriv->comp);
730091810dbSJose Abreu out:
731aeb4a5e8SJose Abreu 	kfree_skb(skb);
732091810dbSJose Abreu 	return 0;
733091810dbSJose Abreu }
734091810dbSJose Abreu 
stmmac_test_flowctrl(struct stmmac_priv * priv)735091810dbSJose Abreu static int stmmac_test_flowctrl(struct stmmac_priv *priv)
736091810dbSJose Abreu {
737091810dbSJose Abreu 	unsigned char paddr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x01};
738091810dbSJose Abreu 	struct phy_device *phydev = priv->dev->phydev;
739091810dbSJose Abreu 	u32 rx_cnt = priv->plat->rx_queues_to_use;
740091810dbSJose Abreu 	struct stmmac_test_priv *tpriv;
741091810dbSJose Abreu 	unsigned int pkt_count;
742091810dbSJose Abreu 	int i, ret = 0;
743091810dbSJose Abreu 
744b0ce902fSJose Abreu 	if (!phydev || (!phydev->pause && !phydev->asym_pause))
745091810dbSJose Abreu 		return -EOPNOTSUPP;
746091810dbSJose Abreu 
747091810dbSJose Abreu 	tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL);
748091810dbSJose Abreu 	if (!tpriv)
749091810dbSJose Abreu 		return -ENOMEM;
750091810dbSJose Abreu 
751091810dbSJose Abreu 	tpriv->ok = false;
752091810dbSJose Abreu 	init_completion(&tpriv->comp);
753091810dbSJose Abreu 	tpriv->pt.type = htons(ETH_P_PAUSE);
754091810dbSJose Abreu 	tpriv->pt.func = stmmac_test_flowctrl_validate;
755091810dbSJose Abreu 	tpriv->pt.dev = priv->dev;
756091810dbSJose Abreu 	tpriv->pt.af_packet_priv = tpriv;
757091810dbSJose Abreu 	dev_add_pack(&tpriv->pt);
758091810dbSJose Abreu 
759091810dbSJose Abreu 	/* Compute minimum number of packets to make FIFO full */
760091810dbSJose Abreu 	pkt_count = priv->plat->rx_fifo_size;
761091810dbSJose Abreu 	if (!pkt_count)
762091810dbSJose Abreu 		pkt_count = priv->dma_cap.rx_fifo_size;
763091810dbSJose Abreu 	pkt_count /= 1400;
764091810dbSJose Abreu 	pkt_count *= 2;
765091810dbSJose Abreu 
766091810dbSJose Abreu 	for (i = 0; i < rx_cnt; i++)
767091810dbSJose Abreu 		stmmac_stop_rx(priv, priv->ioaddr, i);
768091810dbSJose Abreu 
769091810dbSJose Abreu 	ret = dev_set_promiscuity(priv->dev, 1);
770091810dbSJose Abreu 	if (ret)
771091810dbSJose Abreu 		goto cleanup;
772091810dbSJose Abreu 
773091810dbSJose Abreu 	ret = dev_mc_add(priv->dev, paddr);
774091810dbSJose Abreu 	if (ret)
775091810dbSJose Abreu 		goto cleanup;
776091810dbSJose Abreu 
777091810dbSJose Abreu 	for (i = 0; i < pkt_count; i++) {
778091810dbSJose Abreu 		struct stmmac_packet_attrs attr = { };
779091810dbSJose Abreu 
780091810dbSJose Abreu 		attr.dst = priv->dev->dev_addr;
781091810dbSJose Abreu 		attr.dont_wait = true;
782091810dbSJose Abreu 		attr.size = 1400;
783091810dbSJose Abreu 
784091810dbSJose Abreu 		ret = __stmmac_test_loopback(priv, &attr);
785091810dbSJose Abreu 		if (ret)
786091810dbSJose Abreu 			goto cleanup;
787091810dbSJose Abreu 		if (tpriv->ok)
788091810dbSJose Abreu 			break;
789091810dbSJose Abreu 	}
790091810dbSJose Abreu 
791091810dbSJose Abreu 	/* Wait for some time in case RX Watchdog is enabled */
792091810dbSJose Abreu 	msleep(200);
793091810dbSJose Abreu 
794091810dbSJose Abreu 	for (i = 0; i < rx_cnt; i++) {
795091810dbSJose Abreu 		struct stmmac_channel *ch = &priv->channel[i];
796b3138c5bSJose Abreu 		u32 tail;
797091810dbSJose Abreu 
7988531c808SChristian Marangi 		tail = priv->dma_conf.rx_queue[i].dma_rx_phy +
7998531c808SChristian Marangi 			(priv->dma_conf.dma_rx_size * sizeof(struct dma_desc));
800b3138c5bSJose Abreu 
801b3138c5bSJose Abreu 		stmmac_set_rx_tail_ptr(priv, priv->ioaddr, tail, i);
802091810dbSJose Abreu 		stmmac_start_rx(priv, priv->ioaddr, i);
803b3138c5bSJose Abreu 
804091810dbSJose Abreu 		local_bh_disable();
805*73382e91SChristian Marangi 		napi_schedule(&ch->rx_napi);
806091810dbSJose Abreu 		local_bh_enable();
807091810dbSJose Abreu 	}
808091810dbSJose Abreu 
809091810dbSJose Abreu 	wait_for_completion_timeout(&tpriv->comp, STMMAC_LB_TIMEOUT);
81095133210SJose Abreu 	ret = tpriv->ok ? 0 : -ETIMEDOUT;
811091810dbSJose Abreu 
812091810dbSJose Abreu cleanup:
813091810dbSJose Abreu 	dev_mc_del(priv->dev, paddr);
814091810dbSJose Abreu 	dev_set_promiscuity(priv->dev, -1);
815091810dbSJose Abreu 	dev_remove_pack(&tpriv->pt);
816091810dbSJose Abreu 	kfree(tpriv);
817091810dbSJose Abreu 	return ret;
818091810dbSJose Abreu }
819091810dbSJose Abreu 
stmmac_test_rss(struct stmmac_priv * priv)8201fbdad00SJose Abreu static int stmmac_test_rss(struct stmmac_priv *priv)
8211fbdad00SJose Abreu {
8221fbdad00SJose Abreu 	struct stmmac_packet_attrs attr = { };
8231fbdad00SJose Abreu 
8241fbdad00SJose Abreu 	if (!priv->dma_cap.rssen || !priv->rss.enable)
8251fbdad00SJose Abreu 		return -EOPNOTSUPP;
8261fbdad00SJose Abreu 
8271fbdad00SJose Abreu 	attr.dst = priv->dev->dev_addr;
8281fbdad00SJose Abreu 	attr.exp_hash = true;
8291fbdad00SJose Abreu 	attr.sport = 0x321;
8301fbdad00SJose Abreu 	attr.dport = 0x123;
8311fbdad00SJose Abreu 
8321fbdad00SJose Abreu 	return __stmmac_test_loopback(priv, &attr);
8331fbdad00SJose Abreu }
8341fbdad00SJose Abreu 
stmmac_test_vlan_validate(struct sk_buff * skb,struct net_device * ndev,struct packet_type * pt,struct net_device * orig_ndev)83574043f6bSJose Abreu static int stmmac_test_vlan_validate(struct sk_buff *skb,
83674043f6bSJose Abreu 				     struct net_device *ndev,
83774043f6bSJose Abreu 				     struct packet_type *pt,
83874043f6bSJose Abreu 				     struct net_device *orig_ndev)
83974043f6bSJose Abreu {
84074043f6bSJose Abreu 	struct stmmac_test_priv *tpriv = pt->af_packet_priv;
84174043f6bSJose Abreu 	struct stmmachdr *shdr;
84274043f6bSJose Abreu 	struct ethhdr *ehdr;
84374043f6bSJose Abreu 	struct udphdr *uhdr;
84474043f6bSJose Abreu 	struct iphdr *ihdr;
84594e18382SJose Abreu 	u16 proto;
84694e18382SJose Abreu 
84794e18382SJose Abreu 	proto = tpriv->double_vlan ? ETH_P_8021AD : ETH_P_8021Q;
84874043f6bSJose Abreu 
84974043f6bSJose Abreu 	skb = skb_unshare(skb, GFP_ATOMIC);
85074043f6bSJose Abreu 	if (!skb)
85174043f6bSJose Abreu 		goto out;
85274043f6bSJose Abreu 
85374043f6bSJose Abreu 	if (skb_linearize(skb))
85474043f6bSJose Abreu 		goto out;
85574043f6bSJose Abreu 	if (skb_headlen(skb) < (STMMAC_TEST_PKT_SIZE - ETH_HLEN))
85674043f6bSJose Abreu 		goto out;
85794e18382SJose Abreu 	if (tpriv->vlan_id) {
85894e18382SJose Abreu 		if (skb->vlan_proto != htons(proto))
85994e18382SJose Abreu 			goto out;
860d39b68e5SJose Abreu 		if (skb->vlan_tci != tpriv->vlan_id) {
861d39b68e5SJose Abreu 			/* Means filter did not work. */
862d39b68e5SJose Abreu 			tpriv->ok = false;
863d39b68e5SJose Abreu 			complete(&tpriv->comp);
86494e18382SJose Abreu 			goto out;
86594e18382SJose Abreu 		}
866d39b68e5SJose Abreu 	}
86774043f6bSJose Abreu 
86874043f6bSJose Abreu 	ehdr = (struct ethhdr *)skb_mac_header(skb);
8690b9f932eSJose Abreu 	if (!ether_addr_equal_unaligned(ehdr->h_dest, tpriv->packet->dst))
87074043f6bSJose Abreu 		goto out;
87174043f6bSJose Abreu 
87274043f6bSJose Abreu 	ihdr = ip_hdr(skb);
87374043f6bSJose Abreu 	if (tpriv->double_vlan)
87474043f6bSJose Abreu 		ihdr = (struct iphdr *)(skb_network_header(skb) + 4);
87574043f6bSJose Abreu 	if (ihdr->protocol != IPPROTO_UDP)
87674043f6bSJose Abreu 		goto out;
87774043f6bSJose Abreu 
87874043f6bSJose Abreu 	uhdr = (struct udphdr *)((u8 *)ihdr + 4 * ihdr->ihl);
87974043f6bSJose Abreu 	if (uhdr->dest != htons(tpriv->packet->dport))
88074043f6bSJose Abreu 		goto out;
88174043f6bSJose Abreu 
88274043f6bSJose Abreu 	shdr = (struct stmmachdr *)((u8 *)uhdr + sizeof(*uhdr));
88374043f6bSJose Abreu 	if (shdr->magic != cpu_to_be64(STMMAC_TEST_PKT_MAGIC))
88474043f6bSJose Abreu 		goto out;
88574043f6bSJose Abreu 
88674043f6bSJose Abreu 	tpriv->ok = true;
88774043f6bSJose Abreu 	complete(&tpriv->comp);
88874043f6bSJose Abreu 
88974043f6bSJose Abreu out:
89074043f6bSJose Abreu 	kfree_skb(skb);
89174043f6bSJose Abreu 	return 0;
89274043f6bSJose Abreu }
89374043f6bSJose Abreu 
__stmmac_test_vlanfilt(struct stmmac_priv * priv)8941b2250a0SJose Abreu static int __stmmac_test_vlanfilt(struct stmmac_priv *priv)
89574043f6bSJose Abreu {
89674043f6bSJose Abreu 	struct stmmac_packet_attrs attr = { };
89774043f6bSJose Abreu 	struct stmmac_test_priv *tpriv;
89874043f6bSJose Abreu 	struct sk_buff *skb = NULL;
89974043f6bSJose Abreu 	int ret = 0, i;
90074043f6bSJose Abreu 
90174043f6bSJose Abreu 	tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL);
90274043f6bSJose Abreu 	if (!tpriv)
90374043f6bSJose Abreu 		return -ENOMEM;
90474043f6bSJose Abreu 
90574043f6bSJose Abreu 	tpriv->ok = false;
90674043f6bSJose Abreu 	init_completion(&tpriv->comp);
90774043f6bSJose Abreu 
90874043f6bSJose Abreu 	tpriv->pt.type = htons(ETH_P_IP);
90974043f6bSJose Abreu 	tpriv->pt.func = stmmac_test_vlan_validate;
91074043f6bSJose Abreu 	tpriv->pt.dev = priv->dev;
91174043f6bSJose Abreu 	tpriv->pt.af_packet_priv = tpriv;
91274043f6bSJose Abreu 	tpriv->packet = &attr;
91374043f6bSJose Abreu 
91474043f6bSJose Abreu 	/*
91574043f6bSJose Abreu 	 * As we use HASH filtering, false positives may appear. This is a
91674043f6bSJose Abreu 	 * specially chosen ID so that adjacent IDs (+4) have different
91774043f6bSJose Abreu 	 * HASH values.
91874043f6bSJose Abreu 	 */
91974043f6bSJose Abreu 	tpriv->vlan_id = 0x123;
92074043f6bSJose Abreu 	dev_add_pack(&tpriv->pt);
92174043f6bSJose Abreu 
92274043f6bSJose Abreu 	ret = vlan_vid_add(priv->dev, htons(ETH_P_8021Q), tpriv->vlan_id);
92374043f6bSJose Abreu 	if (ret)
92474043f6bSJose Abreu 		goto cleanup;
92574043f6bSJose Abreu 
92674043f6bSJose Abreu 	for (i = 0; i < 4; i++) {
92774043f6bSJose Abreu 		attr.vlan = 1;
92874043f6bSJose Abreu 		attr.vlan_id_out = tpriv->vlan_id + i;
92974043f6bSJose Abreu 		attr.dst = priv->dev->dev_addr;
93074043f6bSJose Abreu 		attr.sport = 9;
93174043f6bSJose Abreu 		attr.dport = 9;
93274043f6bSJose Abreu 
93374043f6bSJose Abreu 		skb = stmmac_test_get_udp_skb(priv, &attr);
93474043f6bSJose Abreu 		if (!skb) {
93574043f6bSJose Abreu 			ret = -ENOMEM;
93674043f6bSJose Abreu 			goto vlan_del;
93774043f6bSJose Abreu 		}
93874043f6bSJose Abreu 
93905373e31SJose Abreu 		ret = dev_direct_xmit(skb, 0);
94074043f6bSJose Abreu 		if (ret)
94174043f6bSJose Abreu 			goto vlan_del;
94274043f6bSJose Abreu 
94374043f6bSJose Abreu 		wait_for_completion_timeout(&tpriv->comp, STMMAC_LB_TIMEOUT);
94495133210SJose Abreu 		ret = tpriv->ok ? 0 : -ETIMEDOUT;
94574043f6bSJose Abreu 		if (ret && !i) {
94674043f6bSJose Abreu 			goto vlan_del;
94774043f6bSJose Abreu 		} else if (!ret && i) {
94895133210SJose Abreu 			ret = -EINVAL;
94974043f6bSJose Abreu 			goto vlan_del;
95074043f6bSJose Abreu 		} else {
95174043f6bSJose Abreu 			ret = 0;
95274043f6bSJose Abreu 		}
95374043f6bSJose Abreu 
95474043f6bSJose Abreu 		tpriv->ok = false;
95574043f6bSJose Abreu 	}
95674043f6bSJose Abreu 
95774043f6bSJose Abreu vlan_del:
95874043f6bSJose Abreu 	vlan_vid_del(priv->dev, htons(ETH_P_8021Q), tpriv->vlan_id);
95974043f6bSJose Abreu cleanup:
96074043f6bSJose Abreu 	dev_remove_pack(&tpriv->pt);
96174043f6bSJose Abreu 	kfree(tpriv);
96274043f6bSJose Abreu 	return ret;
96374043f6bSJose Abreu }
96474043f6bSJose Abreu 
stmmac_test_vlanfilt(struct stmmac_priv * priv)9651b2250a0SJose Abreu static int stmmac_test_vlanfilt(struct stmmac_priv *priv)
9661b2250a0SJose Abreu {
9671b2250a0SJose Abreu 	if (!priv->dma_cap.vlhash)
9681b2250a0SJose Abreu 		return -EOPNOTSUPP;
9691b2250a0SJose Abreu 
9701b2250a0SJose Abreu 	return __stmmac_test_vlanfilt(priv);
9711b2250a0SJose Abreu }
9721b2250a0SJose Abreu 
stmmac_test_vlanfilt_perfect(struct stmmac_priv * priv)9731b2250a0SJose Abreu static int stmmac_test_vlanfilt_perfect(struct stmmac_priv *priv)
9741b2250a0SJose Abreu {
9751b2250a0SJose Abreu 	int ret, prev_cap = priv->dma_cap.vlhash;
9761b2250a0SJose Abreu 
9774eee13f1SJose Abreu 	if (!(priv->dev->features & NETIF_F_HW_VLAN_CTAG_FILTER))
9784eee13f1SJose Abreu 		return -EOPNOTSUPP;
9794eee13f1SJose Abreu 
9801b2250a0SJose Abreu 	priv->dma_cap.vlhash = 0;
9811b2250a0SJose Abreu 	ret = __stmmac_test_vlanfilt(priv);
9821b2250a0SJose Abreu 	priv->dma_cap.vlhash = prev_cap;
9831b2250a0SJose Abreu 
9841b2250a0SJose Abreu 	return ret;
9851b2250a0SJose Abreu }
9861b2250a0SJose Abreu 
__stmmac_test_dvlanfilt(struct stmmac_priv * priv)9871b2250a0SJose Abreu static int __stmmac_test_dvlanfilt(struct stmmac_priv *priv)
98874043f6bSJose Abreu {
98974043f6bSJose Abreu 	struct stmmac_packet_attrs attr = { };
99074043f6bSJose Abreu 	struct stmmac_test_priv *tpriv;
99174043f6bSJose Abreu 	struct sk_buff *skb = NULL;
99274043f6bSJose Abreu 	int ret = 0, i;
99374043f6bSJose Abreu 
99474043f6bSJose Abreu 	tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL);
99574043f6bSJose Abreu 	if (!tpriv)
99674043f6bSJose Abreu 		return -ENOMEM;
99774043f6bSJose Abreu 
99874043f6bSJose Abreu 	tpriv->ok = false;
99974043f6bSJose Abreu 	tpriv->double_vlan = true;
100074043f6bSJose Abreu 	init_completion(&tpriv->comp);
100174043f6bSJose Abreu 
100274043f6bSJose Abreu 	tpriv->pt.type = htons(ETH_P_8021Q);
100374043f6bSJose Abreu 	tpriv->pt.func = stmmac_test_vlan_validate;
100474043f6bSJose Abreu 	tpriv->pt.dev = priv->dev;
100574043f6bSJose Abreu 	tpriv->pt.af_packet_priv = tpriv;
100674043f6bSJose Abreu 	tpriv->packet = &attr;
100774043f6bSJose Abreu 
100874043f6bSJose Abreu 	/*
100974043f6bSJose Abreu 	 * As we use HASH filtering, false positives may appear. This is a
101074043f6bSJose Abreu 	 * specially chosen ID so that adjacent IDs (+4) have different
101174043f6bSJose Abreu 	 * HASH values.
101274043f6bSJose Abreu 	 */
101374043f6bSJose Abreu 	tpriv->vlan_id = 0x123;
101474043f6bSJose Abreu 	dev_add_pack(&tpriv->pt);
101574043f6bSJose Abreu 
101674043f6bSJose Abreu 	ret = vlan_vid_add(priv->dev, htons(ETH_P_8021AD), tpriv->vlan_id);
101774043f6bSJose Abreu 	if (ret)
101874043f6bSJose Abreu 		goto cleanup;
101974043f6bSJose Abreu 
102074043f6bSJose Abreu 	for (i = 0; i < 4; i++) {
102174043f6bSJose Abreu 		attr.vlan = 2;
102274043f6bSJose Abreu 		attr.vlan_id_out = tpriv->vlan_id + i;
102374043f6bSJose Abreu 		attr.dst = priv->dev->dev_addr;
102474043f6bSJose Abreu 		attr.sport = 9;
102574043f6bSJose Abreu 		attr.dport = 9;
102674043f6bSJose Abreu 
102774043f6bSJose Abreu 		skb = stmmac_test_get_udp_skb(priv, &attr);
102874043f6bSJose Abreu 		if (!skb) {
102974043f6bSJose Abreu 			ret = -ENOMEM;
103074043f6bSJose Abreu 			goto vlan_del;
103174043f6bSJose Abreu 		}
103274043f6bSJose Abreu 
103305373e31SJose Abreu 		ret = dev_direct_xmit(skb, 0);
103474043f6bSJose Abreu 		if (ret)
103574043f6bSJose Abreu 			goto vlan_del;
103674043f6bSJose Abreu 
103774043f6bSJose Abreu 		wait_for_completion_timeout(&tpriv->comp, STMMAC_LB_TIMEOUT);
103895133210SJose Abreu 		ret = tpriv->ok ? 0 : -ETIMEDOUT;
103974043f6bSJose Abreu 		if (ret && !i) {
104074043f6bSJose Abreu 			goto vlan_del;
104174043f6bSJose Abreu 		} else if (!ret && i) {
104295133210SJose Abreu 			ret = -EINVAL;
104374043f6bSJose Abreu 			goto vlan_del;
104474043f6bSJose Abreu 		} else {
104574043f6bSJose Abreu 			ret = 0;
104674043f6bSJose Abreu 		}
104774043f6bSJose Abreu 
104874043f6bSJose Abreu 		tpriv->ok = false;
104974043f6bSJose Abreu 	}
105074043f6bSJose Abreu 
105174043f6bSJose Abreu vlan_del:
105274043f6bSJose Abreu 	vlan_vid_del(priv->dev, htons(ETH_P_8021AD), tpriv->vlan_id);
105374043f6bSJose Abreu cleanup:
105474043f6bSJose Abreu 	dev_remove_pack(&tpriv->pt);
105574043f6bSJose Abreu 	kfree(tpriv);
105674043f6bSJose Abreu 	return ret;
105774043f6bSJose Abreu }
105874043f6bSJose Abreu 
stmmac_test_dvlanfilt(struct stmmac_priv * priv)10591b2250a0SJose Abreu static int stmmac_test_dvlanfilt(struct stmmac_priv *priv)
10601b2250a0SJose Abreu {
10611b2250a0SJose Abreu 	if (!priv->dma_cap.vlhash)
10621b2250a0SJose Abreu 		return -EOPNOTSUPP;
10631b2250a0SJose Abreu 
10641b2250a0SJose Abreu 	return __stmmac_test_dvlanfilt(priv);
10651b2250a0SJose Abreu }
10661b2250a0SJose Abreu 
stmmac_test_dvlanfilt_perfect(struct stmmac_priv * priv)10671b2250a0SJose Abreu static int stmmac_test_dvlanfilt_perfect(struct stmmac_priv *priv)
10681b2250a0SJose Abreu {
10691b2250a0SJose Abreu 	int ret, prev_cap = priv->dma_cap.vlhash;
10701b2250a0SJose Abreu 
10714eee13f1SJose Abreu 	if (!(priv->dev->features & NETIF_F_HW_VLAN_STAG_FILTER))
10724eee13f1SJose Abreu 		return -EOPNOTSUPP;
10734eee13f1SJose Abreu 
10741b2250a0SJose Abreu 	priv->dma_cap.vlhash = 0;
10751b2250a0SJose Abreu 	ret = __stmmac_test_dvlanfilt(priv);
10761b2250a0SJose Abreu 	priv->dma_cap.vlhash = prev_cap;
10771b2250a0SJose Abreu 
10781b2250a0SJose Abreu 	return ret;
10791b2250a0SJose Abreu }
10801b2250a0SJose Abreu 
1081ccfc639aSJose Abreu #ifdef CONFIG_NET_CLS_ACT
stmmac_test_rxp(struct stmmac_priv * priv)1082ccfc639aSJose Abreu static int stmmac_test_rxp(struct stmmac_priv *priv)
1083ccfc639aSJose Abreu {
1084ccfc639aSJose Abreu 	unsigned char addr[ETH_ALEN] = {0xde, 0xad, 0xbe, 0xef, 0x00, 0x00};
1085ccfc639aSJose Abreu 	struct tc_cls_u32_offload cls_u32 = { };
1086ccfc639aSJose Abreu 	struct stmmac_packet_attrs attr = { };
1087fe5c5fc1SJakub Kicinski 	struct tc_action **actions;
1088ccfc639aSJose Abreu 	struct tc_u32_sel *sel;
1089fe5c5fc1SJakub Kicinski 	struct tcf_gact *gact;
1090ccfc639aSJose Abreu 	struct tcf_exts *exts;
1091ccfc639aSJose Abreu 	int ret, i, nk = 1;
1092ccfc639aSJose Abreu 
1093ccfc639aSJose Abreu 	if (!tc_can_offload(priv->dev))
1094ccfc639aSJose Abreu 		return -EOPNOTSUPP;
1095ccfc639aSJose Abreu 	if (!priv->dma_cap.frpsel)
1096ccfc639aSJose Abreu 		return -EOPNOTSUPP;
1097ccfc639aSJose Abreu 
10984e638025SGustavo A. R. Silva 	sel = kzalloc(struct_size(sel, keys, nk), GFP_KERNEL);
1099ccfc639aSJose Abreu 	if (!sel)
1100ccfc639aSJose Abreu 		return -ENOMEM;
1101ccfc639aSJose Abreu 
1102ccfc639aSJose Abreu 	exts = kzalloc(sizeof(*exts), GFP_KERNEL);
1103ccfc639aSJose Abreu 	if (!exts) {
1104ccfc639aSJose Abreu 		ret = -ENOMEM;
1105ccfc639aSJose Abreu 		goto cleanup_sel;
1106ccfc639aSJose Abreu 	}
1107ccfc639aSJose Abreu 
110836371876SGustavo A. R. Silva 	actions = kcalloc(nk, sizeof(*actions), GFP_KERNEL);
1109ccfc639aSJose Abreu 	if (!actions) {
1110ccfc639aSJose Abreu 		ret = -ENOMEM;
1111ccfc639aSJose Abreu 		goto cleanup_exts;
1112ccfc639aSJose Abreu 	}
1113ccfc639aSJose Abreu 
1114fe5c5fc1SJakub Kicinski 	gact = kcalloc(nk, sizeof(*gact), GFP_KERNEL);
1115fe5c5fc1SJakub Kicinski 	if (!gact) {
1116ccfc639aSJose Abreu 		ret = -ENOMEM;
1117ccfc639aSJose Abreu 		goto cleanup_actions;
1118ccfc639aSJose Abreu 	}
1119ccfc639aSJose Abreu 
1120ccfc639aSJose Abreu 	cls_u32.command = TC_CLSU32_NEW_KNODE;
1121ccfc639aSJose Abreu 	cls_u32.common.chain_index = 0;
1122ccfc639aSJose Abreu 	cls_u32.common.protocol = htons(ETH_P_ALL);
1123ccfc639aSJose Abreu 	cls_u32.knode.exts = exts;
1124ccfc639aSJose Abreu 	cls_u32.knode.sel = sel;
1125ccfc639aSJose Abreu 	cls_u32.knode.handle = 0x123;
1126ccfc639aSJose Abreu 
1127ccfc639aSJose Abreu 	exts->nr_actions = nk;
1128ccfc639aSJose Abreu 	exts->actions = actions;
1129ccfc639aSJose Abreu 	for (i = 0; i < nk; i++) {
1130fe5c5fc1SJakub Kicinski 		actions[i] = (struct tc_action *)&gact[i];
1131ccfc639aSJose Abreu 		gact->tcf_action = TC_ACT_SHOT;
1132ccfc639aSJose Abreu 	}
1133ccfc639aSJose Abreu 
1134ccfc639aSJose Abreu 	sel->nkeys = nk;
1135ccfc639aSJose Abreu 	sel->offshift = 0;
1136ccfc639aSJose Abreu 	sel->keys[0].off = 6;
1137ccfc639aSJose Abreu 	sel->keys[0].val = htonl(0xdeadbeef);
1138ccfc639aSJose Abreu 	sel->keys[0].mask = ~0x0;
1139ccfc639aSJose Abreu 
1140ccfc639aSJose Abreu 	ret = stmmac_tc_setup_cls_u32(priv, priv, &cls_u32);
1141ccfc639aSJose Abreu 	if (ret)
1142ccfc639aSJose Abreu 		goto cleanup_act;
1143ccfc639aSJose Abreu 
1144ccfc639aSJose Abreu 	attr.dst = priv->dev->dev_addr;
1145ccfc639aSJose Abreu 	attr.src = addr;
1146ccfc639aSJose Abreu 
1147ccfc639aSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
114895133210SJose Abreu 	ret = ret ? 0 : -EINVAL; /* Shall NOT receive packet */
1149ccfc639aSJose Abreu 
1150ccfc639aSJose Abreu 	cls_u32.command = TC_CLSU32_DELETE_KNODE;
1151ccfc639aSJose Abreu 	stmmac_tc_setup_cls_u32(priv, priv, &cls_u32);
1152ccfc639aSJose Abreu 
1153ccfc639aSJose Abreu cleanup_act:
1154fe5c5fc1SJakub Kicinski 	kfree(gact);
1155ccfc639aSJose Abreu cleanup_actions:
1156ccfc639aSJose Abreu 	kfree(actions);
1157ccfc639aSJose Abreu cleanup_exts:
1158ccfc639aSJose Abreu 	kfree(exts);
1159ccfc639aSJose Abreu cleanup_sel:
1160ccfc639aSJose Abreu 	kfree(sel);
1161ccfc639aSJose Abreu 	return ret;
1162ccfc639aSJose Abreu }
1163ccfc639aSJose Abreu #else
stmmac_test_rxp(struct stmmac_priv * priv)1164ccfc639aSJose Abreu static int stmmac_test_rxp(struct stmmac_priv *priv)
1165ccfc639aSJose Abreu {
1166ccfc639aSJose Abreu 	return -EOPNOTSUPP;
1167ccfc639aSJose Abreu }
1168ccfc639aSJose Abreu #endif
1169ccfc639aSJose Abreu 
stmmac_test_desc_sai(struct stmmac_priv * priv)11708180d579SJose Abreu static int stmmac_test_desc_sai(struct stmmac_priv *priv)
11718180d579SJose Abreu {
11728180d579SJose Abreu 	unsigned char src[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
11738180d579SJose Abreu 	struct stmmac_packet_attrs attr = { };
11748180d579SJose Abreu 	int ret;
11758180d579SJose Abreu 
1176034c8fadSJose Abreu 	if (!priv->dma_cap.vlins)
1177034c8fadSJose Abreu 		return -EOPNOTSUPP;
1178034c8fadSJose Abreu 
11798180d579SJose Abreu 	attr.remove_sa = true;
11808180d579SJose Abreu 	attr.sarc = true;
11818180d579SJose Abreu 	attr.src = src;
11828180d579SJose Abreu 	attr.dst = priv->dev->dev_addr;
11838180d579SJose Abreu 
11848180d579SJose Abreu 	priv->sarc_type = 0x1;
11858180d579SJose Abreu 
11868180d579SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
11878180d579SJose Abreu 
11888180d579SJose Abreu 	priv->sarc_type = 0x0;
11898180d579SJose Abreu 	return ret;
11908180d579SJose Abreu }
11918180d579SJose Abreu 
stmmac_test_desc_sar(struct stmmac_priv * priv)11928180d579SJose Abreu static int stmmac_test_desc_sar(struct stmmac_priv *priv)
11938180d579SJose Abreu {
11948180d579SJose Abreu 	unsigned char src[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
11958180d579SJose Abreu 	struct stmmac_packet_attrs attr = { };
11968180d579SJose Abreu 	int ret;
11978180d579SJose Abreu 
1198034c8fadSJose Abreu 	if (!priv->dma_cap.vlins)
1199034c8fadSJose Abreu 		return -EOPNOTSUPP;
1200034c8fadSJose Abreu 
12018180d579SJose Abreu 	attr.sarc = true;
12028180d579SJose Abreu 	attr.src = src;
12038180d579SJose Abreu 	attr.dst = priv->dev->dev_addr;
12048180d579SJose Abreu 
12058180d579SJose Abreu 	priv->sarc_type = 0x2;
12068180d579SJose Abreu 
12078180d579SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
12088180d579SJose Abreu 
12098180d579SJose Abreu 	priv->sarc_type = 0x0;
12108180d579SJose Abreu 	return ret;
12118180d579SJose Abreu }
12128180d579SJose Abreu 
stmmac_test_reg_sai(struct stmmac_priv * priv)12138180d579SJose Abreu static int stmmac_test_reg_sai(struct stmmac_priv *priv)
12148180d579SJose Abreu {
12158180d579SJose Abreu 	unsigned char src[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
12168180d579SJose Abreu 	struct stmmac_packet_attrs attr = { };
12178180d579SJose Abreu 	int ret;
12188180d579SJose Abreu 
1219034c8fadSJose Abreu 	if (!priv->dma_cap.vlins)
1220034c8fadSJose Abreu 		return -EOPNOTSUPP;
1221034c8fadSJose Abreu 
12228180d579SJose Abreu 	attr.remove_sa = true;
12238180d579SJose Abreu 	attr.sarc = true;
12248180d579SJose Abreu 	attr.src = src;
12258180d579SJose Abreu 	attr.dst = priv->dev->dev_addr;
12268180d579SJose Abreu 
12278180d579SJose Abreu 	if (stmmac_sarc_configure(priv, priv->ioaddr, 0x2))
12288180d579SJose Abreu 		return -EOPNOTSUPP;
12298180d579SJose Abreu 
12308180d579SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
12318180d579SJose Abreu 
12328180d579SJose Abreu 	stmmac_sarc_configure(priv, priv->ioaddr, 0x0);
12338180d579SJose Abreu 	return ret;
12348180d579SJose Abreu }
12358180d579SJose Abreu 
stmmac_test_reg_sar(struct stmmac_priv * priv)12368180d579SJose Abreu static int stmmac_test_reg_sar(struct stmmac_priv *priv)
12378180d579SJose Abreu {
12388180d579SJose Abreu 	unsigned char src[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
12398180d579SJose Abreu 	struct stmmac_packet_attrs attr = { };
12408180d579SJose Abreu 	int ret;
12418180d579SJose Abreu 
1242034c8fadSJose Abreu 	if (!priv->dma_cap.vlins)
1243034c8fadSJose Abreu 		return -EOPNOTSUPP;
1244034c8fadSJose Abreu 
12458180d579SJose Abreu 	attr.sarc = true;
12468180d579SJose Abreu 	attr.src = src;
12478180d579SJose Abreu 	attr.dst = priv->dev->dev_addr;
12488180d579SJose Abreu 
12498180d579SJose Abreu 	if (stmmac_sarc_configure(priv, priv->ioaddr, 0x3))
12508180d579SJose Abreu 		return -EOPNOTSUPP;
12518180d579SJose Abreu 
12528180d579SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
12538180d579SJose Abreu 
12548180d579SJose Abreu 	stmmac_sarc_configure(priv, priv->ioaddr, 0x0);
12558180d579SJose Abreu 	return ret;
12568180d579SJose Abreu }
12578180d579SJose Abreu 
stmmac_test_vlanoff_common(struct stmmac_priv * priv,bool svlan)125894e18382SJose Abreu static int stmmac_test_vlanoff_common(struct stmmac_priv *priv, bool svlan)
125994e18382SJose Abreu {
126094e18382SJose Abreu 	struct stmmac_packet_attrs attr = { };
126194e18382SJose Abreu 	struct stmmac_test_priv *tpriv;
126294e18382SJose Abreu 	struct sk_buff *skb = NULL;
126394e18382SJose Abreu 	int ret = 0;
126494e18382SJose Abreu 	u16 proto;
126594e18382SJose Abreu 
126694e18382SJose Abreu 	if (!priv->dma_cap.vlins)
126794e18382SJose Abreu 		return -EOPNOTSUPP;
126894e18382SJose Abreu 
126994e18382SJose Abreu 	tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL);
127094e18382SJose Abreu 	if (!tpriv)
127194e18382SJose Abreu 		return -ENOMEM;
127294e18382SJose Abreu 
127394e18382SJose Abreu 	proto = svlan ? ETH_P_8021AD : ETH_P_8021Q;
127494e18382SJose Abreu 
127594e18382SJose Abreu 	tpriv->ok = false;
127694e18382SJose Abreu 	tpriv->double_vlan = svlan;
127794e18382SJose Abreu 	init_completion(&tpriv->comp);
127894e18382SJose Abreu 
127994e18382SJose Abreu 	tpriv->pt.type = svlan ? htons(ETH_P_8021Q) : htons(ETH_P_IP);
128094e18382SJose Abreu 	tpriv->pt.func = stmmac_test_vlan_validate;
128194e18382SJose Abreu 	tpriv->pt.dev = priv->dev;
128294e18382SJose Abreu 	tpriv->pt.af_packet_priv = tpriv;
128394e18382SJose Abreu 	tpriv->packet = &attr;
128494e18382SJose Abreu 	tpriv->vlan_id = 0x123;
128594e18382SJose Abreu 	dev_add_pack(&tpriv->pt);
128694e18382SJose Abreu 
128794e18382SJose Abreu 	ret = vlan_vid_add(priv->dev, htons(proto), tpriv->vlan_id);
128894e18382SJose Abreu 	if (ret)
128994e18382SJose Abreu 		goto cleanup;
129094e18382SJose Abreu 
129194e18382SJose Abreu 	attr.dst = priv->dev->dev_addr;
129294e18382SJose Abreu 
129394e18382SJose Abreu 	skb = stmmac_test_get_udp_skb(priv, &attr);
129494e18382SJose Abreu 	if (!skb) {
129594e18382SJose Abreu 		ret = -ENOMEM;
129694e18382SJose Abreu 		goto vlan_del;
129794e18382SJose Abreu 	}
129894e18382SJose Abreu 
129994e18382SJose Abreu 	__vlan_hwaccel_put_tag(skb, htons(proto), tpriv->vlan_id);
130094e18382SJose Abreu 	skb->protocol = htons(proto);
130194e18382SJose Abreu 
130205373e31SJose Abreu 	ret = dev_direct_xmit(skb, 0);
130394e18382SJose Abreu 	if (ret)
130494e18382SJose Abreu 		goto vlan_del;
130594e18382SJose Abreu 
130694e18382SJose Abreu 	wait_for_completion_timeout(&tpriv->comp, STMMAC_LB_TIMEOUT);
130794e18382SJose Abreu 	ret = tpriv->ok ? 0 : -ETIMEDOUT;
130894e18382SJose Abreu 
130994e18382SJose Abreu vlan_del:
131094e18382SJose Abreu 	vlan_vid_del(priv->dev, htons(proto), tpriv->vlan_id);
131194e18382SJose Abreu cleanup:
131294e18382SJose Abreu 	dev_remove_pack(&tpriv->pt);
131394e18382SJose Abreu 	kfree(tpriv);
131494e18382SJose Abreu 	return ret;
131594e18382SJose Abreu }
131694e18382SJose Abreu 
stmmac_test_vlanoff(struct stmmac_priv * priv)131794e18382SJose Abreu static int stmmac_test_vlanoff(struct stmmac_priv *priv)
131894e18382SJose Abreu {
131994e18382SJose Abreu 	return stmmac_test_vlanoff_common(priv, false);
132094e18382SJose Abreu }
132194e18382SJose Abreu 
stmmac_test_svlanoff(struct stmmac_priv * priv)132294e18382SJose Abreu static int stmmac_test_svlanoff(struct stmmac_priv *priv)
132394e18382SJose Abreu {
132494e18382SJose Abreu 	if (!priv->dma_cap.dvlan)
132594e18382SJose Abreu 		return -EOPNOTSUPP;
132694e18382SJose Abreu 	return stmmac_test_vlanoff_common(priv, true);
132794e18382SJose Abreu }
132894e18382SJose Abreu 
13294647e021SJose Abreu #ifdef CONFIG_NET_CLS_ACT
__stmmac_test_l3filt(struct stmmac_priv * priv,u32 dst,u32 src,u32 dst_mask,u32 src_mask)13304647e021SJose Abreu static int __stmmac_test_l3filt(struct stmmac_priv *priv, u32 dst, u32 src,
13314647e021SJose Abreu 				u32 dst_mask, u32 src_mask)
13324647e021SJose Abreu {
13334647e021SJose Abreu 	struct flow_dissector_key_ipv4_addrs key, mask;
13344647e021SJose Abreu 	unsigned long dummy_cookie = 0xdeadbeef;
13354647e021SJose Abreu 	struct stmmac_packet_attrs attr = { };
13364647e021SJose Abreu 	struct flow_dissector *dissector;
13374647e021SJose Abreu 	struct flow_cls_offload *cls;
1338e715d745SJose Abreu 	int ret, old_enable = 0;
13394647e021SJose Abreu 	struct flow_rule *rule;
13404647e021SJose Abreu 
13414647e021SJose Abreu 	if (!tc_can_offload(priv->dev))
13424647e021SJose Abreu 		return -EOPNOTSUPP;
13434647e021SJose Abreu 	if (!priv->dma_cap.l3l4fnum)
13444647e021SJose Abreu 		return -EOPNOTSUPP;
1345e715d745SJose Abreu 	if (priv->rss.enable) {
1346e715d745SJose Abreu 		old_enable = priv->rss.enable;
1347e715d745SJose Abreu 		priv->rss.enable = false;
1348b6b6cc9aSArnd Bergmann 		stmmac_rss_configure(priv, priv->hw, NULL,
13494647e021SJose Abreu 				     priv->plat->rx_queues_to_use);
1350e715d745SJose Abreu 	}
13514647e021SJose Abreu 
13524647e021SJose Abreu 	dissector = kzalloc(sizeof(*dissector), GFP_KERNEL);
13534647e021SJose Abreu 	if (!dissector) {
13544647e021SJose Abreu 		ret = -ENOMEM;
13554647e021SJose Abreu 		goto cleanup_rss;
13564647e021SJose Abreu 	}
13574647e021SJose Abreu 
13582b3082c6SRatheesh Kannoth 	dissector->used_keys |= (1ULL << FLOW_DISSECTOR_KEY_IPV4_ADDRS);
13594647e021SJose Abreu 	dissector->offset[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = 0;
13604647e021SJose Abreu 
13614647e021SJose Abreu 	cls = kzalloc(sizeof(*cls), GFP_KERNEL);
13624647e021SJose Abreu 	if (!cls) {
13634647e021SJose Abreu 		ret = -ENOMEM;
13644647e021SJose Abreu 		goto cleanup_dissector;
13654647e021SJose Abreu 	}
13664647e021SJose Abreu 
13674647e021SJose Abreu 	cls->common.chain_index = 0;
13684647e021SJose Abreu 	cls->command = FLOW_CLS_REPLACE;
13694647e021SJose Abreu 	cls->cookie = dummy_cookie;
13704647e021SJose Abreu 
13714647e021SJose Abreu 	rule = kzalloc(struct_size(rule, action.entries, 1), GFP_KERNEL);
13724647e021SJose Abreu 	if (!rule) {
13734647e021SJose Abreu 		ret = -ENOMEM;
13744647e021SJose Abreu 		goto cleanup_cls;
13754647e021SJose Abreu 	}
13764647e021SJose Abreu 
13774647e021SJose Abreu 	rule->match.dissector = dissector;
13784647e021SJose Abreu 	rule->match.key = (void *)&key;
13794647e021SJose Abreu 	rule->match.mask = (void *)&mask;
13804647e021SJose Abreu 
13814647e021SJose Abreu 	key.src = htonl(src);
13824647e021SJose Abreu 	key.dst = htonl(dst);
13834647e021SJose Abreu 	mask.src = src_mask;
13844647e021SJose Abreu 	mask.dst = dst_mask;
13854647e021SJose Abreu 
13864647e021SJose Abreu 	cls->rule = rule;
13874647e021SJose Abreu 
13884647e021SJose Abreu 	rule->action.entries[0].id = FLOW_ACTION_DROP;
13890dfb2d82SJakub Kicinski 	rule->action.entries[0].hw_stats = FLOW_ACTION_HW_STATS_ANY;
13904647e021SJose Abreu 	rule->action.num_entries = 1;
13914647e021SJose Abreu 
13924647e021SJose Abreu 	attr.dst = priv->dev->dev_addr;
13934647e021SJose Abreu 	attr.ip_dst = dst;
13944647e021SJose Abreu 	attr.ip_src = src;
13954647e021SJose Abreu 
13964647e021SJose Abreu 	/* Shall receive packet */
13974647e021SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
13984647e021SJose Abreu 	if (ret)
13994647e021SJose Abreu 		goto cleanup_rule;
14004647e021SJose Abreu 
14014647e021SJose Abreu 	ret = stmmac_tc_setup_cls(priv, priv, cls);
14024647e021SJose Abreu 	if (ret)
14034647e021SJose Abreu 		goto cleanup_rule;
14044647e021SJose Abreu 
14054647e021SJose Abreu 	/* Shall NOT receive packet */
14064647e021SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
14074647e021SJose Abreu 	ret = ret ? 0 : -EINVAL;
14084647e021SJose Abreu 
14094647e021SJose Abreu 	cls->command = FLOW_CLS_DESTROY;
14104647e021SJose Abreu 	stmmac_tc_setup_cls(priv, priv, cls);
14114647e021SJose Abreu cleanup_rule:
14124647e021SJose Abreu 	kfree(rule);
14134647e021SJose Abreu cleanup_cls:
14144647e021SJose Abreu 	kfree(cls);
14154647e021SJose Abreu cleanup_dissector:
14164647e021SJose Abreu 	kfree(dissector);
14174647e021SJose Abreu cleanup_rss:
1418e715d745SJose Abreu 	if (old_enable) {
1419e715d745SJose Abreu 		priv->rss.enable = old_enable;
14204647e021SJose Abreu 		stmmac_rss_configure(priv, priv->hw, &priv->rss,
14214647e021SJose Abreu 				     priv->plat->rx_queues_to_use);
14224647e021SJose Abreu 	}
14234647e021SJose Abreu 
14244647e021SJose Abreu 	return ret;
14254647e021SJose Abreu }
14264647e021SJose Abreu #else
__stmmac_test_l3filt(struct stmmac_priv * priv,u32 dst,u32 src,u32 dst_mask,u32 src_mask)14274647e021SJose Abreu static int __stmmac_test_l3filt(struct stmmac_priv *priv, u32 dst, u32 src,
14284647e021SJose Abreu 				u32 dst_mask, u32 src_mask)
14294647e021SJose Abreu {
14304647e021SJose Abreu 	return -EOPNOTSUPP;
14314647e021SJose Abreu }
14324647e021SJose Abreu #endif
14334647e021SJose Abreu 
stmmac_test_l3filt_da(struct stmmac_priv * priv)14344647e021SJose Abreu static int stmmac_test_l3filt_da(struct stmmac_priv *priv)
14354647e021SJose Abreu {
14364647e021SJose Abreu 	u32 addr = 0x10203040;
14374647e021SJose Abreu 
14384647e021SJose Abreu 	return __stmmac_test_l3filt(priv, addr, 0, ~0, 0);
14394647e021SJose Abreu }
14404647e021SJose Abreu 
stmmac_test_l3filt_sa(struct stmmac_priv * priv)14414647e021SJose Abreu static int stmmac_test_l3filt_sa(struct stmmac_priv *priv)
14424647e021SJose Abreu {
14434647e021SJose Abreu 	u32 addr = 0x10203040;
14444647e021SJose Abreu 
14454647e021SJose Abreu 	return __stmmac_test_l3filt(priv, 0, addr, 0, ~0);
14464647e021SJose Abreu }
14474647e021SJose Abreu 
14484647e021SJose Abreu #ifdef CONFIG_NET_CLS_ACT
__stmmac_test_l4filt(struct stmmac_priv * priv,u32 dst,u32 src,u32 dst_mask,u32 src_mask,bool udp)14494647e021SJose Abreu static int __stmmac_test_l4filt(struct stmmac_priv *priv, u32 dst, u32 src,
14504647e021SJose Abreu 				u32 dst_mask, u32 src_mask, bool udp)
14514647e021SJose Abreu {
14524647e021SJose Abreu 	struct {
14534647e021SJose Abreu 		struct flow_dissector_key_basic bkey;
14544647e021SJose Abreu 		struct flow_dissector_key_ports key;
14554647e021SJose Abreu 	} __aligned(BITS_PER_LONG / 8) keys;
14564647e021SJose Abreu 	struct {
14574647e021SJose Abreu 		struct flow_dissector_key_basic bmask;
14584647e021SJose Abreu 		struct flow_dissector_key_ports mask;
14594647e021SJose Abreu 	} __aligned(BITS_PER_LONG / 8) masks;
14604647e021SJose Abreu 	unsigned long dummy_cookie = 0xdeadbeef;
14614647e021SJose Abreu 	struct stmmac_packet_attrs attr = { };
14624647e021SJose Abreu 	struct flow_dissector *dissector;
14634647e021SJose Abreu 	struct flow_cls_offload *cls;
1464e715d745SJose Abreu 	int ret, old_enable = 0;
14654647e021SJose Abreu 	struct flow_rule *rule;
14664647e021SJose Abreu 
14674647e021SJose Abreu 	if (!tc_can_offload(priv->dev))
14684647e021SJose Abreu 		return -EOPNOTSUPP;
14694647e021SJose Abreu 	if (!priv->dma_cap.l3l4fnum)
14704647e021SJose Abreu 		return -EOPNOTSUPP;
1471e715d745SJose Abreu 	if (priv->rss.enable) {
1472e715d745SJose Abreu 		old_enable = priv->rss.enable;
1473e715d745SJose Abreu 		priv->rss.enable = false;
1474b6b6cc9aSArnd Bergmann 		stmmac_rss_configure(priv, priv->hw, NULL,
14754647e021SJose Abreu 				     priv->plat->rx_queues_to_use);
1476e715d745SJose Abreu 	}
14774647e021SJose Abreu 
14784647e021SJose Abreu 	dissector = kzalloc(sizeof(*dissector), GFP_KERNEL);
14794647e021SJose Abreu 	if (!dissector) {
14804647e021SJose Abreu 		ret = -ENOMEM;
14814647e021SJose Abreu 		goto cleanup_rss;
14824647e021SJose Abreu 	}
14834647e021SJose Abreu 
14842b3082c6SRatheesh Kannoth 	dissector->used_keys |= (1ULL << FLOW_DISSECTOR_KEY_BASIC);
14852b3082c6SRatheesh Kannoth 	dissector->used_keys |= (1ULL << FLOW_DISSECTOR_KEY_PORTS);
14864647e021SJose Abreu 	dissector->offset[FLOW_DISSECTOR_KEY_BASIC] = 0;
14874647e021SJose Abreu 	dissector->offset[FLOW_DISSECTOR_KEY_PORTS] = offsetof(typeof(keys), key);
14884647e021SJose Abreu 
14894647e021SJose Abreu 	cls = kzalloc(sizeof(*cls), GFP_KERNEL);
14904647e021SJose Abreu 	if (!cls) {
14914647e021SJose Abreu 		ret = -ENOMEM;
14924647e021SJose Abreu 		goto cleanup_dissector;
14934647e021SJose Abreu 	}
14944647e021SJose Abreu 
14954647e021SJose Abreu 	cls->common.chain_index = 0;
14964647e021SJose Abreu 	cls->command = FLOW_CLS_REPLACE;
14974647e021SJose Abreu 	cls->cookie = dummy_cookie;
14984647e021SJose Abreu 
14994647e021SJose Abreu 	rule = kzalloc(struct_size(rule, action.entries, 1), GFP_KERNEL);
15004647e021SJose Abreu 	if (!rule) {
15014647e021SJose Abreu 		ret = -ENOMEM;
15024647e021SJose Abreu 		goto cleanup_cls;
15034647e021SJose Abreu 	}
15044647e021SJose Abreu 
15054647e021SJose Abreu 	rule->match.dissector = dissector;
15064647e021SJose Abreu 	rule->match.key = (void *)&keys;
15074647e021SJose Abreu 	rule->match.mask = (void *)&masks;
15084647e021SJose Abreu 
15094647e021SJose Abreu 	keys.bkey.ip_proto = udp ? IPPROTO_UDP : IPPROTO_TCP;
15104647e021SJose Abreu 	keys.key.src = htons(src);
15114647e021SJose Abreu 	keys.key.dst = htons(dst);
15124647e021SJose Abreu 	masks.mask.src = src_mask;
15134647e021SJose Abreu 	masks.mask.dst = dst_mask;
15144647e021SJose Abreu 
15154647e021SJose Abreu 	cls->rule = rule;
15164647e021SJose Abreu 
15174647e021SJose Abreu 	rule->action.entries[0].id = FLOW_ACTION_DROP;
15180dfb2d82SJakub Kicinski 	rule->action.entries[0].hw_stats = FLOW_ACTION_HW_STATS_ANY;
15194647e021SJose Abreu 	rule->action.num_entries = 1;
15204647e021SJose Abreu 
15214647e021SJose Abreu 	attr.dst = priv->dev->dev_addr;
15224647e021SJose Abreu 	attr.tcp = !udp;
15234647e021SJose Abreu 	attr.sport = src;
15244647e021SJose Abreu 	attr.dport = dst;
15254647e021SJose Abreu 	attr.ip_dst = 0;
15264647e021SJose Abreu 
15274647e021SJose Abreu 	/* Shall receive packet */
15284647e021SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
15294647e021SJose Abreu 	if (ret)
15304647e021SJose Abreu 		goto cleanup_rule;
15314647e021SJose Abreu 
15324647e021SJose Abreu 	ret = stmmac_tc_setup_cls(priv, priv, cls);
15334647e021SJose Abreu 	if (ret)
15344647e021SJose Abreu 		goto cleanup_rule;
15354647e021SJose Abreu 
15364647e021SJose Abreu 	/* Shall NOT receive packet */
15374647e021SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
15384647e021SJose Abreu 	ret = ret ? 0 : -EINVAL;
15394647e021SJose Abreu 
15404647e021SJose Abreu 	cls->command = FLOW_CLS_DESTROY;
15414647e021SJose Abreu 	stmmac_tc_setup_cls(priv, priv, cls);
15424647e021SJose Abreu cleanup_rule:
15434647e021SJose Abreu 	kfree(rule);
15444647e021SJose Abreu cleanup_cls:
15454647e021SJose Abreu 	kfree(cls);
15464647e021SJose Abreu cleanup_dissector:
15474647e021SJose Abreu 	kfree(dissector);
15484647e021SJose Abreu cleanup_rss:
1549e715d745SJose Abreu 	if (old_enable) {
1550e715d745SJose Abreu 		priv->rss.enable = old_enable;
15514647e021SJose Abreu 		stmmac_rss_configure(priv, priv->hw, &priv->rss,
15524647e021SJose Abreu 				     priv->plat->rx_queues_to_use);
15534647e021SJose Abreu 	}
15544647e021SJose Abreu 
15554647e021SJose Abreu 	return ret;
15564647e021SJose Abreu }
15574647e021SJose Abreu #else
__stmmac_test_l4filt(struct stmmac_priv * priv,u32 dst,u32 src,u32 dst_mask,u32 src_mask,bool udp)15584647e021SJose Abreu static int __stmmac_test_l4filt(struct stmmac_priv *priv, u32 dst, u32 src,
15594647e021SJose Abreu 				u32 dst_mask, u32 src_mask, bool udp)
15604647e021SJose Abreu {
15614647e021SJose Abreu 	return -EOPNOTSUPP;
15624647e021SJose Abreu }
15634647e021SJose Abreu #endif
15644647e021SJose Abreu 
stmmac_test_l4filt_da_tcp(struct stmmac_priv * priv)15654647e021SJose Abreu static int stmmac_test_l4filt_da_tcp(struct stmmac_priv *priv)
15664647e021SJose Abreu {
15674647e021SJose Abreu 	u16 dummy_port = 0x123;
15684647e021SJose Abreu 
15694647e021SJose Abreu 	return __stmmac_test_l4filt(priv, dummy_port, 0, ~0, 0, false);
15704647e021SJose Abreu }
15714647e021SJose Abreu 
stmmac_test_l4filt_sa_tcp(struct stmmac_priv * priv)15724647e021SJose Abreu static int stmmac_test_l4filt_sa_tcp(struct stmmac_priv *priv)
15734647e021SJose Abreu {
15744647e021SJose Abreu 	u16 dummy_port = 0x123;
15754647e021SJose Abreu 
15764647e021SJose Abreu 	return __stmmac_test_l4filt(priv, 0, dummy_port, 0, ~0, false);
15774647e021SJose Abreu }
15784647e021SJose Abreu 
stmmac_test_l4filt_da_udp(struct stmmac_priv * priv)15794647e021SJose Abreu static int stmmac_test_l4filt_da_udp(struct stmmac_priv *priv)
15804647e021SJose Abreu {
15814647e021SJose Abreu 	u16 dummy_port = 0x123;
15824647e021SJose Abreu 
15834647e021SJose Abreu 	return __stmmac_test_l4filt(priv, dummy_port, 0, ~0, 0, true);
15844647e021SJose Abreu }
15854647e021SJose Abreu 
stmmac_test_l4filt_sa_udp(struct stmmac_priv * priv)15864647e021SJose Abreu static int stmmac_test_l4filt_sa_udp(struct stmmac_priv *priv)
15874647e021SJose Abreu {
15884647e021SJose Abreu 	u16 dummy_port = 0x123;
15894647e021SJose Abreu 
15904647e021SJose Abreu 	return __stmmac_test_l4filt(priv, 0, dummy_port, 0, ~0, true);
15914647e021SJose Abreu }
15924647e021SJose Abreu 
stmmac_test_arp_validate(struct sk_buff * skb,struct net_device * ndev,struct packet_type * pt,struct net_device * orig_ndev)15935e3fb0a6SJose Abreu static int stmmac_test_arp_validate(struct sk_buff *skb,
15945e3fb0a6SJose Abreu 				    struct net_device *ndev,
15955e3fb0a6SJose Abreu 				    struct packet_type *pt,
15965e3fb0a6SJose Abreu 				    struct net_device *orig_ndev)
15975e3fb0a6SJose Abreu {
15985e3fb0a6SJose Abreu 	struct stmmac_test_priv *tpriv = pt->af_packet_priv;
15995e3fb0a6SJose Abreu 	struct ethhdr *ehdr;
16005e3fb0a6SJose Abreu 	struct arphdr *ahdr;
16015e3fb0a6SJose Abreu 
16025e3fb0a6SJose Abreu 	ehdr = (struct ethhdr *)skb_mac_header(skb);
16030b9f932eSJose Abreu 	if (!ether_addr_equal_unaligned(ehdr->h_dest, tpriv->packet->src))
16045e3fb0a6SJose Abreu 		goto out;
16055e3fb0a6SJose Abreu 
16065e3fb0a6SJose Abreu 	ahdr = arp_hdr(skb);
16075e3fb0a6SJose Abreu 	if (ahdr->ar_op != htons(ARPOP_REPLY))
16085e3fb0a6SJose Abreu 		goto out;
16095e3fb0a6SJose Abreu 
16105e3fb0a6SJose Abreu 	tpriv->ok = true;
16115e3fb0a6SJose Abreu 	complete(&tpriv->comp);
16125e3fb0a6SJose Abreu out:
16135e3fb0a6SJose Abreu 	kfree_skb(skb);
16145e3fb0a6SJose Abreu 	return 0;
16155e3fb0a6SJose Abreu }
16165e3fb0a6SJose Abreu 
stmmac_test_arpoffload(struct stmmac_priv * priv)16175e3fb0a6SJose Abreu static int stmmac_test_arpoffload(struct stmmac_priv *priv)
16185e3fb0a6SJose Abreu {
16195e3fb0a6SJose Abreu 	unsigned char src[ETH_ALEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
16205e3fb0a6SJose Abreu 	unsigned char dst[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
16215e3fb0a6SJose Abreu 	struct stmmac_packet_attrs attr = { };
16225e3fb0a6SJose Abreu 	struct stmmac_test_priv *tpriv;
16235e3fb0a6SJose Abreu 	struct sk_buff *skb = NULL;
16245e3fb0a6SJose Abreu 	u32 ip_addr = 0xdeadcafe;
16255e3fb0a6SJose Abreu 	u32 ip_src = 0xdeadbeef;
16265e3fb0a6SJose Abreu 	int ret;
16275e3fb0a6SJose Abreu 
16285e3fb0a6SJose Abreu 	if (!priv->dma_cap.arpoffsel)
16295e3fb0a6SJose Abreu 		return -EOPNOTSUPP;
16305e3fb0a6SJose Abreu 
16315e3fb0a6SJose Abreu 	tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL);
16325e3fb0a6SJose Abreu 	if (!tpriv)
16335e3fb0a6SJose Abreu 		return -ENOMEM;
16345e3fb0a6SJose Abreu 
16355e3fb0a6SJose Abreu 	tpriv->ok = false;
16365e3fb0a6SJose Abreu 	init_completion(&tpriv->comp);
16375e3fb0a6SJose Abreu 
16385e3fb0a6SJose Abreu 	tpriv->pt.type = htons(ETH_P_ARP);
16395e3fb0a6SJose Abreu 	tpriv->pt.func = stmmac_test_arp_validate;
16405e3fb0a6SJose Abreu 	tpriv->pt.dev = priv->dev;
16415e3fb0a6SJose Abreu 	tpriv->pt.af_packet_priv = tpriv;
16425e3fb0a6SJose Abreu 	tpriv->packet = &attr;
16435e3fb0a6SJose Abreu 	dev_add_pack(&tpriv->pt);
16445e3fb0a6SJose Abreu 
16455e3fb0a6SJose Abreu 	attr.src = src;
16465e3fb0a6SJose Abreu 	attr.ip_src = ip_src;
16475e3fb0a6SJose Abreu 	attr.dst = dst;
16485e3fb0a6SJose Abreu 	attr.ip_dst = ip_addr;
16495e3fb0a6SJose Abreu 
16505e3fb0a6SJose Abreu 	skb = stmmac_test_get_arp_skb(priv, &attr);
16515e3fb0a6SJose Abreu 	if (!skb) {
16525e3fb0a6SJose Abreu 		ret = -ENOMEM;
16535e3fb0a6SJose Abreu 		goto cleanup;
16545e3fb0a6SJose Abreu 	}
16555e3fb0a6SJose Abreu 
16565e3fb0a6SJose Abreu 	ret = stmmac_set_arp_offload(priv, priv->hw, true, ip_addr);
1657f150b63fSZhang Changzhong 	if (ret) {
1658f150b63fSZhang Changzhong 		kfree_skb(skb);
16595e3fb0a6SJose Abreu 		goto cleanup;
1660f150b63fSZhang Changzhong 	}
16615e3fb0a6SJose Abreu 
16625e3fb0a6SJose Abreu 	ret = dev_set_promiscuity(priv->dev, 1);
1663f150b63fSZhang Changzhong 	if (ret) {
1664f150b63fSZhang Changzhong 		kfree_skb(skb);
16655e3fb0a6SJose Abreu 		goto cleanup;
1666f150b63fSZhang Changzhong 	}
16675e3fb0a6SJose Abreu 
166805373e31SJose Abreu 	ret = dev_direct_xmit(skb, 0);
16695e3fb0a6SJose Abreu 	if (ret)
16705e3fb0a6SJose Abreu 		goto cleanup_promisc;
16715e3fb0a6SJose Abreu 
16725e3fb0a6SJose Abreu 	wait_for_completion_timeout(&tpriv->comp, STMMAC_LB_TIMEOUT);
16735e3fb0a6SJose Abreu 	ret = tpriv->ok ? 0 : -ETIMEDOUT;
16745e3fb0a6SJose Abreu 
16755e3fb0a6SJose Abreu cleanup_promisc:
16765e3fb0a6SJose Abreu 	dev_set_promiscuity(priv->dev, -1);
16775e3fb0a6SJose Abreu cleanup:
16785e3fb0a6SJose Abreu 	stmmac_set_arp_offload(priv, priv->hw, false, 0x0);
16795e3fb0a6SJose Abreu 	dev_remove_pack(&tpriv->pt);
16805e3fb0a6SJose Abreu 	kfree(tpriv);
16815e3fb0a6SJose Abreu 	return ret;
16825e3fb0a6SJose Abreu }
16835e3fb0a6SJose Abreu 
__stmmac_test_jumbo(struct stmmac_priv * priv,u16 queue)1684427849e8SJose Abreu static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue)
1685427849e8SJose Abreu {
1686427849e8SJose Abreu 	struct stmmac_packet_attrs attr = { };
16878531c808SChristian Marangi 	int size = priv->dma_conf.dma_buf_sz;
1688427849e8SJose Abreu 
1689427849e8SJose Abreu 	attr.dst = priv->dev->dev_addr;
1690427849e8SJose Abreu 	attr.max_size = size - ETH_FCS_LEN;
1691427849e8SJose Abreu 	attr.queue_mapping = queue;
1692427849e8SJose Abreu 
1693427849e8SJose Abreu 	return __stmmac_test_loopback(priv, &attr);
1694427849e8SJose Abreu }
1695427849e8SJose Abreu 
stmmac_test_jumbo(struct stmmac_priv * priv)1696427849e8SJose Abreu static int stmmac_test_jumbo(struct stmmac_priv *priv)
1697427849e8SJose Abreu {
1698427849e8SJose Abreu 	return __stmmac_test_jumbo(priv, 0);
1699427849e8SJose Abreu }
1700427849e8SJose Abreu 
stmmac_test_mjumbo(struct stmmac_priv * priv)1701427849e8SJose Abreu static int stmmac_test_mjumbo(struct stmmac_priv *priv)
1702427849e8SJose Abreu {
1703427849e8SJose Abreu 	u32 chan, tx_cnt = priv->plat->tx_queues_to_use;
1704427849e8SJose Abreu 	int ret;
1705427849e8SJose Abreu 
1706427849e8SJose Abreu 	if (tx_cnt <= 1)
1707427849e8SJose Abreu 		return -EOPNOTSUPP;
1708427849e8SJose Abreu 
1709427849e8SJose Abreu 	for (chan = 0; chan < tx_cnt; chan++) {
1710427849e8SJose Abreu 		ret = __stmmac_test_jumbo(priv, chan);
1711427849e8SJose Abreu 		if (ret)
1712427849e8SJose Abreu 			return ret;
1713427849e8SJose Abreu 	}
1714427849e8SJose Abreu 
1715427849e8SJose Abreu 	return 0;
1716427849e8SJose Abreu }
1717427849e8SJose Abreu 
stmmac_test_sph(struct stmmac_priv * priv)17185f8475daSJose Abreu static int stmmac_test_sph(struct stmmac_priv *priv)
17195f8475daSJose Abreu {
17205f8475daSJose Abreu 	unsigned long cnt_end, cnt_start = priv->xstats.rx_split_hdr_pkt_n;
17215f8475daSJose Abreu 	struct stmmac_packet_attrs attr = { };
17225f8475daSJose Abreu 	int ret;
17235f8475daSJose Abreu 
17245f8475daSJose Abreu 	if (!priv->sph)
17255f8475daSJose Abreu 		return -EOPNOTSUPP;
17265f8475daSJose Abreu 
17275f8475daSJose Abreu 	/* Check for UDP first */
17285f8475daSJose Abreu 	attr.dst = priv->dev->dev_addr;
17295f8475daSJose Abreu 	attr.tcp = false;
17305f8475daSJose Abreu 
17315f8475daSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
17325f8475daSJose Abreu 	if (ret)
17335f8475daSJose Abreu 		return ret;
17345f8475daSJose Abreu 
17355f8475daSJose Abreu 	cnt_end = priv->xstats.rx_split_hdr_pkt_n;
17365f8475daSJose Abreu 	if (cnt_end <= cnt_start)
17375f8475daSJose Abreu 		return -EINVAL;
17385f8475daSJose Abreu 
17395f8475daSJose Abreu 	/* Check for TCP now */
17405f8475daSJose Abreu 	cnt_start = cnt_end;
17415f8475daSJose Abreu 
17425f8475daSJose Abreu 	attr.dst = priv->dev->dev_addr;
17435f8475daSJose Abreu 	attr.tcp = true;
17445f8475daSJose Abreu 
17455f8475daSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
17465f8475daSJose Abreu 	if (ret)
17475f8475daSJose Abreu 		return ret;
17485f8475daSJose Abreu 
17495f8475daSJose Abreu 	cnt_end = priv->xstats.rx_split_hdr_pkt_n;
17505f8475daSJose Abreu 	if (cnt_end <= cnt_start)
17515f8475daSJose Abreu 		return -EINVAL;
17525f8475daSJose Abreu 
17535f8475daSJose Abreu 	return 0;
17545f8475daSJose Abreu }
17555f8475daSJose Abreu 
stmmac_test_tbs(struct stmmac_priv * priv)175628c1cf73SJose Abreu static int stmmac_test_tbs(struct stmmac_priv *priv)
175728c1cf73SJose Abreu {
175828c1cf73SJose Abreu #define STMMAC_TBS_LT_OFFSET		(500 * 1000 * 1000) /* 500 ms*/
175928c1cf73SJose Abreu 	struct stmmac_packet_attrs attr = { };
176028c1cf73SJose Abreu 	struct tc_etf_qopt_offload qopt;
176128c1cf73SJose Abreu 	u64 start_time, curr_time = 0;
176228c1cf73SJose Abreu 	unsigned long flags;
176328c1cf73SJose Abreu 	int ret, i;
176428c1cf73SJose Abreu 
176528c1cf73SJose Abreu 	if (!priv->hwts_tx_en)
176628c1cf73SJose Abreu 		return -EOPNOTSUPP;
176728c1cf73SJose Abreu 
176828c1cf73SJose Abreu 	/* Find first TBS enabled Queue, if any */
176928c1cf73SJose Abreu 	for (i = 0; i < priv->plat->tx_queues_to_use; i++)
17708531c808SChristian Marangi 		if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_AVAIL)
177128c1cf73SJose Abreu 			break;
177228c1cf73SJose Abreu 
177328c1cf73SJose Abreu 	if (i >= priv->plat->tx_queues_to_use)
177428c1cf73SJose Abreu 		return -EOPNOTSUPP;
177528c1cf73SJose Abreu 
177628c1cf73SJose Abreu 	qopt.enable = true;
177728c1cf73SJose Abreu 	qopt.queue = i;
177828c1cf73SJose Abreu 
177928c1cf73SJose Abreu 	ret = stmmac_tc_setup_etf(priv, priv, &qopt);
178028c1cf73SJose Abreu 	if (ret)
178128c1cf73SJose Abreu 		return ret;
178228c1cf73SJose Abreu 
1783642436a1SYannick Vignon 	read_lock_irqsave(&priv->ptp_lock, flags);
178428c1cf73SJose Abreu 	stmmac_get_systime(priv, priv->ptpaddr, &curr_time);
1785642436a1SYannick Vignon 	read_unlock_irqrestore(&priv->ptp_lock, flags);
178628c1cf73SJose Abreu 
178728c1cf73SJose Abreu 	if (!curr_time) {
178828c1cf73SJose Abreu 		ret = -EOPNOTSUPP;
178928c1cf73SJose Abreu 		goto fail_disable;
179028c1cf73SJose Abreu 	}
179128c1cf73SJose Abreu 
179228c1cf73SJose Abreu 	start_time = curr_time;
179328c1cf73SJose Abreu 	curr_time += STMMAC_TBS_LT_OFFSET;
179428c1cf73SJose Abreu 
179528c1cf73SJose Abreu 	attr.dst = priv->dev->dev_addr;
179628c1cf73SJose Abreu 	attr.timestamp = curr_time;
179728c1cf73SJose Abreu 	attr.timeout = nsecs_to_jiffies(2 * STMMAC_TBS_LT_OFFSET);
179828c1cf73SJose Abreu 	attr.queue_mapping = i;
179928c1cf73SJose Abreu 
180028c1cf73SJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
180128c1cf73SJose Abreu 	if (ret)
180228c1cf73SJose Abreu 		goto fail_disable;
180328c1cf73SJose Abreu 
180428c1cf73SJose Abreu 	/* Check if expected time has elapsed */
1805642436a1SYannick Vignon 	read_lock_irqsave(&priv->ptp_lock, flags);
180628c1cf73SJose Abreu 	stmmac_get_systime(priv, priv->ptpaddr, &curr_time);
1807642436a1SYannick Vignon 	read_unlock_irqrestore(&priv->ptp_lock, flags);
180828c1cf73SJose Abreu 
180928c1cf73SJose Abreu 	if ((curr_time - start_time) < STMMAC_TBS_LT_OFFSET)
181028c1cf73SJose Abreu 		ret = -EINVAL;
181128c1cf73SJose Abreu 
181228c1cf73SJose Abreu fail_disable:
181328c1cf73SJose Abreu 	qopt.enable = false;
181428c1cf73SJose Abreu 	stmmac_tc_setup_etf(priv, priv, &qopt);
181528c1cf73SJose Abreu 	return ret;
181628c1cf73SJose Abreu }
181728c1cf73SJose Abreu 
1818091810dbSJose Abreu #define STMMAC_LOOPBACK_NONE	0
1819091810dbSJose Abreu #define STMMAC_LOOPBACK_MAC	1
1820091810dbSJose Abreu #define STMMAC_LOOPBACK_PHY	2
1821091810dbSJose Abreu 
1822091810dbSJose Abreu static const struct stmmac_test {
1823091810dbSJose Abreu 	char name[ETH_GSTRING_LEN];
1824091810dbSJose Abreu 	int lb;
1825091810dbSJose Abreu 	int (*fn)(struct stmmac_priv *priv);
1826091810dbSJose Abreu } stmmac_selftests[] = {
1827091810dbSJose Abreu 	{
1828091810dbSJose Abreu 		.name = "MAC Loopback               ",
1829091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_MAC,
1830091810dbSJose Abreu 		.fn = stmmac_test_mac_loopback,
1831091810dbSJose Abreu 	}, {
1832091810dbSJose Abreu 		.name = "PHY Loopback               ",
1833091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_NONE, /* Test will handle it */
1834091810dbSJose Abreu 		.fn = stmmac_test_phy_loopback,
1835091810dbSJose Abreu 	}, {
1836091810dbSJose Abreu 		.name = "MMC Counters               ",
1837091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1838091810dbSJose Abreu 		.fn = stmmac_test_mmc,
1839091810dbSJose Abreu 	}, {
1840091810dbSJose Abreu 		.name = "EEE                        ",
1841091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1842091810dbSJose Abreu 		.fn = stmmac_test_eee,
1843091810dbSJose Abreu 	}, {
1844091810dbSJose Abreu 		.name = "Hash Filter MC             ",
1845091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1846091810dbSJose Abreu 		.fn = stmmac_test_hfilt,
1847091810dbSJose Abreu 	}, {
1848091810dbSJose Abreu 		.name = "Perfect Filter UC          ",
1849091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1850091810dbSJose Abreu 		.fn = stmmac_test_pfilt,
1851091810dbSJose Abreu 	}, {
1852091810dbSJose Abreu 		.name = "MC Filter                  ",
1853091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1854091810dbSJose Abreu 		.fn = stmmac_test_mcfilt,
1855091810dbSJose Abreu 	}, {
1856091810dbSJose Abreu 		.name = "UC Filter                  ",
1857091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1858091810dbSJose Abreu 		.fn = stmmac_test_ucfilt,
1859091810dbSJose Abreu 	}, {
1860091810dbSJose Abreu 		.name = "Flow Control               ",
1861091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1862091810dbSJose Abreu 		.fn = stmmac_test_flowctrl,
18631fbdad00SJose Abreu 	}, {
18641fbdad00SJose Abreu 		.name = "RSS                        ",
18651fbdad00SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
18661fbdad00SJose Abreu 		.fn = stmmac_test_rss,
186774043f6bSJose Abreu 	}, {
186874043f6bSJose Abreu 		.name = "VLAN Filtering             ",
186974043f6bSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
187074043f6bSJose Abreu 		.fn = stmmac_test_vlanfilt,
187174043f6bSJose Abreu 	}, {
18721b2250a0SJose Abreu 		.name = "VLAN Filtering (perf)      ",
18731b2250a0SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
18741b2250a0SJose Abreu 		.fn = stmmac_test_vlanfilt_perfect,
18751b2250a0SJose Abreu 	}, {
18761b2250a0SJose Abreu 		.name = "Double VLAN Filter         ",
187774043f6bSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
187874043f6bSJose Abreu 		.fn = stmmac_test_dvlanfilt,
1879ccfc639aSJose Abreu 	}, {
18801b2250a0SJose Abreu 		.name = "Double VLAN Filter (perf)  ",
18811b2250a0SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
18821b2250a0SJose Abreu 		.fn = stmmac_test_dvlanfilt_perfect,
18831b2250a0SJose Abreu 	}, {
1884ccfc639aSJose Abreu 		.name = "Flexible RX Parser         ",
1885ccfc639aSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1886ccfc639aSJose Abreu 		.fn = stmmac_test_rxp,
18878180d579SJose Abreu 	}, {
18888180d579SJose Abreu 		.name = "SA Insertion (desc)        ",
18898180d579SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
18908180d579SJose Abreu 		.fn = stmmac_test_desc_sai,
18918180d579SJose Abreu 	}, {
18928180d579SJose Abreu 		.name = "SA Replacement (desc)      ",
18938180d579SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
18948180d579SJose Abreu 		.fn = stmmac_test_desc_sar,
18958180d579SJose Abreu 	}, {
18968180d579SJose Abreu 		.name = "SA Insertion (reg)         ",
18978180d579SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
18988180d579SJose Abreu 		.fn = stmmac_test_reg_sai,
18998180d579SJose Abreu 	}, {
19008180d579SJose Abreu 		.name = "SA Replacement (reg)       ",
19018180d579SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19028180d579SJose Abreu 		.fn = stmmac_test_reg_sar,
190394e18382SJose Abreu 	}, {
190494e18382SJose Abreu 		.name = "VLAN TX Insertion          ",
190594e18382SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
190694e18382SJose Abreu 		.fn = stmmac_test_vlanoff,
190794e18382SJose Abreu 	}, {
190894e18382SJose Abreu 		.name = "SVLAN TX Insertion         ",
190994e18382SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
191094e18382SJose Abreu 		.fn = stmmac_test_svlanoff,
19114647e021SJose Abreu 	}, {
19124647e021SJose Abreu 		.name = "L3 DA Filtering            ",
19134647e021SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19144647e021SJose Abreu 		.fn = stmmac_test_l3filt_da,
19154647e021SJose Abreu 	}, {
19164647e021SJose Abreu 		.name = "L3 SA Filtering            ",
19174647e021SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19184647e021SJose Abreu 		.fn = stmmac_test_l3filt_sa,
19194647e021SJose Abreu 	}, {
19204647e021SJose Abreu 		.name = "L4 DA TCP Filtering        ",
19214647e021SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19224647e021SJose Abreu 		.fn = stmmac_test_l4filt_da_tcp,
19234647e021SJose Abreu 	}, {
19244647e021SJose Abreu 		.name = "L4 SA TCP Filtering        ",
19254647e021SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19264647e021SJose Abreu 		.fn = stmmac_test_l4filt_sa_tcp,
19274647e021SJose Abreu 	}, {
19284647e021SJose Abreu 		.name = "L4 DA UDP Filtering        ",
19294647e021SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19304647e021SJose Abreu 		.fn = stmmac_test_l4filt_da_udp,
19314647e021SJose Abreu 	}, {
19324647e021SJose Abreu 		.name = "L4 SA UDP Filtering        ",
19334647e021SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19344647e021SJose Abreu 		.fn = stmmac_test_l4filt_sa_udp,
19355e3fb0a6SJose Abreu 	}, {
19365e3fb0a6SJose Abreu 		.name = "ARP Offload                ",
19375e3fb0a6SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19385e3fb0a6SJose Abreu 		.fn = stmmac_test_arpoffload,
1939427849e8SJose Abreu 	}, {
1940427849e8SJose Abreu 		.name = "Jumbo Frame                ",
1941427849e8SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1942427849e8SJose Abreu 		.fn = stmmac_test_jumbo,
1943427849e8SJose Abreu 	}, {
1944427849e8SJose Abreu 		.name = "Multichannel Jumbo         ",
1945427849e8SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
1946427849e8SJose Abreu 		.fn = stmmac_test_mjumbo,
19475f8475daSJose Abreu 	}, {
19485f8475daSJose Abreu 		.name = "Split Header               ",
19495f8475daSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
19505f8475daSJose Abreu 		.fn = stmmac_test_sph,
195128c1cf73SJose Abreu 	}, {
195228c1cf73SJose Abreu 		.name = "TBS (ETF Scheduler)        ",
195328c1cf73SJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
195428c1cf73SJose Abreu 		.fn = stmmac_test_tbs,
1955091810dbSJose Abreu 	},
1956091810dbSJose Abreu };
1957091810dbSJose Abreu 
stmmac_selftest_run(struct net_device * dev,struct ethtool_test * etest,u64 * buf)1958091810dbSJose Abreu void stmmac_selftest_run(struct net_device *dev,
1959091810dbSJose Abreu 			 struct ethtool_test *etest, u64 *buf)
1960091810dbSJose Abreu {
1961091810dbSJose Abreu 	struct stmmac_priv *priv = netdev_priv(dev);
1962091810dbSJose Abreu 	int count = stmmac_selftest_get_count(priv);
1963091810dbSJose Abreu 	int i, ret;
1964091810dbSJose Abreu 
1965091810dbSJose Abreu 	memset(buf, 0, sizeof(*buf) * count);
1966091810dbSJose Abreu 	stmmac_test_next_id = 0;
1967091810dbSJose Abreu 
1968091810dbSJose Abreu 	if (etest->flags != ETH_TEST_FL_OFFLINE) {
1969091810dbSJose Abreu 		netdev_err(priv->dev, "Only offline tests are supported\n");
1970091810dbSJose Abreu 		etest->flags |= ETH_TEST_FL_FAILED;
1971091810dbSJose Abreu 		return;
197205373e31SJose Abreu 	} else if (!netif_carrier_ok(dev)) {
1973091810dbSJose Abreu 		netdev_err(priv->dev, "You need valid Link to execute tests\n");
1974091810dbSJose Abreu 		etest->flags |= ETH_TEST_FL_FAILED;
1975091810dbSJose Abreu 		return;
1976091810dbSJose Abreu 	}
1977091810dbSJose Abreu 
1978091810dbSJose Abreu 	/* Wait for queues drain */
1979091810dbSJose Abreu 	msleep(200);
1980091810dbSJose Abreu 
1981091810dbSJose Abreu 	for (i = 0; i < count; i++) {
1982091810dbSJose Abreu 		ret = 0;
1983091810dbSJose Abreu 
1984091810dbSJose Abreu 		switch (stmmac_selftests[i].lb) {
1985091810dbSJose Abreu 		case STMMAC_LOOPBACK_PHY:
1986091810dbSJose Abreu 			ret = -EOPNOTSUPP;
1987091810dbSJose Abreu 			if (dev->phydev)
1988091810dbSJose Abreu 				ret = phy_loopback(dev->phydev, true);
1989091810dbSJose Abreu 			if (!ret)
1990091810dbSJose Abreu 				break;
1991df561f66SGustavo A. R. Silva 			fallthrough;
1992091810dbSJose Abreu 		case STMMAC_LOOPBACK_MAC:
1993091810dbSJose Abreu 			ret = stmmac_set_mac_loopback(priv, priv->ioaddr, true);
1994091810dbSJose Abreu 			break;
1995091810dbSJose Abreu 		case STMMAC_LOOPBACK_NONE:
1996091810dbSJose Abreu 			break;
1997091810dbSJose Abreu 		default:
1998091810dbSJose Abreu 			ret = -EOPNOTSUPP;
1999091810dbSJose Abreu 			break;
2000091810dbSJose Abreu 		}
2001091810dbSJose Abreu 
2002091810dbSJose Abreu 		/*
2003091810dbSJose Abreu 		 * First tests will always be MAC / PHY loobpack. If any of
2004091810dbSJose Abreu 		 * them is not supported we abort earlier.
2005091810dbSJose Abreu 		 */
2006091810dbSJose Abreu 		if (ret) {
2007091810dbSJose Abreu 			netdev_err(priv->dev, "Loopback is not supported\n");
2008091810dbSJose Abreu 			etest->flags |= ETH_TEST_FL_FAILED;
2009091810dbSJose Abreu 			break;
2010091810dbSJose Abreu 		}
2011091810dbSJose Abreu 
2012091810dbSJose Abreu 		ret = stmmac_selftests[i].fn(priv);
2013091810dbSJose Abreu 		if (ret && (ret != -EOPNOTSUPP))
2014091810dbSJose Abreu 			etest->flags |= ETH_TEST_FL_FAILED;
2015091810dbSJose Abreu 		buf[i] = ret;
2016091810dbSJose Abreu 
2017091810dbSJose Abreu 		switch (stmmac_selftests[i].lb) {
2018091810dbSJose Abreu 		case STMMAC_LOOPBACK_PHY:
2019091810dbSJose Abreu 			ret = -EOPNOTSUPP;
2020091810dbSJose Abreu 			if (dev->phydev)
2021091810dbSJose Abreu 				ret = phy_loopback(dev->phydev, false);
2022091810dbSJose Abreu 			if (!ret)
2023091810dbSJose Abreu 				break;
2024df561f66SGustavo A. R. Silva 			fallthrough;
2025091810dbSJose Abreu 		case STMMAC_LOOPBACK_MAC:
2026091810dbSJose Abreu 			stmmac_set_mac_loopback(priv, priv->ioaddr, false);
2027091810dbSJose Abreu 			break;
2028091810dbSJose Abreu 		default:
2029091810dbSJose Abreu 			break;
2030091810dbSJose Abreu 		}
2031091810dbSJose Abreu 	}
2032091810dbSJose Abreu }
2033091810dbSJose Abreu 
stmmac_selftest_get_strings(struct stmmac_priv * priv,u8 * data)2034091810dbSJose Abreu void stmmac_selftest_get_strings(struct stmmac_priv *priv, u8 *data)
2035091810dbSJose Abreu {
2036091810dbSJose Abreu 	u8 *p = data;
2037091810dbSJose Abreu 	int i;
2038091810dbSJose Abreu 
2039091810dbSJose Abreu 	for (i = 0; i < stmmac_selftest_get_count(priv); i++) {
2040091810dbSJose Abreu 		snprintf(p, ETH_GSTRING_LEN, "%2d. %s", i + 1,
2041091810dbSJose Abreu 			 stmmac_selftests[i].name);
2042091810dbSJose Abreu 		p += ETH_GSTRING_LEN;
2043091810dbSJose Abreu 	}
2044091810dbSJose Abreu }
2045091810dbSJose Abreu 
stmmac_selftest_get_count(struct stmmac_priv * priv)2046091810dbSJose Abreu int stmmac_selftest_get_count(struct stmmac_priv *priv)
2047091810dbSJose Abreu {
2048091810dbSJose Abreu 	return ARRAY_SIZE(stmmac_selftests);
2049091810dbSJose Abreu }
2050