xref: /linux/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c (revision 2d135dea53fa7e8c9a20ccbac9857e3b9a926f9f)
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 
9091810dbSJose Abreu #include <linux/completion.h>
10091810dbSJose Abreu #include <linux/ethtool.h>
11091810dbSJose Abreu #include <linux/ip.h>
12091810dbSJose Abreu #include <linux/phy.h>
13091810dbSJose Abreu #include <linux/udp.h>
14091810dbSJose Abreu #include <net/tcp.h>
15091810dbSJose Abreu #include <net/udp.h>
16091810dbSJose Abreu #include "stmmac.h"
17091810dbSJose Abreu 
18091810dbSJose Abreu struct stmmachdr {
19091810dbSJose Abreu 	__be32 version;
20091810dbSJose Abreu 	__be64 magic;
21091810dbSJose Abreu 	u8 id;
22091810dbSJose Abreu } __packed;
23091810dbSJose Abreu 
24091810dbSJose Abreu #define STMMAC_TEST_PKT_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \
25091810dbSJose Abreu 			      sizeof(struct stmmachdr))
26091810dbSJose Abreu #define STMMAC_TEST_PKT_MAGIC	0xdeadcafecafedeadULL
27091810dbSJose Abreu #define STMMAC_LB_TIMEOUT	msecs_to_jiffies(200)
28091810dbSJose Abreu 
29091810dbSJose Abreu struct stmmac_packet_attrs {
30091810dbSJose Abreu 	int vlan;
31091810dbSJose Abreu 	int vlan_id_in;
32091810dbSJose Abreu 	int vlan_id_out;
33091810dbSJose Abreu 	unsigned char *src;
34091810dbSJose Abreu 	unsigned char *dst;
35091810dbSJose Abreu 	u32 ip_src;
36091810dbSJose Abreu 	u32 ip_dst;
37091810dbSJose Abreu 	int tcp;
38091810dbSJose Abreu 	int sport;
39091810dbSJose Abreu 	int dport;
40091810dbSJose Abreu 	u32 exp_hash;
41091810dbSJose Abreu 	int dont_wait;
42091810dbSJose Abreu 	int timeout;
43091810dbSJose Abreu 	int size;
44091810dbSJose Abreu 	int remove_sa;
45091810dbSJose Abreu 	u8 id;
46091810dbSJose Abreu };
47091810dbSJose Abreu 
48091810dbSJose Abreu static u8 stmmac_test_next_id;
49091810dbSJose Abreu 
50091810dbSJose Abreu static struct sk_buff *stmmac_test_get_udp_skb(struct stmmac_priv *priv,
51091810dbSJose Abreu 					       struct stmmac_packet_attrs *attr)
52091810dbSJose Abreu {
53091810dbSJose Abreu 	struct sk_buff *skb = NULL;
54091810dbSJose Abreu 	struct udphdr *uhdr = NULL;
55091810dbSJose Abreu 	struct tcphdr *thdr = NULL;
56091810dbSJose Abreu 	struct stmmachdr *shdr;
57091810dbSJose Abreu 	struct ethhdr *ehdr;
58091810dbSJose Abreu 	struct iphdr *ihdr;
59091810dbSJose Abreu 	int iplen, size;
60091810dbSJose Abreu 
61091810dbSJose Abreu 	size = attr->size + STMMAC_TEST_PKT_SIZE;
62091810dbSJose Abreu 	if (attr->vlan) {
63091810dbSJose Abreu 		size += 4;
64091810dbSJose Abreu 		if (attr->vlan > 1)
65091810dbSJose Abreu 			size += 4;
66091810dbSJose Abreu 	}
67091810dbSJose Abreu 
68091810dbSJose Abreu 	if (attr->tcp)
69091810dbSJose Abreu 		size += sizeof(struct tcphdr);
70091810dbSJose Abreu 	else
71091810dbSJose Abreu 		size += sizeof(struct udphdr);
72091810dbSJose Abreu 
73091810dbSJose Abreu 	skb = netdev_alloc_skb(priv->dev, size);
74091810dbSJose Abreu 	if (!skb)
75091810dbSJose Abreu 		return NULL;
76091810dbSJose Abreu 
77091810dbSJose Abreu 	prefetchw(skb->data);
78091810dbSJose Abreu 	skb_reserve(skb, NET_IP_ALIGN);
79091810dbSJose Abreu 
80091810dbSJose Abreu 	if (attr->vlan > 1)
81091810dbSJose Abreu 		ehdr = skb_push(skb, ETH_HLEN + 8);
82091810dbSJose Abreu 	else if (attr->vlan)
83091810dbSJose Abreu 		ehdr = skb_push(skb, ETH_HLEN + 4);
84091810dbSJose Abreu 	else if (attr->remove_sa)
85091810dbSJose Abreu 		ehdr = skb_push(skb, ETH_HLEN - 6);
86091810dbSJose Abreu 	else
87091810dbSJose Abreu 		ehdr = skb_push(skb, ETH_HLEN);
88091810dbSJose Abreu 	skb_reset_mac_header(skb);
89091810dbSJose Abreu 
90091810dbSJose Abreu 	skb_set_network_header(skb, skb->len);
91091810dbSJose Abreu 	ihdr = skb_put(skb, sizeof(*ihdr));
92091810dbSJose Abreu 
93091810dbSJose Abreu 	skb_set_transport_header(skb, skb->len);
94091810dbSJose Abreu 	if (attr->tcp)
95091810dbSJose Abreu 		thdr = skb_put(skb, sizeof(*thdr));
96091810dbSJose Abreu 	else
97091810dbSJose Abreu 		uhdr = skb_put(skb, sizeof(*uhdr));
98091810dbSJose Abreu 
99091810dbSJose Abreu 	if (!attr->remove_sa)
100091810dbSJose Abreu 		eth_zero_addr(ehdr->h_source);
101091810dbSJose Abreu 	eth_zero_addr(ehdr->h_dest);
102091810dbSJose Abreu 	if (attr->src && !attr->remove_sa)
103091810dbSJose Abreu 		ether_addr_copy(ehdr->h_source, attr->src);
104091810dbSJose Abreu 	if (attr->dst)
105091810dbSJose Abreu 		ether_addr_copy(ehdr->h_dest, attr->dst);
106091810dbSJose Abreu 
107091810dbSJose Abreu 	if (!attr->remove_sa) {
108091810dbSJose Abreu 		ehdr->h_proto = htons(ETH_P_IP);
109091810dbSJose Abreu 	} else {
110091810dbSJose Abreu 		__be16 *ptr = (__be16 *)ehdr;
111091810dbSJose Abreu 
112091810dbSJose Abreu 		/* HACK */
113091810dbSJose Abreu 		ptr[3] = htons(ETH_P_IP);
114091810dbSJose Abreu 	}
115091810dbSJose Abreu 
116091810dbSJose Abreu 	if (attr->vlan) {
117*2d135deaSJose Abreu 		__be16 *tag, *proto;
118091810dbSJose Abreu 
119091810dbSJose Abreu 		if (!attr->remove_sa) {
120091810dbSJose Abreu 			tag = (void *)ehdr + ETH_HLEN;
121091810dbSJose Abreu 			proto = (void *)ehdr + (2 * ETH_ALEN);
122091810dbSJose Abreu 		} else {
123091810dbSJose Abreu 			tag = (void *)ehdr + ETH_HLEN - 6;
124091810dbSJose Abreu 			proto = (void *)ehdr + ETH_ALEN;
125091810dbSJose Abreu 		}
126091810dbSJose Abreu 
127091810dbSJose Abreu 		proto[0] = htons(ETH_P_8021Q);
128091810dbSJose Abreu 		tag[0] = htons(attr->vlan_id_out);
129091810dbSJose Abreu 		tag[1] = htons(ETH_P_IP);
130091810dbSJose Abreu 		if (attr->vlan > 1) {
131091810dbSJose Abreu 			proto[0] = htons(ETH_P_8021AD);
132091810dbSJose Abreu 			tag[1] = htons(ETH_P_8021Q);
133091810dbSJose Abreu 			tag[2] = htons(attr->vlan_id_in);
134091810dbSJose Abreu 			tag[3] = htons(ETH_P_IP);
135091810dbSJose Abreu 		}
136091810dbSJose Abreu 	}
137091810dbSJose Abreu 
138091810dbSJose Abreu 	if (attr->tcp) {
139091810dbSJose Abreu 		thdr->source = htons(attr->sport);
140091810dbSJose Abreu 		thdr->dest = htons(attr->dport);
141091810dbSJose Abreu 		thdr->doff = sizeof(struct tcphdr) / 4;
142091810dbSJose Abreu 		thdr->check = 0;
143091810dbSJose Abreu 	} else {
144091810dbSJose Abreu 		uhdr->source = htons(attr->sport);
145091810dbSJose Abreu 		uhdr->dest = htons(attr->dport);
146091810dbSJose Abreu 		uhdr->len = htons(sizeof(*shdr) + sizeof(*uhdr) + attr->size);
147091810dbSJose Abreu 		uhdr->check = 0;
148091810dbSJose Abreu 	}
149091810dbSJose Abreu 
150091810dbSJose Abreu 	ihdr->ihl = 5;
151091810dbSJose Abreu 	ihdr->ttl = 32;
152091810dbSJose Abreu 	ihdr->version = 4;
153091810dbSJose Abreu 	if (attr->tcp)
154091810dbSJose Abreu 		ihdr->protocol = IPPROTO_TCP;
155091810dbSJose Abreu 	else
156091810dbSJose Abreu 		ihdr->protocol = IPPROTO_UDP;
157091810dbSJose Abreu 	iplen = sizeof(*ihdr) + sizeof(*shdr) + attr->size;
158091810dbSJose Abreu 	if (attr->tcp)
159091810dbSJose Abreu 		iplen += sizeof(*thdr);
160091810dbSJose Abreu 	else
161091810dbSJose Abreu 		iplen += sizeof(*uhdr);
162091810dbSJose Abreu 	ihdr->tot_len = htons(iplen);
163091810dbSJose Abreu 	ihdr->frag_off = 0;
164091810dbSJose Abreu 	ihdr->saddr = 0;
165091810dbSJose Abreu 	ihdr->daddr = htonl(attr->ip_dst);
166091810dbSJose Abreu 	ihdr->tos = 0;
167091810dbSJose Abreu 	ihdr->id = 0;
168091810dbSJose Abreu 	ip_send_check(ihdr);
169091810dbSJose Abreu 
170091810dbSJose Abreu 	shdr = skb_put(skb, sizeof(*shdr));
171091810dbSJose Abreu 	shdr->version = 0;
172091810dbSJose Abreu 	shdr->magic = cpu_to_be64(STMMAC_TEST_PKT_MAGIC);
173091810dbSJose Abreu 	attr->id = stmmac_test_next_id;
174091810dbSJose Abreu 	shdr->id = stmmac_test_next_id++;
175091810dbSJose Abreu 
176091810dbSJose Abreu 	if (attr->size)
177091810dbSJose Abreu 		skb_put(skb, attr->size);
178091810dbSJose Abreu 
179091810dbSJose Abreu 	skb->csum = 0;
180091810dbSJose Abreu 	skb->ip_summed = CHECKSUM_PARTIAL;
181091810dbSJose Abreu 	if (attr->tcp) {
182091810dbSJose Abreu 		thdr->check = ~tcp_v4_check(skb->len, ihdr->saddr, ihdr->daddr, 0);
183091810dbSJose Abreu 		skb->csum_start = skb_transport_header(skb) - skb->head;
184091810dbSJose Abreu 		skb->csum_offset = offsetof(struct tcphdr, check);
185091810dbSJose Abreu 	} else {
186091810dbSJose Abreu 		udp4_hwcsum(skb, ihdr->saddr, ihdr->daddr);
187091810dbSJose Abreu 	}
188091810dbSJose Abreu 
189091810dbSJose Abreu 	skb->protocol = htons(ETH_P_IP);
190091810dbSJose Abreu 	skb->pkt_type = PACKET_HOST;
191091810dbSJose Abreu 	skb->dev = priv->dev;
192091810dbSJose Abreu 
193091810dbSJose Abreu 	return skb;
194091810dbSJose Abreu }
195091810dbSJose Abreu 
196091810dbSJose Abreu struct stmmac_test_priv {
197091810dbSJose Abreu 	struct stmmac_packet_attrs *packet;
198091810dbSJose Abreu 	struct packet_type pt;
199091810dbSJose Abreu 	struct completion comp;
200091810dbSJose Abreu 	int double_vlan;
201091810dbSJose Abreu 	int vlan_id;
202091810dbSJose Abreu 	int ok;
203091810dbSJose Abreu };
204091810dbSJose Abreu 
205091810dbSJose Abreu static int stmmac_test_loopback_validate(struct sk_buff *skb,
206091810dbSJose Abreu 					 struct net_device *ndev,
207091810dbSJose Abreu 					 struct packet_type *pt,
208091810dbSJose Abreu 					 struct net_device *orig_ndev)
209091810dbSJose Abreu {
210091810dbSJose Abreu 	struct stmmac_test_priv *tpriv = pt->af_packet_priv;
211091810dbSJose Abreu 	struct stmmachdr *shdr;
212091810dbSJose Abreu 	struct ethhdr *ehdr;
213091810dbSJose Abreu 	struct udphdr *uhdr;
214091810dbSJose Abreu 	struct tcphdr *thdr;
215091810dbSJose Abreu 	struct iphdr *ihdr;
216091810dbSJose Abreu 
217091810dbSJose Abreu 	skb = skb_unshare(skb, GFP_ATOMIC);
218091810dbSJose Abreu 	if (!skb)
219091810dbSJose Abreu 		goto out;
220091810dbSJose Abreu 
221091810dbSJose Abreu 	if (skb_linearize(skb))
222091810dbSJose Abreu 		goto out;
223091810dbSJose Abreu 	if (skb_headlen(skb) < (STMMAC_TEST_PKT_SIZE - ETH_HLEN))
224091810dbSJose Abreu 		goto out;
225091810dbSJose Abreu 
226091810dbSJose Abreu 	ehdr = (struct ethhdr *)skb_mac_header(skb);
227091810dbSJose Abreu 	if (tpriv->packet->dst) {
228091810dbSJose Abreu 		if (!ether_addr_equal(ehdr->h_dest, tpriv->packet->dst))
229091810dbSJose Abreu 			goto out;
230091810dbSJose Abreu 	}
231091810dbSJose Abreu 	if (tpriv->packet->src) {
232091810dbSJose Abreu 		if (!ether_addr_equal(ehdr->h_source, orig_ndev->dev_addr))
233091810dbSJose Abreu 			goto out;
234091810dbSJose Abreu 	}
235091810dbSJose Abreu 
236091810dbSJose Abreu 	ihdr = ip_hdr(skb);
237091810dbSJose Abreu 	if (tpriv->double_vlan)
238091810dbSJose Abreu 		ihdr = (struct iphdr *)(skb_network_header(skb) + 4);
239091810dbSJose Abreu 
240091810dbSJose Abreu 	if (tpriv->packet->tcp) {
241091810dbSJose Abreu 		if (ihdr->protocol != IPPROTO_TCP)
242091810dbSJose Abreu 			goto out;
243091810dbSJose Abreu 
244091810dbSJose Abreu 		thdr = (struct tcphdr *)((u8 *)ihdr + 4 * ihdr->ihl);
245091810dbSJose Abreu 		if (thdr->dest != htons(tpriv->packet->dport))
246091810dbSJose Abreu 			goto out;
247091810dbSJose Abreu 
248091810dbSJose Abreu 		shdr = (struct stmmachdr *)((u8 *)thdr + sizeof(*thdr));
249091810dbSJose Abreu 	} else {
250091810dbSJose Abreu 		if (ihdr->protocol != IPPROTO_UDP)
251091810dbSJose Abreu 			goto out;
252091810dbSJose Abreu 
253091810dbSJose Abreu 		uhdr = (struct udphdr *)((u8 *)ihdr + 4 * ihdr->ihl);
254091810dbSJose Abreu 		if (uhdr->dest != htons(tpriv->packet->dport))
255091810dbSJose Abreu 			goto out;
256091810dbSJose Abreu 
257091810dbSJose Abreu 		shdr = (struct stmmachdr *)((u8 *)uhdr + sizeof(*uhdr));
258091810dbSJose Abreu 	}
259091810dbSJose Abreu 
260091810dbSJose Abreu 	if (shdr->magic != cpu_to_be64(STMMAC_TEST_PKT_MAGIC))
261091810dbSJose Abreu 		goto out;
262091810dbSJose Abreu 	if (tpriv->packet->exp_hash && !skb->hash)
263091810dbSJose Abreu 		goto out;
264091810dbSJose Abreu 	if (tpriv->packet->id != shdr->id)
265091810dbSJose Abreu 		goto out;
266091810dbSJose Abreu 
267091810dbSJose Abreu 	tpriv->ok = true;
268091810dbSJose Abreu 	complete(&tpriv->comp);
269091810dbSJose Abreu out:
270091810dbSJose Abreu 	kfree_skb(skb);
271091810dbSJose Abreu 	return 0;
272091810dbSJose Abreu }
273091810dbSJose Abreu 
274091810dbSJose Abreu static int __stmmac_test_loopback(struct stmmac_priv *priv,
275091810dbSJose Abreu 				  struct stmmac_packet_attrs *attr)
276091810dbSJose Abreu {
277091810dbSJose Abreu 	struct stmmac_test_priv *tpriv;
278091810dbSJose Abreu 	struct sk_buff *skb = NULL;
279091810dbSJose Abreu 	int ret = 0;
280091810dbSJose Abreu 
281091810dbSJose Abreu 	tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL);
282091810dbSJose Abreu 	if (!tpriv)
283091810dbSJose Abreu 		return -ENOMEM;
284091810dbSJose Abreu 
285091810dbSJose Abreu 	tpriv->ok = false;
286091810dbSJose Abreu 	init_completion(&tpriv->comp);
287091810dbSJose Abreu 
288091810dbSJose Abreu 	tpriv->pt.type = htons(ETH_P_IP);
289091810dbSJose Abreu 	tpriv->pt.func = stmmac_test_loopback_validate;
290091810dbSJose Abreu 	tpriv->pt.dev = priv->dev;
291091810dbSJose Abreu 	tpriv->pt.af_packet_priv = tpriv;
292091810dbSJose Abreu 	tpriv->packet = attr;
293091810dbSJose Abreu 	dev_add_pack(&tpriv->pt);
294091810dbSJose Abreu 
295091810dbSJose Abreu 	skb = stmmac_test_get_udp_skb(priv, attr);
296091810dbSJose Abreu 	if (!skb) {
297091810dbSJose Abreu 		ret = -ENOMEM;
298091810dbSJose Abreu 		goto cleanup;
299091810dbSJose Abreu 	}
300091810dbSJose Abreu 
301091810dbSJose Abreu 	skb_set_queue_mapping(skb, 0);
302091810dbSJose Abreu 	ret = dev_queue_xmit(skb);
303091810dbSJose Abreu 	if (ret)
304091810dbSJose Abreu 		goto cleanup;
305091810dbSJose Abreu 
306091810dbSJose Abreu 	if (attr->dont_wait)
307091810dbSJose Abreu 		goto cleanup;
308091810dbSJose Abreu 
309091810dbSJose Abreu 	if (!attr->timeout)
310091810dbSJose Abreu 		attr->timeout = STMMAC_LB_TIMEOUT;
311091810dbSJose Abreu 
312091810dbSJose Abreu 	wait_for_completion_timeout(&tpriv->comp, attr->timeout);
313091810dbSJose Abreu 	ret = !tpriv->ok;
314091810dbSJose Abreu 
315091810dbSJose Abreu cleanup:
316091810dbSJose Abreu 	dev_remove_pack(&tpriv->pt);
317091810dbSJose Abreu 	kfree(tpriv);
318091810dbSJose Abreu 	return ret;
319091810dbSJose Abreu }
320091810dbSJose Abreu 
321091810dbSJose Abreu static int stmmac_test_mac_loopback(struct stmmac_priv *priv)
322091810dbSJose Abreu {
323091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
324091810dbSJose Abreu 
325091810dbSJose Abreu 	attr.dst = priv->dev->dev_addr;
326091810dbSJose Abreu 	return __stmmac_test_loopback(priv, &attr);
327091810dbSJose Abreu }
328091810dbSJose Abreu 
329091810dbSJose Abreu static int stmmac_test_phy_loopback(struct stmmac_priv *priv)
330091810dbSJose Abreu {
331091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
332091810dbSJose Abreu 	int ret;
333091810dbSJose Abreu 
334091810dbSJose Abreu 	if (!priv->dev->phydev)
335091810dbSJose Abreu 		return -EBUSY;
336091810dbSJose Abreu 
337091810dbSJose Abreu 	ret = phy_loopback(priv->dev->phydev, true);
338091810dbSJose Abreu 	if (ret)
339091810dbSJose Abreu 		return ret;
340091810dbSJose Abreu 
341091810dbSJose Abreu 	attr.dst = priv->dev->dev_addr;
342091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
343091810dbSJose Abreu 
344091810dbSJose Abreu 	phy_loopback(priv->dev->phydev, false);
345091810dbSJose Abreu 	return ret;
346091810dbSJose Abreu }
347091810dbSJose Abreu 
348091810dbSJose Abreu static int stmmac_test_mmc(struct stmmac_priv *priv)
349091810dbSJose Abreu {
350091810dbSJose Abreu 	struct stmmac_counters initial, final;
351091810dbSJose Abreu 	int ret;
352091810dbSJose Abreu 
353091810dbSJose Abreu 	memset(&initial, 0, sizeof(initial));
354091810dbSJose Abreu 	memset(&final, 0, sizeof(final));
355091810dbSJose Abreu 
356091810dbSJose Abreu 	if (!priv->dma_cap.rmon)
357091810dbSJose Abreu 		return -EOPNOTSUPP;
358091810dbSJose Abreu 
359091810dbSJose Abreu 	/* Save previous results into internal struct */
360091810dbSJose Abreu 	stmmac_mmc_read(priv, priv->mmcaddr, &priv->mmc);
361091810dbSJose Abreu 
362091810dbSJose Abreu 	ret = stmmac_test_mac_loopback(priv);
363091810dbSJose Abreu 	if (ret)
364091810dbSJose Abreu 		return ret;
365091810dbSJose Abreu 
366091810dbSJose Abreu 	/* These will be loopback results so no need to save them */
367091810dbSJose Abreu 	stmmac_mmc_read(priv, priv->mmcaddr, &final);
368091810dbSJose Abreu 
369091810dbSJose Abreu 	/*
370091810dbSJose Abreu 	 * The number of MMC counters available depends on HW configuration
371091810dbSJose Abreu 	 * so we just use this one to validate the feature. I hope there is
372091810dbSJose Abreu 	 * not a version without this counter.
373091810dbSJose Abreu 	 */
374091810dbSJose Abreu 	if (final.mmc_tx_framecount_g <= initial.mmc_tx_framecount_g)
375091810dbSJose Abreu 		return -EINVAL;
376091810dbSJose Abreu 
377091810dbSJose Abreu 	return 0;
378091810dbSJose Abreu }
379091810dbSJose Abreu 
380091810dbSJose Abreu static int stmmac_test_eee(struct stmmac_priv *priv)
381091810dbSJose Abreu {
382091810dbSJose Abreu 	struct stmmac_extra_stats *initial, *final;
383091810dbSJose Abreu 	int retries = 10;
384091810dbSJose Abreu 	int ret;
385091810dbSJose Abreu 
386091810dbSJose Abreu 	if (!priv->dma_cap.eee || !priv->eee_active)
387091810dbSJose Abreu 		return -EOPNOTSUPP;
388091810dbSJose Abreu 
389091810dbSJose Abreu 	initial = kzalloc(sizeof(*initial), GFP_KERNEL);
390091810dbSJose Abreu 	if (!initial)
391091810dbSJose Abreu 		return -ENOMEM;
392091810dbSJose Abreu 
393091810dbSJose Abreu 	final = kzalloc(sizeof(*final), GFP_KERNEL);
394091810dbSJose Abreu 	if (!final) {
395091810dbSJose Abreu 		ret = -ENOMEM;
396091810dbSJose Abreu 		goto out_free_initial;
397091810dbSJose Abreu 	}
398091810dbSJose Abreu 
399091810dbSJose Abreu 	memcpy(initial, &priv->xstats, sizeof(*initial));
400091810dbSJose Abreu 
401091810dbSJose Abreu 	ret = stmmac_test_mac_loopback(priv);
402091810dbSJose Abreu 	if (ret)
403091810dbSJose Abreu 		goto out_free_final;
404091810dbSJose Abreu 
405091810dbSJose Abreu 	/* We have no traffic in the line so, sooner or later it will go LPI */
406091810dbSJose Abreu 	while (--retries) {
407091810dbSJose Abreu 		memcpy(final, &priv->xstats, sizeof(*final));
408091810dbSJose Abreu 
409091810dbSJose Abreu 		if (final->irq_tx_path_in_lpi_mode_n >
410091810dbSJose Abreu 		    initial->irq_tx_path_in_lpi_mode_n)
411091810dbSJose Abreu 			break;
412091810dbSJose Abreu 		msleep(100);
413091810dbSJose Abreu 	}
414091810dbSJose Abreu 
415091810dbSJose Abreu 	if (!retries) {
416091810dbSJose Abreu 		ret = -ETIMEDOUT;
417091810dbSJose Abreu 		goto out_free_final;
418091810dbSJose Abreu 	}
419091810dbSJose Abreu 
420091810dbSJose Abreu 	if (final->irq_tx_path_in_lpi_mode_n <=
421091810dbSJose Abreu 	    initial->irq_tx_path_in_lpi_mode_n) {
422091810dbSJose Abreu 		ret = -EINVAL;
423091810dbSJose Abreu 		goto out_free_final;
424091810dbSJose Abreu 	}
425091810dbSJose Abreu 
426091810dbSJose Abreu 	if (final->irq_tx_path_exit_lpi_mode_n <=
427091810dbSJose Abreu 	    initial->irq_tx_path_exit_lpi_mode_n) {
428091810dbSJose Abreu 		ret = -EINVAL;
429091810dbSJose Abreu 		goto out_free_final;
430091810dbSJose Abreu 	}
431091810dbSJose Abreu 
432091810dbSJose Abreu out_free_final:
433091810dbSJose Abreu 	kfree(final);
434091810dbSJose Abreu out_free_initial:
435091810dbSJose Abreu 	kfree(initial);
436091810dbSJose Abreu 	return ret;
437091810dbSJose Abreu }
438091810dbSJose Abreu 
439091810dbSJose Abreu static int stmmac_filter_check(struct stmmac_priv *priv)
440091810dbSJose Abreu {
441091810dbSJose Abreu 	if (!(priv->dev->flags & IFF_PROMISC))
442091810dbSJose Abreu 		return 0;
443091810dbSJose Abreu 
444091810dbSJose Abreu 	netdev_warn(priv->dev, "Test can't be run in promiscuous mode!\n");
445091810dbSJose Abreu 	return -EOPNOTSUPP;
446091810dbSJose Abreu }
447091810dbSJose Abreu 
448091810dbSJose Abreu static int stmmac_test_hfilt(struct stmmac_priv *priv)
449091810dbSJose Abreu {
450091810dbSJose Abreu 	unsigned char gd_addr[ETH_ALEN] = {0x01, 0x00, 0xcc, 0xcc, 0xdd, 0xdd};
451091810dbSJose Abreu 	unsigned char bd_addr[ETH_ALEN] = {0x09, 0x00, 0xaa, 0xaa, 0xbb, 0xbb};
452091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
453091810dbSJose Abreu 	int ret;
454091810dbSJose Abreu 
455091810dbSJose Abreu 	ret = stmmac_filter_check(priv);
456091810dbSJose Abreu 	if (ret)
457091810dbSJose Abreu 		return ret;
458091810dbSJose Abreu 
459091810dbSJose Abreu 	ret = dev_mc_add(priv->dev, gd_addr);
460091810dbSJose Abreu 	if (ret)
461091810dbSJose Abreu 		return ret;
462091810dbSJose Abreu 
463091810dbSJose Abreu 	attr.dst = gd_addr;
464091810dbSJose Abreu 
465091810dbSJose Abreu 	/* Shall receive packet */
466091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
467091810dbSJose Abreu 	if (ret)
468091810dbSJose Abreu 		goto cleanup;
469091810dbSJose Abreu 
470091810dbSJose Abreu 	attr.dst = bd_addr;
471091810dbSJose Abreu 
472091810dbSJose Abreu 	/* Shall NOT receive packet */
473091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
474091810dbSJose Abreu 	ret = !ret;
475091810dbSJose Abreu 
476091810dbSJose Abreu cleanup:
477091810dbSJose Abreu 	dev_mc_del(priv->dev, gd_addr);
478091810dbSJose Abreu 	return ret;
479091810dbSJose Abreu }
480091810dbSJose Abreu 
481091810dbSJose Abreu static int stmmac_test_pfilt(struct stmmac_priv *priv)
482091810dbSJose Abreu {
483091810dbSJose Abreu 	unsigned char gd_addr[ETH_ALEN] = {0x00, 0x01, 0x44, 0x55, 0x66, 0x77};
484091810dbSJose Abreu 	unsigned char bd_addr[ETH_ALEN] = {0x08, 0x00, 0x22, 0x33, 0x44, 0x55};
485091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
486091810dbSJose Abreu 	int ret;
487091810dbSJose Abreu 
488091810dbSJose Abreu 	if (stmmac_filter_check(priv))
489091810dbSJose Abreu 		return -EOPNOTSUPP;
490091810dbSJose Abreu 
491091810dbSJose Abreu 	ret = dev_uc_add(priv->dev, gd_addr);
492091810dbSJose Abreu 	if (ret)
493091810dbSJose Abreu 		return ret;
494091810dbSJose Abreu 
495091810dbSJose Abreu 	attr.dst = gd_addr;
496091810dbSJose Abreu 
497091810dbSJose Abreu 	/* Shall receive packet */
498091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
499091810dbSJose Abreu 	if (ret)
500091810dbSJose Abreu 		goto cleanup;
501091810dbSJose Abreu 
502091810dbSJose Abreu 	attr.dst = bd_addr;
503091810dbSJose Abreu 
504091810dbSJose Abreu 	/* Shall NOT receive packet */
505091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
506091810dbSJose Abreu 	ret = !ret;
507091810dbSJose Abreu 
508091810dbSJose Abreu cleanup:
509091810dbSJose Abreu 	dev_uc_del(priv->dev, gd_addr);
510091810dbSJose Abreu 	return ret;
511091810dbSJose Abreu }
512091810dbSJose Abreu 
513091810dbSJose Abreu static int stmmac_dummy_sync(struct net_device *netdev, const u8 *addr)
514091810dbSJose Abreu {
515091810dbSJose Abreu 	return 0;
516091810dbSJose Abreu }
517091810dbSJose Abreu 
518091810dbSJose Abreu static void stmmac_test_set_rx_mode(struct net_device *netdev)
519091810dbSJose Abreu {
520091810dbSJose Abreu 	/* As we are in test mode of ethtool we already own the rtnl lock
521091810dbSJose Abreu 	 * so no address will change from user. We can just call the
522091810dbSJose Abreu 	 * ndo_set_rx_mode() callback directly */
523091810dbSJose Abreu 	if (netdev->netdev_ops->ndo_set_rx_mode)
524091810dbSJose Abreu 		netdev->netdev_ops->ndo_set_rx_mode(netdev);
525091810dbSJose Abreu }
526091810dbSJose Abreu 
527091810dbSJose Abreu static int stmmac_test_mcfilt(struct stmmac_priv *priv)
528091810dbSJose Abreu {
529091810dbSJose Abreu 	unsigned char uc_addr[ETH_ALEN] = {0x00, 0x01, 0x44, 0x55, 0x66, 0x77};
530091810dbSJose Abreu 	unsigned char mc_addr[ETH_ALEN] = {0x01, 0x01, 0x44, 0x55, 0x66, 0x77};
531091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
532091810dbSJose Abreu 	int ret;
533091810dbSJose Abreu 
534091810dbSJose Abreu 	if (stmmac_filter_check(priv))
535091810dbSJose Abreu 		return -EOPNOTSUPP;
536091810dbSJose Abreu 
537091810dbSJose Abreu 	/* Remove all MC addresses */
538091810dbSJose Abreu 	__dev_mc_unsync(priv->dev, NULL);
539091810dbSJose Abreu 	stmmac_test_set_rx_mode(priv->dev);
540091810dbSJose Abreu 
541091810dbSJose Abreu 	ret = dev_uc_add(priv->dev, uc_addr);
542091810dbSJose Abreu 	if (ret)
543091810dbSJose Abreu 		goto cleanup;
544091810dbSJose Abreu 
545091810dbSJose Abreu 	attr.dst = uc_addr;
546091810dbSJose Abreu 
547091810dbSJose Abreu 	/* Shall receive packet */
548091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
549091810dbSJose Abreu 	if (ret)
550091810dbSJose Abreu 		goto cleanup;
551091810dbSJose Abreu 
552091810dbSJose Abreu 	attr.dst = mc_addr;
553091810dbSJose Abreu 
554091810dbSJose Abreu 	/* Shall NOT receive packet */
555091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
556091810dbSJose Abreu 	ret = !ret;
557091810dbSJose Abreu 
558091810dbSJose Abreu cleanup:
559091810dbSJose Abreu 	dev_uc_del(priv->dev, uc_addr);
560091810dbSJose Abreu 	__dev_mc_sync(priv->dev, stmmac_dummy_sync, NULL);
561091810dbSJose Abreu 	stmmac_test_set_rx_mode(priv->dev);
562091810dbSJose Abreu 	return ret;
563091810dbSJose Abreu }
564091810dbSJose Abreu 
565091810dbSJose Abreu static int stmmac_test_ucfilt(struct stmmac_priv *priv)
566091810dbSJose Abreu {
567091810dbSJose Abreu 	unsigned char uc_addr[ETH_ALEN] = {0x00, 0x01, 0x44, 0x55, 0x66, 0x77};
568091810dbSJose Abreu 	unsigned char mc_addr[ETH_ALEN] = {0x01, 0x01, 0x44, 0x55, 0x66, 0x77};
569091810dbSJose Abreu 	struct stmmac_packet_attrs attr = { };
570091810dbSJose Abreu 	int ret;
571091810dbSJose Abreu 
572091810dbSJose Abreu 	if (stmmac_filter_check(priv))
573091810dbSJose Abreu 		return -EOPNOTSUPP;
574091810dbSJose Abreu 
575091810dbSJose Abreu 	/* Remove all UC addresses */
576091810dbSJose Abreu 	__dev_uc_unsync(priv->dev, NULL);
577091810dbSJose Abreu 	stmmac_test_set_rx_mode(priv->dev);
578091810dbSJose Abreu 
579091810dbSJose Abreu 	ret = dev_mc_add(priv->dev, mc_addr);
580091810dbSJose Abreu 	if (ret)
581091810dbSJose Abreu 		goto cleanup;
582091810dbSJose Abreu 
583091810dbSJose Abreu 	attr.dst = mc_addr;
584091810dbSJose Abreu 
585091810dbSJose Abreu 	/* Shall receive packet */
586091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
587091810dbSJose Abreu 	if (ret)
588091810dbSJose Abreu 		goto cleanup;
589091810dbSJose Abreu 
590091810dbSJose Abreu 	attr.dst = uc_addr;
591091810dbSJose Abreu 
592091810dbSJose Abreu 	/* Shall NOT receive packet */
593091810dbSJose Abreu 	ret = __stmmac_test_loopback(priv, &attr);
594091810dbSJose Abreu 	ret = !ret;
595091810dbSJose Abreu 
596091810dbSJose Abreu cleanup:
597091810dbSJose Abreu 	dev_mc_del(priv->dev, mc_addr);
598091810dbSJose Abreu 	__dev_uc_sync(priv->dev, stmmac_dummy_sync, NULL);
599091810dbSJose Abreu 	stmmac_test_set_rx_mode(priv->dev);
600091810dbSJose Abreu 	return ret;
601091810dbSJose Abreu }
602091810dbSJose Abreu 
603091810dbSJose Abreu static int stmmac_test_flowctrl_validate(struct sk_buff *skb,
604091810dbSJose Abreu 					 struct net_device *ndev,
605091810dbSJose Abreu 					 struct packet_type *pt,
606091810dbSJose Abreu 					 struct net_device *orig_ndev)
607091810dbSJose Abreu {
608091810dbSJose Abreu 	struct stmmac_test_priv *tpriv = pt->af_packet_priv;
609091810dbSJose Abreu 	struct ethhdr *ehdr;
610091810dbSJose Abreu 
611091810dbSJose Abreu 	ehdr = (struct ethhdr *)skb_mac_header(skb);
612091810dbSJose Abreu 	if (!ether_addr_equal(ehdr->h_source, orig_ndev->dev_addr))
613091810dbSJose Abreu 		goto out;
614091810dbSJose Abreu 	if (ehdr->h_proto != htons(ETH_P_PAUSE))
615091810dbSJose Abreu 		goto out;
616091810dbSJose Abreu 
617091810dbSJose Abreu 	tpriv->ok = true;
618091810dbSJose Abreu 	complete(&tpriv->comp);
619091810dbSJose Abreu out:
620091810dbSJose Abreu 	kfree(skb);
621091810dbSJose Abreu 	return 0;
622091810dbSJose Abreu }
623091810dbSJose Abreu 
624091810dbSJose Abreu static int stmmac_test_flowctrl(struct stmmac_priv *priv)
625091810dbSJose Abreu {
626091810dbSJose Abreu 	unsigned char paddr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x01};
627091810dbSJose Abreu 	struct phy_device *phydev = priv->dev->phydev;
628091810dbSJose Abreu 	u32 rx_cnt = priv->plat->rx_queues_to_use;
629091810dbSJose Abreu 	struct stmmac_test_priv *tpriv;
630091810dbSJose Abreu 	unsigned int pkt_count;
631091810dbSJose Abreu 	int i, ret = 0;
632091810dbSJose Abreu 
633091810dbSJose Abreu 	if (!phydev || !phydev->pause)
634091810dbSJose Abreu 		return -EOPNOTSUPP;
635091810dbSJose Abreu 
636091810dbSJose Abreu 	tpriv = kzalloc(sizeof(*tpriv), GFP_KERNEL);
637091810dbSJose Abreu 	if (!tpriv)
638091810dbSJose Abreu 		return -ENOMEM;
639091810dbSJose Abreu 
640091810dbSJose Abreu 	tpriv->ok = false;
641091810dbSJose Abreu 	init_completion(&tpriv->comp);
642091810dbSJose Abreu 	tpriv->pt.type = htons(ETH_P_PAUSE);
643091810dbSJose Abreu 	tpriv->pt.func = stmmac_test_flowctrl_validate;
644091810dbSJose Abreu 	tpriv->pt.dev = priv->dev;
645091810dbSJose Abreu 	tpriv->pt.af_packet_priv = tpriv;
646091810dbSJose Abreu 	dev_add_pack(&tpriv->pt);
647091810dbSJose Abreu 
648091810dbSJose Abreu 	/* Compute minimum number of packets to make FIFO full */
649091810dbSJose Abreu 	pkt_count = priv->plat->rx_fifo_size;
650091810dbSJose Abreu 	if (!pkt_count)
651091810dbSJose Abreu 		pkt_count = priv->dma_cap.rx_fifo_size;
652091810dbSJose Abreu 	pkt_count /= 1400;
653091810dbSJose Abreu 	pkt_count *= 2;
654091810dbSJose Abreu 
655091810dbSJose Abreu 	for (i = 0; i < rx_cnt; i++)
656091810dbSJose Abreu 		stmmac_stop_rx(priv, priv->ioaddr, i);
657091810dbSJose Abreu 
658091810dbSJose Abreu 	ret = dev_set_promiscuity(priv->dev, 1);
659091810dbSJose Abreu 	if (ret)
660091810dbSJose Abreu 		goto cleanup;
661091810dbSJose Abreu 
662091810dbSJose Abreu 	ret = dev_mc_add(priv->dev, paddr);
663091810dbSJose Abreu 	if (ret)
664091810dbSJose Abreu 		goto cleanup;
665091810dbSJose Abreu 
666091810dbSJose Abreu 	for (i = 0; i < pkt_count; i++) {
667091810dbSJose Abreu 		struct stmmac_packet_attrs attr = { };
668091810dbSJose Abreu 
669091810dbSJose Abreu 		attr.dst = priv->dev->dev_addr;
670091810dbSJose Abreu 		attr.dont_wait = true;
671091810dbSJose Abreu 		attr.size = 1400;
672091810dbSJose Abreu 
673091810dbSJose Abreu 		ret = __stmmac_test_loopback(priv, &attr);
674091810dbSJose Abreu 		if (ret)
675091810dbSJose Abreu 			goto cleanup;
676091810dbSJose Abreu 		if (tpriv->ok)
677091810dbSJose Abreu 			break;
678091810dbSJose Abreu 	}
679091810dbSJose Abreu 
680091810dbSJose Abreu 	/* Wait for some time in case RX Watchdog is enabled */
681091810dbSJose Abreu 	msleep(200);
682091810dbSJose Abreu 
683091810dbSJose Abreu 	for (i = 0; i < rx_cnt; i++) {
684091810dbSJose Abreu 		struct stmmac_channel *ch = &priv->channel[i];
685091810dbSJose Abreu 
686091810dbSJose Abreu 		stmmac_start_rx(priv, priv->ioaddr, i);
687091810dbSJose Abreu 		local_bh_disable();
688091810dbSJose Abreu 		napi_reschedule(&ch->rx_napi);
689091810dbSJose Abreu 		local_bh_enable();
690091810dbSJose Abreu 	}
691091810dbSJose Abreu 
692091810dbSJose Abreu 	wait_for_completion_timeout(&tpriv->comp, STMMAC_LB_TIMEOUT);
693091810dbSJose Abreu 	ret = !tpriv->ok;
694091810dbSJose Abreu 
695091810dbSJose Abreu cleanup:
696091810dbSJose Abreu 	dev_mc_del(priv->dev, paddr);
697091810dbSJose Abreu 	dev_set_promiscuity(priv->dev, -1);
698091810dbSJose Abreu 	dev_remove_pack(&tpriv->pt);
699091810dbSJose Abreu 	kfree(tpriv);
700091810dbSJose Abreu 	return ret;
701091810dbSJose Abreu }
702091810dbSJose Abreu 
703091810dbSJose Abreu #define STMMAC_LOOPBACK_NONE	0
704091810dbSJose Abreu #define STMMAC_LOOPBACK_MAC	1
705091810dbSJose Abreu #define STMMAC_LOOPBACK_PHY	2
706091810dbSJose Abreu 
707091810dbSJose Abreu static const struct stmmac_test {
708091810dbSJose Abreu 	char name[ETH_GSTRING_LEN];
709091810dbSJose Abreu 	int lb;
710091810dbSJose Abreu 	int (*fn)(struct stmmac_priv *priv);
711091810dbSJose Abreu } stmmac_selftests[] = {
712091810dbSJose Abreu 	{
713091810dbSJose Abreu 		.name = "MAC Loopback         ",
714091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_MAC,
715091810dbSJose Abreu 		.fn = stmmac_test_mac_loopback,
716091810dbSJose Abreu 	}, {
717091810dbSJose Abreu 		.name = "PHY Loopback         ",
718091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_NONE, /* Test will handle it */
719091810dbSJose Abreu 		.fn = stmmac_test_phy_loopback,
720091810dbSJose Abreu 	}, {
721091810dbSJose Abreu 		.name = "MMC Counters         ",
722091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
723091810dbSJose Abreu 		.fn = stmmac_test_mmc,
724091810dbSJose Abreu 	}, {
725091810dbSJose Abreu 		.name = "EEE                  ",
726091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
727091810dbSJose Abreu 		.fn = stmmac_test_eee,
728091810dbSJose Abreu 	}, {
729091810dbSJose Abreu 		.name = "Hash Filter MC       ",
730091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
731091810dbSJose Abreu 		.fn = stmmac_test_hfilt,
732091810dbSJose Abreu 	}, {
733091810dbSJose Abreu 		.name = "Perfect Filter UC    ",
734091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
735091810dbSJose Abreu 		.fn = stmmac_test_pfilt,
736091810dbSJose Abreu 	}, {
737091810dbSJose Abreu 		.name = "MC Filter            ",
738091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
739091810dbSJose Abreu 		.fn = stmmac_test_mcfilt,
740091810dbSJose Abreu 	}, {
741091810dbSJose Abreu 		.name = "UC Filter            ",
742091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
743091810dbSJose Abreu 		.fn = stmmac_test_ucfilt,
744091810dbSJose Abreu 	}, {
745091810dbSJose Abreu 		.name = "Flow Control         ",
746091810dbSJose Abreu 		.lb = STMMAC_LOOPBACK_PHY,
747091810dbSJose Abreu 		.fn = stmmac_test_flowctrl,
748091810dbSJose Abreu 	},
749091810dbSJose Abreu };
750091810dbSJose Abreu 
751091810dbSJose Abreu void stmmac_selftest_run(struct net_device *dev,
752091810dbSJose Abreu 			 struct ethtool_test *etest, u64 *buf)
753091810dbSJose Abreu {
754091810dbSJose Abreu 	struct stmmac_priv *priv = netdev_priv(dev);
755091810dbSJose Abreu 	int count = stmmac_selftest_get_count(priv);
756091810dbSJose Abreu 	int carrier = netif_carrier_ok(dev);
757091810dbSJose Abreu 	int i, ret;
758091810dbSJose Abreu 
759091810dbSJose Abreu 	memset(buf, 0, sizeof(*buf) * count);
760091810dbSJose Abreu 	stmmac_test_next_id = 0;
761091810dbSJose Abreu 
762091810dbSJose Abreu 	if (etest->flags != ETH_TEST_FL_OFFLINE) {
763091810dbSJose Abreu 		netdev_err(priv->dev, "Only offline tests are supported\n");
764091810dbSJose Abreu 		etest->flags |= ETH_TEST_FL_FAILED;
765091810dbSJose Abreu 		return;
766091810dbSJose Abreu 	} else if (!carrier) {
767091810dbSJose Abreu 		netdev_err(priv->dev, "You need valid Link to execute tests\n");
768091810dbSJose Abreu 		etest->flags |= ETH_TEST_FL_FAILED;
769091810dbSJose Abreu 		return;
770091810dbSJose Abreu 	}
771091810dbSJose Abreu 
772091810dbSJose Abreu 	/* We don't want extra traffic */
773091810dbSJose Abreu 	netif_carrier_off(dev);
774091810dbSJose Abreu 
775091810dbSJose Abreu 	/* Wait for queues drain */
776091810dbSJose Abreu 	msleep(200);
777091810dbSJose Abreu 
778091810dbSJose Abreu 	for (i = 0; i < count; i++) {
779091810dbSJose Abreu 		ret = 0;
780091810dbSJose Abreu 
781091810dbSJose Abreu 		switch (stmmac_selftests[i].lb) {
782091810dbSJose Abreu 		case STMMAC_LOOPBACK_PHY:
783091810dbSJose Abreu 			ret = -EOPNOTSUPP;
784091810dbSJose Abreu 			if (dev->phydev)
785091810dbSJose Abreu 				ret = phy_loopback(dev->phydev, true);
786091810dbSJose Abreu 			if (!ret)
787091810dbSJose Abreu 				break;
788091810dbSJose Abreu 			/* Fallthrough */
789091810dbSJose Abreu 		case STMMAC_LOOPBACK_MAC:
790091810dbSJose Abreu 			ret = stmmac_set_mac_loopback(priv, priv->ioaddr, true);
791091810dbSJose Abreu 			break;
792091810dbSJose Abreu 		case STMMAC_LOOPBACK_NONE:
793091810dbSJose Abreu 			break;
794091810dbSJose Abreu 		default:
795091810dbSJose Abreu 			ret = -EOPNOTSUPP;
796091810dbSJose Abreu 			break;
797091810dbSJose Abreu 		}
798091810dbSJose Abreu 
799091810dbSJose Abreu 		/*
800091810dbSJose Abreu 		 * First tests will always be MAC / PHY loobpack. If any of
801091810dbSJose Abreu 		 * them is not supported we abort earlier.
802091810dbSJose Abreu 		 */
803091810dbSJose Abreu 		if (ret) {
804091810dbSJose Abreu 			netdev_err(priv->dev, "Loopback is not supported\n");
805091810dbSJose Abreu 			etest->flags |= ETH_TEST_FL_FAILED;
806091810dbSJose Abreu 			break;
807091810dbSJose Abreu 		}
808091810dbSJose Abreu 
809091810dbSJose Abreu 		ret = stmmac_selftests[i].fn(priv);
810091810dbSJose Abreu 		if (ret && (ret != -EOPNOTSUPP))
811091810dbSJose Abreu 			etest->flags |= ETH_TEST_FL_FAILED;
812091810dbSJose Abreu 		buf[i] = ret;
813091810dbSJose Abreu 
814091810dbSJose Abreu 		switch (stmmac_selftests[i].lb) {
815091810dbSJose Abreu 		case STMMAC_LOOPBACK_PHY:
816091810dbSJose Abreu 			ret = -EOPNOTSUPP;
817091810dbSJose Abreu 			if (dev->phydev)
818091810dbSJose Abreu 				ret = phy_loopback(dev->phydev, false);
819091810dbSJose Abreu 			if (!ret)
820091810dbSJose Abreu 				break;
821091810dbSJose Abreu 			/* Fallthrough */
822091810dbSJose Abreu 		case STMMAC_LOOPBACK_MAC:
823091810dbSJose Abreu 			stmmac_set_mac_loopback(priv, priv->ioaddr, false);
824091810dbSJose Abreu 			break;
825091810dbSJose Abreu 		default:
826091810dbSJose Abreu 			break;
827091810dbSJose Abreu 		}
828091810dbSJose Abreu 	}
829091810dbSJose Abreu 
830091810dbSJose Abreu 	/* Restart everything */
831091810dbSJose Abreu 	if (carrier)
832091810dbSJose Abreu 		netif_carrier_on(dev);
833091810dbSJose Abreu }
834091810dbSJose Abreu 
835091810dbSJose Abreu void stmmac_selftest_get_strings(struct stmmac_priv *priv, u8 *data)
836091810dbSJose Abreu {
837091810dbSJose Abreu 	u8 *p = data;
838091810dbSJose Abreu 	int i;
839091810dbSJose Abreu 
840091810dbSJose Abreu 	for (i = 0; i < stmmac_selftest_get_count(priv); i++) {
841091810dbSJose Abreu 		snprintf(p, ETH_GSTRING_LEN, "%2d. %s", i + 1,
842091810dbSJose Abreu 			 stmmac_selftests[i].name);
843091810dbSJose Abreu 		p += ETH_GSTRING_LEN;
844091810dbSJose Abreu 	}
845091810dbSJose Abreu }
846091810dbSJose Abreu 
847091810dbSJose Abreu int stmmac_selftest_get_count(struct stmmac_priv *priv)
848091810dbSJose Abreu {
849091810dbSJose Abreu 	return ARRAY_SIZE(stmmac_selftests);
850091810dbSJose Abreu }
851