aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_ethtool.c
blob: 92b798f8e73ab3b3baa04135afa82f366a70580f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause

/* Ethtool support for Mellanox Gigabit Ethernet driver
 *
 * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES
 */

#include <linux/phy.h>

#include "mlxbf_gige.h"
#include "mlxbf_gige_regs.h"

/* Start of struct ethtool_ops functions */
static int mlxbf_gige_get_regs_len(struct net_device *netdev)
{
	return MLXBF_GIGE_MMIO_REG_SZ;
}

static void mlxbf_gige_get_regs(struct net_device *netdev,
				struct ethtool_regs *regs, void *p)
{
	struct mlxbf_gige *priv = netdev_priv(netdev);

	regs->version = MLXBF_GIGE_REGS_VERSION;

	/* Read entire MMIO register space and store results
	 * into the provided buffer. Each 64-bit word is converted
	 * to big-endian to make the output more readable.
	 *
	 * NOTE: by design, a read to an offset without an existing
	 *       register will be acknowledged and return zero.
	 */
	memcpy_fromio(p, priv->base, MLXBF_GIGE_MMIO_REG_SZ);
}

static void mlxbf_gige_get_ringparam(struct net_device *netdev,
				     struct ethtool_ringparam *ering)
{
	struct mlxbf_gige *priv = netdev_priv(netdev);

	ering->rx_max_pending = MLXBF_GIGE_MAX_RXQ_SZ;
	ering->tx_max_pending = MLXBF_GIGE_MAX_TXQ_SZ;
	ering->rx_pending = priv->rx_q_entries;
	ering->tx_pending = priv->tx_q_entries;
}

static const struct {
	const char string[ETH_GSTRING_LEN];
} mlxbf_gige_ethtool_stats_keys[] = {
	{ "hw_access_errors" },
	{ "tx_invalid_checksums" },
	{ "tx_small_frames" },
	{ "tx_index_errors" },
	{ "sw_config_errors" },
	{ "sw_access_errors" },
	{ "rx_truncate_errors" },
	{ "rx_mac_errors" },
	{ "rx_din_dropped_pkts" },
	{ "tx_fifo_full" },
	{ "rx_filter_passed_pkts" },
	{ "rx_filter_discard_pkts" },
};

static int mlxbf_gige_get_sset_count(struct net_device *netdev, int stringset)
{
	if (stringset != ETH_SS_STATS)
		return -EOPNOTSUPP;
	return ARRAY_SIZE(mlxbf_gige_ethtool_stats_keys);
}

static void mlxbf_gige_get_strings(struct net_device *netdev, u32 stringset,
				   u8 *buf)
{
	if (stringset != ETH_SS_STATS)
		return;
	memcpy(buf, &mlxbf_gige_ethtool_stats_keys,
	       sizeof(mlxbf_gige_ethtool_stats_keys));
}

static void mlxbf_gige_get_ethtool_stats(struct net_device *netdev,
					 struct ethtool_stats *estats,
					 u64 *data)
{
	struct mlxbf_gige *priv = netdev_priv(netdev);

	/* Fill data array with interface statistics
	 *
	 * NOTE: the data writes must be in
	 *       sync with the strings shown in
	 *       the mlxbf_gige_ethtool_stats_keys[] array
	 *
	 * NOTE2: certain statistics below are zeroed upon
	 *        port disable, so the calculation below
	 *        must include the "cached" value of the stat
	 *        plus the value read directly from hardware.
	 *        Cached statistics are currently:
	 *          rx_din_dropped_pkts
	 *          rx_filter_passed_pkts
	 *          rx_filter_discard_pkts
	 */
	*data++ = priv->stats.hw_access_errors;
	*data++ = priv->stats.tx_invalid_checksums;
	*data++ = priv->stats.tx_small_frames;
	*data++ = priv->stats.tx_index_errors;
	*data++ = priv->stats.sw_config_errors;
	*data++ = priv->stats.sw_access_errors;
	*data++ = priv->stats.rx_truncate_errors;
	*data++ = priv->stats.rx_mac_errors;
	*data++ = (priv->stats.rx_din_dropped_pkts +
		   readq(priv->base + MLXBF_GIGE_RX_DIN_DROP_COUNTER));
	*data++ = priv->stats.tx_fifo_full;
	*data++ = (priv->stats.rx_filter_passed_pkts +
		   readq(priv->base + MLXBF_GIGE_RX_PASS_COUNTER_ALL));
	*data++ = (priv->stats.rx_filter_discard_pkts +
		   readq(priv->base + MLXBF_GIGE_RX_DISC_COUNTER_ALL));
}

static void mlxbf_gige_get_pauseparam(struct net_device *netdev,
				      struct ethtool_pauseparam *pause)
{
	pause->autoneg = AUTONEG_DISABLE;
	pause->rx_pause = 1;
	pause->tx_pause = 1;
}

const struct ethtool_ops mlxbf_gige_ethtool_ops = {
	.get_link		= ethtool_op_get_link,
	.get_ringparam		= mlxbf_gige_get_ringparam,
	.get_regs_len           = mlxbf_gige_get_regs_len,
	.get_regs               = mlxbf_gige_get_regs,
	.get_strings            = mlxbf_gige_get_strings,
	.get_sset_count         = mlxbf_gige_get_sset_count,
	.get_ethtool_stats      = mlxbf_gige_get_ethtool_stats,
	.nway_reset		= phy_ethtool_nway_reset,
	.get_pauseparam		= mlxbf_gige_get_pauseparam,
	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
};