154359Sroberto/*
2182007Sroberto * Copyright (c) 2007 Mellanox Technologies. All rights reserved.
354359Sroberto *
4182007Sroberto * This software is available to you under a choice of one of two
554359Sroberto * licenses.  You may choose to be licensed under the terms of the GNU
654359Sroberto * General Public License (GPL) Version 2, available from the file
754359Sroberto * COPYING in the main directory of this source tree, or the
854359Sroberto * OpenIB.org BSD license below:
954359Sroberto *
1054359Sroberto *     Redistribution and use in source and binary forms, with or
1154359Sroberto *     without modification, are permitted provided that the following
1254359Sroberto *     conditions are met:
1354359Sroberto *
1454359Sroberto *      - Redistributions of source code must retain the above
1554359Sroberto *        copyright notice, this list of conditions and the following
1682498Sroberto *        disclaimer.
1754359Sroberto *
1854359Sroberto *      - Redistributions in binary form must reproduce the above
1954359Sroberto *        copyright notice, this list of conditions and the following
2054359Sroberto *        disclaimer in the documentation and/or other materials
2154359Sroberto *        provided with the distribution.
2254359Sroberto *
2354359Sroberto * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2454359Sroberto * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2554359Sroberto * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2654359Sroberto * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2754359Sroberto * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2854359Sroberto * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2954359Sroberto * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3054359Sroberto * SOFTWARE.
3154359Sroberto *
3254359Sroberto */
33280849Scy
3454359Sroberto#include <linux/kernel.h>
3554359Sroberto#include <linux/ethtool.h>
3654359Sroberto#include <linux/netdevice.h>
3754359Sroberto#include <linux/delay.h>
3854359Sroberto#include <linux/mlx4/driver.h>
3954359Sroberto
4054359Sroberto#include "mlx4_en.h"
4154359Sroberto
4254359Sroberto
4354359Srobertostatic int mlx4_en_test_registers(struct mlx4_en_priv *priv)
4454359Sroberto{
4554359Sroberto	return mlx4_cmd(priv->mdev->dev, 0, 0, 0, MLX4_CMD_HW_HEALTH_CHECK,
4654359Sroberto			MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
4754359Sroberto}
4854359Sroberto
4954359Srobertostatic int mlx4_en_test_loopback_xmit(struct mlx4_en_priv *priv)
5054359Sroberto{
5154359Sroberto	struct sk_buff *skb;
5254359Sroberto	struct ethhdr *ethh;
5354359Sroberto	unsigned char *packet;
5454359Sroberto	unsigned int packet_size = MLX4_LOOPBACK_TEST_PAYLOAD;
5554359Sroberto	unsigned int i;
5654359Sroberto	int err;
5754359Sroberto
5854359Sroberto
5954359Sroberto	/* build the pkt before xmit */
6054359Sroberto	skb = netdev_alloc_skb(priv->dev, MLX4_LOOPBACK_TEST_PAYLOAD + ETH_HLEN + NET_IP_ALIGN);
6154359Sroberto	if (!skb)
6254359Sroberto		return -ENOMEM;
6354359Sroberto
6454359Sroberto	skb_reserve(skb, NET_IP_ALIGN);
6554359Sroberto
6654359Sroberto	ethh = skb_put(skb, sizeof(struct ethhdr));
6754359Sroberto	packet = skb_put(skb, packet_size);
6854359Sroberto	memcpy(ethh->h_dest, priv->dev->dev_addr, ETH_ALEN);
6954359Sroberto	eth_zero_addr(ethh->h_source);
7054359Sroberto	ethh->h_proto = htons(ETH_P_ARP);
7154359Sroberto	skb_reset_mac_header(skb);
7254359Sroberto	for (i = 0; i < packet_size; ++i)	/* fill our packet */
7354359Sroberto		packet[i] = (unsigned char)(i & 0xff);
7454359Sroberto
7554359Sroberto	/* xmit the pkt */
7654359Sroberto	err = mlx4_en_xmit(skb, priv->dev);
77282408Scy	return err;
78282408Scy}
79282408Scy
8054359Srobertostatic int mlx4_en_test_loopback(struct mlx4_en_priv *priv)
81282408Scy{
8254359Sroberto	u32 loopback_ok = 0;
8354359Sroberto	int i;
8454359Sroberto
8554359Sroberto        priv->loopback_ok = 0;
8654359Sroberto	priv->validate_loopback = 1;
8754359Sroberto
8854359Sroberto	mlx4_en_update_loopback_state(priv->dev, priv->dev->features);
8954359Sroberto
9054359Sroberto	/* xmit */
9154359Sroberto	if (mlx4_en_test_loopback_xmit(priv)) {
9254359Sroberto		en_err(priv, "Transmitting loopback packet failed\n");
9354359Sroberto		goto mlx4_en_test_loopback_exit;
9454359Sroberto	}
9554359Sroberto
9654359Sroberto	/* polling for result */
97280849Scy	for (i = 0; i < MLX4_EN_LOOPBACK_RETRIES; ++i) {
9882498Sroberto		msleep(MLX4_EN_LOOPBACK_TIMEOUT);
9954359Sroberto		if (priv->loopback_ok) {
10082498Sroberto			loopback_ok = 1;
10154359Sroberto			break;
10254359Sroberto		}
10354359Sroberto	}
10454359Sroberto	if (!loopback_ok)
10554359Sroberto		en_err(priv, "Loopback packet didn't arrive\n");
10654359Sroberto
10754359Srobertomlx4_en_test_loopback_exit:
10854359Sroberto
10954359Sroberto	priv->validate_loopback = 0;
11054359Sroberto
11154359Sroberto	mlx4_en_update_loopback_state(priv->dev, priv->dev->features);
11254359Sroberto	return !loopback_ok;
11354359Sroberto}
11454359Sroberto
11554359Srobertostatic int mlx4_en_test_interrupts(struct mlx4_en_priv *priv)
11654359Sroberto{
11754359Sroberto	struct mlx4_en_dev *mdev = priv->mdev;
11854359Sroberto	int err = 0;
11954359Sroberto	int i = 0;
12054359Sroberto
12154359Sroberto	err = mlx4_test_async(mdev->dev);
12254359Sroberto	/* When not in MSI_X or slave, test only async */
12354359Sroberto	if (!(mdev->dev->flags & MLX4_FLAG_MSI_X) || mlx4_is_slave(mdev->dev))
12454359Sroberto		return err;
12554359Sroberto
12654359Sroberto	/* A loop over all completion vectors of current port,
127282408Scy	 * for each vector check whether it works by mapping command
12854359Sroberto	 * completions to that vector and performing a NOP command
129282408Scy	 */
13054359Sroberto	for (i = 0; i < priv->rx_ring_num; i++) {
13154359Sroberto		err = mlx4_test_interrupt(mdev->dev, priv->rx_cq[i]->vector);
13254359Sroberto		if (err)
13354359Sroberto			break;
134282408Scy	}
13554359Sroberto
13654359Sroberto	return err;
13754359Sroberto}
13854359Sroberto
13954359Srobertostatic int mlx4_en_test_link(struct mlx4_en_priv *priv)
140293423Sdelphij{
14154359Sroberto	if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
14254359Sroberto		return -ENOMEM;
14354359Sroberto	if (priv->port_state.link_state == 1)
14454359Sroberto		return 0;
14554359Sroberto	else
14654359Sroberto		return 1;
14754359Sroberto}
14854359Sroberto
14954359Srobertostatic int mlx4_en_test_speed(struct mlx4_en_priv *priv)
15054359Sroberto{
15154359Sroberto
15254359Sroberto	if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
15354359Sroberto		return -ENOMEM;
15454359Sroberto
15554359Sroberto	/* The device supports 100M, 1G, 10G, 20G, 40G and 56G speed */
15654359Sroberto	if (priv->port_state.link_speed != SPEED_100 &&
15754359Sroberto	    priv->port_state.link_speed != SPEED_1000 &&
15854359Sroberto	    priv->port_state.link_speed != SPEED_10000 &&
15954359Sroberto	    priv->port_state.link_speed != SPEED_20000 &&
16054359Sroberto	    priv->port_state.link_speed != SPEED_40000 &&
16154359Sroberto	    priv->port_state.link_speed != SPEED_56000)
16254359Sroberto		return priv->port_state.link_speed;
16354359Sroberto
16454359Sroberto	return 0;
16554359Sroberto}
16654359Sroberto
16754359Sroberto
16854359Srobertovoid mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf)
16954359Sroberto{
17054359Sroberto	struct mlx4_en_priv *priv = netdev_priv(dev);
17154359Sroberto	int i, carrier_ok;
172282408Scy
17354359Sroberto	memset(buf, 0, sizeof(u64) * MLX4_EN_NUM_SELF_TEST);
17454359Sroberto
17554359Sroberto	if (*flags & ETH_TEST_FL_OFFLINE) {
17654359Sroberto		/* disable the interface */
17754359Sroberto		carrier_ok = netif_carrier_ok(dev);
17854359Sroberto
17954359Sroberto		netif_carrier_off(dev);
18054359Sroberto		/* Wait until all tx queues are empty.
18154359Sroberto		 * there should not be any additional incoming traffic
18254359Sroberto		 * since we turned the carrier off */
18354359Sroberto		msleep(200);
18454359Sroberto
185		if (priv->mdev->dev->caps.flags &
186					MLX4_DEV_CAP_FLAG_UC_LOOPBACK) {
187			buf[3] = mlx4_en_test_registers(priv);
188			if (priv->port_up && dev->mtu >= MLX4_SELFTEST_LB_MIN_MTU)
189				buf[4] = mlx4_en_test_loopback(priv);
190		}
191
192		if (carrier_ok)
193			netif_carrier_on(dev);
194
195	}
196	buf[0] = mlx4_en_test_interrupts(priv);
197	buf[1] = mlx4_en_test_link(priv);
198	buf[2] = mlx4_en_test_speed(priv);
199
200	for (i = 0; i < MLX4_EN_NUM_SELF_TEST; i++) {
201		if (buf[i])
202			*flags |= ETH_TEST_FL_FAILED;
203	}
204}
205