1219820Sjeff/*
2272407Shselasky * Copyright (c) 2007, 2014 Mellanox Technologies. All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff *
32219820Sjeff */
33219820Sjeff
34219820Sjeff#include <linux/kernel.h>
35219820Sjeff#include <linux/ethtool.h>
36219820Sjeff#include <linux/netdevice.h>
37219820Sjeff#include <linux/delay.h>
38219820Sjeff#include <linux/mlx4/driver.h>
39219820Sjeff
40272407Shselasky#include "mlx4_en.h"
41219820Sjeff
42272407Shselasky
43219820Sjeffstatic int mlx4_en_test_registers(struct mlx4_en_priv *priv)
44219820Sjeff{
45219820Sjeff	return mlx4_cmd(priv->mdev->dev, 0, 0, 0, MLX4_CMD_HW_HEALTH_CHECK,
46272407Shselasky			MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
47219820Sjeff}
48219820Sjeff
49219820Sjeffstatic int mlx4_en_test_loopback_xmit(struct mlx4_en_priv *priv)
50219820Sjeff{
51272407Shselasky	struct sk_buff *skb;
52219820Sjeff	struct ethhdr *ethh;
53219820Sjeff	unsigned char *packet;
54219820Sjeff	unsigned int packet_size = MLX4_LOOPBACK_TEST_PAYLOAD;
55219820Sjeff	unsigned int i;
56219820Sjeff	int err;
57219820Sjeff
58219820Sjeff
59219820Sjeff	/* build the pkt before xmit */
60272407Shselasky	skb = netdev_alloc_skb(priv->dev, MLX4_LOOPBACK_TEST_PAYLOAD + ETH_HLEN + NET_IP_ALIGN);
61272407Shselasky	if (!skb) {
62272407Shselasky		en_err(priv, "-LOOPBACK_TEST_XMIT- failed to create skb for xmit\n");
63219820Sjeff		return -ENOMEM;
64219820Sjeff	}
65272407Shselasky	skb_reserve(skb, NET_IP_ALIGN);
66219820Sjeff
67272407Shselasky	ethh = (struct ethhdr *)skb_put(skb, sizeof(struct ethhdr));
68272407Shselasky	packet	= (unsigned char *)skb_put(skb, packet_size);
69219820Sjeff	memcpy(ethh->h_dest, priv->dev->dev_addr, ETH_ALEN);
70219820Sjeff	memset(ethh->h_source, 0, ETH_ALEN);
71219820Sjeff	ethh->h_proto = htons(ETH_P_ARP);
72272407Shselasky	skb_set_mac_header(skb, 0);
73219820Sjeff	for (i = 0; i < packet_size; ++i)	/* fill our packet */
74219820Sjeff		packet[i] = (unsigned char)(i & 0xff);
75219820Sjeff
76219820Sjeff	/* xmit the pkt */
77272407Shselasky	err = mlx4_en_xmit(skb, priv->dev);
78219820Sjeff	return err;
79219820Sjeff}
80219820Sjeff
81219820Sjeffstatic int mlx4_en_test_loopback(struct mlx4_en_priv *priv)
82219820Sjeff{
83219820Sjeff	u32 loopback_ok = 0;
84219820Sjeff	int i;
85219820Sjeff
86219820Sjeff
87219820Sjeff        priv->loopback_ok = 0;
88219820Sjeff	priv->validate_loopback = 1;
89219820Sjeff
90272407Shselasky	mlx4_en_update_loopback_state(priv->dev, priv->dev->features);
91272407Shselasky
92219820Sjeff	/* xmit */
93219820Sjeff	if (mlx4_en_test_loopback_xmit(priv)) {
94219820Sjeff		en_err(priv, "Transmitting loopback packet failed\n");
95219820Sjeff		goto mlx4_en_test_loopback_exit;
96219820Sjeff	}
97219820Sjeff
98219820Sjeff	/* polling for result */
99219820Sjeff	for (i = 0; i < MLX4_EN_LOOPBACK_RETRIES; ++i) {
100219820Sjeff		msleep(MLX4_EN_LOOPBACK_TIMEOUT);
101219820Sjeff		if (priv->loopback_ok) {
102219820Sjeff			loopback_ok = 1;
103219820Sjeff			break;
104219820Sjeff		}
105219820Sjeff	}
106219820Sjeff	if (!loopback_ok)
107219820Sjeff		en_err(priv, "Loopback packet didn't arrive\n");
108219820Sjeff
109219820Sjeffmlx4_en_test_loopback_exit:
110219820Sjeff
111219820Sjeff	priv->validate_loopback = 0;
112272407Shselasky	mlx4_en_update_loopback_state(priv->dev, priv->dev->features);
113272407Shselasky	return !loopback_ok;
114219820Sjeff}
115219820Sjeff
116219820Sjeff
117219820Sjeffstatic int mlx4_en_test_link(struct mlx4_en_priv *priv)
118219820Sjeff{
119219820Sjeff	if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
120219820Sjeff		return -ENOMEM;
121219820Sjeff	if (priv->port_state.link_state == 1)
122219820Sjeff		return 0;
123219820Sjeff	else
124219820Sjeff		return 1;
125219820Sjeff}
126219820Sjeff
127219820Sjeffstatic int mlx4_en_test_speed(struct mlx4_en_priv *priv)
128219820Sjeff{
129219820Sjeff
130219820Sjeff	if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
131219820Sjeff		return -ENOMEM;
132219820Sjeff
133272407Shselasky	/* The device supports 1G, 10G and 40G speed */
134272407Shselasky	if (priv->port_state.link_speed != MLX4_EN_LINK_SPEED_1G &&
135272407Shselasky	    priv->port_state.link_speed != MLX4_EN_LINK_SPEED_10G &&
136272407Shselasky	    priv->port_state.link_speed != MLX4_EN_LINK_SPEED_40G)
137219820Sjeff		return priv->port_state.link_speed;
138219820Sjeff	return 0;
139219820Sjeff}
140219820Sjeff
141219820Sjeff
142219820Sjeffvoid mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf)
143219820Sjeff{
144219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
145219820Sjeff	struct mlx4_en_dev *mdev = priv->mdev;
146219820Sjeff	int i, carrier_ok;
147219820Sjeff
148219820Sjeff	memset(buf, 0, sizeof(u64) * MLX4_EN_NUM_SELF_TEST);
149219820Sjeff
150219820Sjeff	if (*flags & ETH_TEST_FL_OFFLINE) {
151219820Sjeff		/* disable the interface */
152219820Sjeff		carrier_ok = netif_carrier_ok(dev);
153219820Sjeff
154219820Sjeff		netif_carrier_off(dev);
155272407Shselasky		/* Wait until all tx queues are empty.
156219820Sjeff		 * there should not be any additional incoming traffic
157219820Sjeff		 * since we turned the carrier off */
158219820Sjeff		msleep(200);
159219820Sjeff
160272407Shselasky		if (priv->mdev->dev->caps.flags &
161272407Shselasky					MLX4_DEV_CAP_FLAG_UC_LOOPBACK) {
162219820Sjeff			buf[3] = mlx4_en_test_registers(priv);
163272407Shselasky			if (priv->port_up)
164272407Shselasky				buf[4] = mlx4_en_test_loopback(priv);
165219820Sjeff		}
166219820Sjeff
167219820Sjeff		if (carrier_ok)
168219820Sjeff			netif_carrier_on(dev);
169219820Sjeff
170219820Sjeff	}
171219820Sjeff	buf[0] = mlx4_test_interrupts(mdev->dev);
172219820Sjeff	buf[1] = mlx4_en_test_link(priv);
173219820Sjeff	buf[2] = mlx4_en_test_speed(priv);
174219820Sjeff
175219820Sjeff	for (i = 0; i < MLX4_EN_NUM_SELF_TEST; i++) {
176219820Sjeff		if (buf[i])
177219820Sjeff			*flags |= ETH_TEST_FL_FAILED;
178219820Sjeff	}
179219820Sjeff}
180