xref: /linux/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_ethtool.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
2 
3 /* Ethtool support for Mellanox Gigabit Ethernet driver
4  *
5  * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES
6  */
7 
8 #include <linux/phy.h>
9 
10 #include "mlxbf_gige.h"
11 #include "mlxbf_gige_regs.h"
12 
13 /* Start of struct ethtool_ops functions */
14 static int mlxbf_gige_get_regs_len(struct net_device *netdev)
15 {
16 	return MLXBF_GIGE_MMIO_REG_SZ;
17 }
18 
19 static void mlxbf_gige_get_regs(struct net_device *netdev,
20 				struct ethtool_regs *regs, void *p)
21 {
22 	struct mlxbf_gige *priv = netdev_priv(netdev);
23 
24 	regs->version = MLXBF_GIGE_REGS_VERSION;
25 
26 	/* Read entire MMIO register space and store results
27 	 * into the provided buffer. By design, a read to an
28 	 * offset without an existing register will be
29 	 * acknowledged and return zero.
30 	 */
31 	memcpy_fromio(p, priv->base, MLXBF_GIGE_MMIO_REG_SZ);
32 }
33 
34 static void
35 mlxbf_gige_get_ringparam(struct net_device *netdev,
36 			 struct ethtool_ringparam *ering,
37 			 struct kernel_ethtool_ringparam *kernel_ering,
38 			 struct netlink_ext_ack *extack)
39 {
40 	struct mlxbf_gige *priv = netdev_priv(netdev);
41 
42 	ering->rx_max_pending = MLXBF_GIGE_MAX_RXQ_SZ;
43 	ering->tx_max_pending = MLXBF_GIGE_MAX_TXQ_SZ;
44 	ering->rx_pending = priv->rx_q_entries;
45 	ering->tx_pending = priv->tx_q_entries;
46 }
47 
48 static const struct {
49 	const char string[ETH_GSTRING_LEN];
50 } mlxbf_gige_ethtool_stats_keys[] = {
51 	{ "hw_access_errors" },
52 	{ "tx_invalid_checksums" },
53 	{ "tx_small_frames" },
54 	{ "tx_index_errors" },
55 	{ "sw_config_errors" },
56 	{ "sw_access_errors" },
57 	{ "rx_truncate_errors" },
58 	{ "rx_mac_errors" },
59 	{ "rx_din_dropped_pkts" },
60 	{ "tx_fifo_full" },
61 	{ "rx_filter_passed_pkts" },
62 	{ "rx_filter_discard_pkts" },
63 };
64 
65 static int mlxbf_gige_get_sset_count(struct net_device *netdev, int stringset)
66 {
67 	if (stringset != ETH_SS_STATS)
68 		return -EOPNOTSUPP;
69 	return ARRAY_SIZE(mlxbf_gige_ethtool_stats_keys);
70 }
71 
72 static void mlxbf_gige_get_strings(struct net_device *netdev, u32 stringset,
73 				   u8 *buf)
74 {
75 	if (stringset != ETH_SS_STATS)
76 		return;
77 	memcpy(buf, &mlxbf_gige_ethtool_stats_keys,
78 	       sizeof(mlxbf_gige_ethtool_stats_keys));
79 }
80 
81 static void mlxbf_gige_get_ethtool_stats(struct net_device *netdev,
82 					 struct ethtool_stats *estats,
83 					 u64 *data)
84 {
85 	struct mlxbf_gige *priv = netdev_priv(netdev);
86 
87 	/* Fill data array with interface statistics
88 	 *
89 	 * NOTE: the data writes must be in
90 	 *       sync with the strings shown in
91 	 *       the mlxbf_gige_ethtool_stats_keys[] array
92 	 *
93 	 * NOTE2: certain statistics below are zeroed upon
94 	 *        port disable, so the calculation below
95 	 *        must include the "cached" value of the stat
96 	 *        plus the value read directly from hardware.
97 	 *        Cached statistics are currently:
98 	 *          rx_din_dropped_pkts
99 	 *          rx_filter_passed_pkts
100 	 *          rx_filter_discard_pkts
101 	 */
102 	*data++ = priv->stats.hw_access_errors;
103 	*data++ = priv->stats.tx_invalid_checksums;
104 	*data++ = priv->stats.tx_small_frames;
105 	*data++ = priv->stats.tx_index_errors;
106 	*data++ = priv->stats.sw_config_errors;
107 	*data++ = priv->stats.sw_access_errors;
108 	*data++ = priv->stats.rx_truncate_errors;
109 	*data++ = priv->stats.rx_mac_errors;
110 	*data++ = (priv->stats.rx_din_dropped_pkts +
111 		   readq(priv->base + MLXBF_GIGE_RX_DIN_DROP_COUNTER));
112 	*data++ = priv->stats.tx_fifo_full;
113 	*data++ = (priv->stats.rx_filter_passed_pkts +
114 		   readq(priv->base + MLXBF_GIGE_RX_PASS_COUNTER_ALL));
115 	*data++ = (priv->stats.rx_filter_discard_pkts +
116 		   readq(priv->base + MLXBF_GIGE_RX_DISC_COUNTER_ALL));
117 }
118 
119 static void mlxbf_gige_get_pauseparam(struct net_device *netdev,
120 				      struct ethtool_pauseparam *pause)
121 {
122 	pause->autoneg = AUTONEG_DISABLE;
123 	pause->rx_pause = 1;
124 	pause->tx_pause = 1;
125 }
126 
127 static bool mlxbf_gige_llu_counters_enabled(struct mlxbf_gige *priv)
128 {
129 	u32 data;
130 
131 	if (priv->hw_version == MLXBF_GIGE_VERSION_BF2) {
132 		data = readl(priv->llu_base + MLXBF_GIGE_BF2_LLU_GENERAL_CONFIG);
133 		if (data & MLXBF_GIGE_BF2_LLU_COUNTERS_EN)
134 			return true;
135 	} else {
136 		data = readl(priv->llu_base + MLXBF_GIGE_BF3_LLU_GENERAL_CONFIG);
137 		if (data & MLXBF_GIGE_BF3_LLU_COUNTERS_EN)
138 			return true;
139 	}
140 
141 	return false;
142 }
143 
144 static void mlxbf_gige_get_pause_stats(struct net_device *netdev,
145 				       struct ethtool_pause_stats *pause_stats)
146 {
147 	struct mlxbf_gige *priv = netdev_priv(netdev);
148 	u64 data_lo, data_hi;
149 
150 	/* Read LLU counters to provide stats only if counters are enabled */
151 	if (mlxbf_gige_llu_counters_enabled(priv)) {
152 		data_lo = readl(priv->llu_base + MLXBF_GIGE_TX_PAUSE_CNT_LO);
153 		data_hi = readl(priv->llu_base + MLXBF_GIGE_TX_PAUSE_CNT_HI);
154 		pause_stats->tx_pause_frames = (data_hi << 32) | data_lo;
155 
156 		data_lo = readl(priv->llu_base + MLXBF_GIGE_RX_PAUSE_CNT_LO);
157 		data_hi = readl(priv->llu_base + MLXBF_GIGE_RX_PAUSE_CNT_HI);
158 		pause_stats->rx_pause_frames = (data_hi << 32) | data_lo;
159 	}
160 }
161 
162 const struct ethtool_ops mlxbf_gige_ethtool_ops = {
163 	.get_link		= ethtool_op_get_link,
164 	.get_ringparam		= mlxbf_gige_get_ringparam,
165 	.get_regs_len           = mlxbf_gige_get_regs_len,
166 	.get_regs               = mlxbf_gige_get_regs,
167 	.get_strings            = mlxbf_gige_get_strings,
168 	.get_sset_count         = mlxbf_gige_get_sset_count,
169 	.get_ethtool_stats      = mlxbf_gige_get_ethtool_stats,
170 	.nway_reset		= phy_ethtool_nway_reset,
171 	.get_pauseparam		= mlxbf_gige_get_pauseparam,
172 	.get_pause_stats	= mlxbf_gige_get_pause_stats,
173 	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
174 	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
175 };
176