1ab69bde6SJohannes Berg /*
2ab69bde6SJohannes Berg  * Copyright (c) 2013 Johannes Berg <johannes@sipsolutions.net>
3ab69bde6SJohannes Berg  *
4ab69bde6SJohannes Berg  *  This file is free software: you may copy, redistribute and/or modify it
5ab69bde6SJohannes Berg  *  under the terms of the GNU General Public License as published by the
6ab69bde6SJohannes Berg  *  Free Software Foundation, either version 2 of the License, or (at your
7ab69bde6SJohannes Berg  *  option) any later version.
8ab69bde6SJohannes Berg  *
9ab69bde6SJohannes Berg  *  This file is distributed in the hope that it will be useful, but
10ab69bde6SJohannes Berg  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11ab69bde6SJohannes Berg  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12ab69bde6SJohannes Berg  *  General Public License for more details.
13ab69bde6SJohannes Berg  *
14ab69bde6SJohannes Berg  *  You should have received a copy of the GNU General Public License
15ab69bde6SJohannes Berg  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16ab69bde6SJohannes Berg  *
17ab69bde6SJohannes Berg  * This file incorporates work covered by the following copyright and
18ab69bde6SJohannes Berg  * permission notice:
19ab69bde6SJohannes Berg  *
20ab69bde6SJohannes Berg  * Copyright (c) 2012 Qualcomm Atheros, Inc.
21ab69bde6SJohannes Berg  *
22ab69bde6SJohannes Berg  * Permission to use, copy, modify, and/or distribute this software for any
23ab69bde6SJohannes Berg  * purpose with or without fee is hereby granted, provided that the above
24ab69bde6SJohannes Berg  * copyright notice and this permission notice appear in all copies.
25ab69bde6SJohannes Berg  *
26ab69bde6SJohannes Berg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
27ab69bde6SJohannes Berg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
28ab69bde6SJohannes Berg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
29ab69bde6SJohannes Berg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
30ab69bde6SJohannes Berg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
31ab69bde6SJohannes Berg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32ab69bde6SJohannes Berg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33ab69bde6SJohannes Berg  */
34ab69bde6SJohannes Berg 
35ab69bde6SJohannes Berg #include <linux/pci.h>
36ab69bde6SJohannes Berg #include <linux/ip.h>
37ab69bde6SJohannes Berg #include <linux/tcp.h>
38ab69bde6SJohannes Berg #include <linux/netdevice.h>
39ab69bde6SJohannes Berg #include <linux/etherdevice.h>
40ab69bde6SJohannes Berg #include <linux/ethtool.h>
41ab69bde6SJohannes Berg #include <linux/mdio.h>
42ab69bde6SJohannes Berg #include <linux/interrupt.h>
43ab69bde6SJohannes Berg #include <asm/byteorder.h>
44ab69bde6SJohannes Berg 
45ab69bde6SJohannes Berg #include "alx.h"
46ab69bde6SJohannes Berg #include "reg.h"
47ab69bde6SJohannes Berg #include "hw.h"
48ab69bde6SJohannes Berg 
49b7e6ce18SSabrina Dubroca /* The order of these strings must match the order of the fields in
50b7e6ce18SSabrina Dubroca  * struct alx_hw_stats
51b7e6ce18SSabrina Dubroca  * See hw.h
52b7e6ce18SSabrina Dubroca  */
53b7e6ce18SSabrina Dubroca static const char alx_gstrings_stats[][ETH_GSTRING_LEN] = {
54b7e6ce18SSabrina Dubroca 	"rx_packets",
55b7e6ce18SSabrina Dubroca 	"rx_bcast_packets",
56b7e6ce18SSabrina Dubroca 	"rx_mcast_packets",
57b7e6ce18SSabrina Dubroca 	"rx_pause_packets",
58b7e6ce18SSabrina Dubroca 	"rx_ctrl_packets",
59b7e6ce18SSabrina Dubroca 	"rx_fcs_errors",
60b7e6ce18SSabrina Dubroca 	"rx_length_errors",
61b7e6ce18SSabrina Dubroca 	"rx_bytes",
62b7e6ce18SSabrina Dubroca 	"rx_runt_packets",
63b7e6ce18SSabrina Dubroca 	"rx_fragments",
64b7e6ce18SSabrina Dubroca 	"rx_64B_or_less_packets",
65b7e6ce18SSabrina Dubroca 	"rx_65B_to_127B_packets",
66b7e6ce18SSabrina Dubroca 	"rx_128B_to_255B_packets",
67b7e6ce18SSabrina Dubroca 	"rx_256B_to_511B_packets",
68b7e6ce18SSabrina Dubroca 	"rx_512B_to_1023B_packets",
69b7e6ce18SSabrina Dubroca 	"rx_1024B_to_1518B_packets",
70b7e6ce18SSabrina Dubroca 	"rx_1519B_to_mtu_packets",
71b7e6ce18SSabrina Dubroca 	"rx_oversize_packets",
72b7e6ce18SSabrina Dubroca 	"rx_rxf_ov_drop_packets",
73b7e6ce18SSabrina Dubroca 	"rx_rrd_ov_drop_packets",
74b7e6ce18SSabrina Dubroca 	"rx_align_errors",
75b7e6ce18SSabrina Dubroca 	"rx_bcast_bytes",
76b7e6ce18SSabrina Dubroca 	"rx_mcast_bytes",
77b7e6ce18SSabrina Dubroca 	"rx_address_errors",
78b7e6ce18SSabrina Dubroca 	"tx_packets",
79b7e6ce18SSabrina Dubroca 	"tx_bcast_packets",
80b7e6ce18SSabrina Dubroca 	"tx_mcast_packets",
81b7e6ce18SSabrina Dubroca 	"tx_pause_packets",
82b7e6ce18SSabrina Dubroca 	"tx_exc_defer_packets",
83b7e6ce18SSabrina Dubroca 	"tx_ctrl_packets",
84b7e6ce18SSabrina Dubroca 	"tx_defer_packets",
85b7e6ce18SSabrina Dubroca 	"tx_bytes",
86b7e6ce18SSabrina Dubroca 	"tx_64B_or_less_packets",
87b7e6ce18SSabrina Dubroca 	"tx_65B_to_127B_packets",
88b7e6ce18SSabrina Dubroca 	"tx_128B_to_255B_packets",
89b7e6ce18SSabrina Dubroca 	"tx_256B_to_511B_packets",
90b7e6ce18SSabrina Dubroca 	"tx_512B_to_1023B_packets",
91b7e6ce18SSabrina Dubroca 	"tx_1024B_to_1518B_packets",
92b7e6ce18SSabrina Dubroca 	"tx_1519B_to_mtu_packets",
93b7e6ce18SSabrina Dubroca 	"tx_single_collision",
94b7e6ce18SSabrina Dubroca 	"tx_multiple_collisions",
95b7e6ce18SSabrina Dubroca 	"tx_late_collision",
96b7e6ce18SSabrina Dubroca 	"tx_abort_collision",
97b7e6ce18SSabrina Dubroca 	"tx_underrun",
98b7e6ce18SSabrina Dubroca 	"tx_trd_eop",
99b7e6ce18SSabrina Dubroca 	"tx_length_errors",
100b7e6ce18SSabrina Dubroca 	"tx_trunc_packets",
101b7e6ce18SSabrina Dubroca 	"tx_bcast_bytes",
102b7e6ce18SSabrina Dubroca 	"tx_mcast_bytes",
103b7e6ce18SSabrina Dubroca 	"tx_update",
104b7e6ce18SSabrina Dubroca };
105b7e6ce18SSabrina Dubroca 
106b7e6ce18SSabrina Dubroca #define ALX_NUM_STATS ARRAY_SIZE(alx_gstrings_stats)
107b7e6ce18SSabrina Dubroca 
108b7e6ce18SSabrina Dubroca 
alx_get_supported_speeds(struct alx_hw * hw)1097ec56894SJohannes Berg static u32 alx_get_supported_speeds(struct alx_hw *hw)
1107ec56894SJohannes Berg {
1117ec56894SJohannes Berg 	u32 supported = SUPPORTED_10baseT_Half |
1127ec56894SJohannes Berg 			SUPPORTED_10baseT_Full |
1137ec56894SJohannes Berg 			SUPPORTED_100baseT_Half |
1147ec56894SJohannes Berg 			SUPPORTED_100baseT_Full;
1157ec56894SJohannes Berg 
1167ec56894SJohannes Berg 	if (alx_hw_giga(hw))
1177ec56894SJohannes Berg 		supported |= SUPPORTED_1000baseT_Full;
1187ec56894SJohannes Berg 
1197ec56894SJohannes Berg 	BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half);
1207ec56894SJohannes Berg 	BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full);
1217ec56894SJohannes Berg 	BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half);
1227ec56894SJohannes Berg 	BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full);
1237ec56894SJohannes Berg 	BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full);
1247ec56894SJohannes Berg 
1257ec56894SJohannes Berg 	return supported;
1267ec56894SJohannes Berg }
127ab69bde6SJohannes Berg 
alx_get_link_ksettings(struct net_device * netdev,struct ethtool_link_ksettings * cmd)12836a4e690SPhilippe Reynes static int alx_get_link_ksettings(struct net_device *netdev,
12936a4e690SPhilippe Reynes 				  struct ethtool_link_ksettings *cmd)
130ab69bde6SJohannes Berg {
131ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
132ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
13336a4e690SPhilippe Reynes 	u32 supported, advertising;
134ab69bde6SJohannes Berg 
13536a4e690SPhilippe Reynes 	supported = SUPPORTED_Autoneg |
136ab69bde6SJohannes Berg 			  SUPPORTED_TP |
1377ec56894SJohannes Berg 			  SUPPORTED_Pause |
1387ec56894SJohannes Berg 			  SUPPORTED_Asym_Pause;
139ab69bde6SJohannes Berg 	if (alx_hw_giga(hw))
14036a4e690SPhilippe Reynes 		supported |= SUPPORTED_1000baseT_Full;
14136a4e690SPhilippe Reynes 	supported |= alx_get_supported_speeds(hw);
142ab69bde6SJohannes Berg 
14336a4e690SPhilippe Reynes 	advertising = ADVERTISED_TP;
144ab69bde6SJohannes Berg 	if (hw->adv_cfg & ADVERTISED_Autoneg)
14536a4e690SPhilippe Reynes 		advertising |= hw->adv_cfg;
146ab69bde6SJohannes Berg 
14736a4e690SPhilippe Reynes 	cmd->base.port = PORT_TP;
14836a4e690SPhilippe Reynes 	cmd->base.phy_address = 0;
1497ec56894SJohannes Berg 
150ab69bde6SJohannes Berg 	if (hw->adv_cfg & ADVERTISED_Autoneg)
15136a4e690SPhilippe Reynes 		cmd->base.autoneg = AUTONEG_ENABLE;
152ab69bde6SJohannes Berg 	else
15336a4e690SPhilippe Reynes 		cmd->base.autoneg = AUTONEG_DISABLE;
154ab69bde6SJohannes Berg 
155ab69bde6SJohannes Berg 	if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) {
156ab69bde6SJohannes Berg 		if (hw->flowctrl & ALX_FC_RX) {
15736a4e690SPhilippe Reynes 			advertising |= ADVERTISED_Pause;
158ab69bde6SJohannes Berg 
159ab69bde6SJohannes Berg 			if (!(hw->flowctrl & ALX_FC_TX))
16036a4e690SPhilippe Reynes 				advertising |= ADVERTISED_Asym_Pause;
161ab69bde6SJohannes Berg 		} else if (hw->flowctrl & ALX_FC_TX) {
16236a4e690SPhilippe Reynes 			advertising |= ADVERTISED_Asym_Pause;
163ab69bde6SJohannes Berg 		}
164ab69bde6SJohannes Berg 	}
165ab69bde6SJohannes Berg 
1664a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
16736a4e690SPhilippe Reynes 	cmd->base.speed = hw->link_speed;
16836a4e690SPhilippe Reynes 	cmd->base.duplex = hw->duplex;
1694a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
17036a4e690SPhilippe Reynes 
17136a4e690SPhilippe Reynes 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
17236a4e690SPhilippe Reynes 						supported);
17336a4e690SPhilippe Reynes 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
17436a4e690SPhilippe Reynes 						advertising);
175ab69bde6SJohannes Berg 
176ab69bde6SJohannes Berg 	return 0;
177ab69bde6SJohannes Berg }
178ab69bde6SJohannes Berg 
alx_set_link_ksettings(struct net_device * netdev,const struct ethtool_link_ksettings * cmd)17936a4e690SPhilippe Reynes static int alx_set_link_ksettings(struct net_device *netdev,
18036a4e690SPhilippe Reynes 				  const struct ethtool_link_ksettings *cmd)
181ab69bde6SJohannes Berg {
182ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
183ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
184ab69bde6SJohannes Berg 	u32 adv_cfg;
18536a4e690SPhilippe Reynes 	u32 advertising;
1864a5fe57eSJohannes Berg 	int ret;
187ab69bde6SJohannes Berg 
18836a4e690SPhilippe Reynes 	ethtool_convert_link_mode_to_legacy_u32(&advertising,
18936a4e690SPhilippe Reynes 						cmd->link_modes.advertising);
19036a4e690SPhilippe Reynes 
19136a4e690SPhilippe Reynes 	if (cmd->base.autoneg == AUTONEG_ENABLE) {
19236a4e690SPhilippe Reynes 		if (advertising & ~alx_get_supported_speeds(hw))
193ab69bde6SJohannes Berg 			return -EINVAL;
19436a4e690SPhilippe Reynes 		adv_cfg = advertising | ADVERTISED_Autoneg;
195ab69bde6SJohannes Berg 	} else {
19636a4e690SPhilippe Reynes 		adv_cfg = alx_speed_to_ethadv(cmd->base.speed,
19736a4e690SPhilippe Reynes 					      cmd->base.duplex);
198ab69bde6SJohannes Berg 
199a5b87cc9SJohannes Berg 		if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full)
200ab69bde6SJohannes Berg 			return -EINVAL;
201ab69bde6SJohannes Berg 	}
202ab69bde6SJohannes Berg 
203ab69bde6SJohannes Berg 	hw->adv_cfg = adv_cfg;
2044a5fe57eSJohannes Berg 
2054a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
2064a5fe57eSJohannes Berg 	ret = alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl);
2074a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
2084a5fe57eSJohannes Berg 
2094a5fe57eSJohannes Berg 	return ret;
210ab69bde6SJohannes Berg }
211ab69bde6SJohannes Berg 
alx_get_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)212ab69bde6SJohannes Berg static void alx_get_pauseparam(struct net_device *netdev,
213ab69bde6SJohannes Berg 			       struct ethtool_pauseparam *pause)
214ab69bde6SJohannes Berg {
215ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
216ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
217ab69bde6SJohannes Berg 
2184a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
2197ec56894SJohannes Berg 	pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG &&
2207ec56894SJohannes Berg 			    hw->adv_cfg & ADVERTISED_Autoneg);
2217ec56894SJohannes Berg 	pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX);
2227ec56894SJohannes Berg 	pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX);
2234a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
224ab69bde6SJohannes Berg }
225ab69bde6SJohannes Berg 
226ab69bde6SJohannes Berg 
alx_set_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)227ab69bde6SJohannes Berg static int alx_set_pauseparam(struct net_device *netdev,
228ab69bde6SJohannes Berg 			      struct ethtool_pauseparam *pause)
229ab69bde6SJohannes Berg {
230ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
231ab69bde6SJohannes Berg 	struct alx_hw *hw = &alx->hw;
232ab69bde6SJohannes Berg 	int err = 0;
233ab69bde6SJohannes Berg 	bool reconfig_phy = false;
234ab69bde6SJohannes Berg 	u8 fc = 0;
235ab69bde6SJohannes Berg 
236ab69bde6SJohannes Berg 	if (pause->tx_pause)
237ab69bde6SJohannes Berg 		fc |= ALX_FC_TX;
238ab69bde6SJohannes Berg 	if (pause->rx_pause)
239ab69bde6SJohannes Berg 		fc |= ALX_FC_RX;
240ab69bde6SJohannes Berg 	if (pause->autoneg)
241ab69bde6SJohannes Berg 		fc |= ALX_FC_ANEG;
242ab69bde6SJohannes Berg 
2434a5fe57eSJohannes Berg 	mutex_lock(&alx->mtx);
244ab69bde6SJohannes Berg 
245ab69bde6SJohannes Berg 	/* restart auto-neg for auto-mode */
246ab69bde6SJohannes Berg 	if (hw->adv_cfg & ADVERTISED_Autoneg) {
247ab69bde6SJohannes Berg 		if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG))
248ab69bde6SJohannes Berg 			reconfig_phy = true;
249ab69bde6SJohannes Berg 		if (fc & hw->flowctrl & ALX_FC_ANEG &&
250ab69bde6SJohannes Berg 		    (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX))
251ab69bde6SJohannes Berg 			reconfig_phy = true;
252ab69bde6SJohannes Berg 	}
253ab69bde6SJohannes Berg 
254ab69bde6SJohannes Berg 	if (reconfig_phy) {
255ab69bde6SJohannes Berg 		err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc);
2562d1c5f29SPu Lehui 		if (err) {
2572d1c5f29SPu Lehui 			mutex_unlock(&alx->mtx);
258ab69bde6SJohannes Berg 			return err;
259ab69bde6SJohannes Berg 		}
2602d1c5f29SPu Lehui 	}
261ab69bde6SJohannes Berg 
262ab69bde6SJohannes Berg 	/* flow control on mac */
263ab69bde6SJohannes Berg 	if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX))
264ab69bde6SJohannes Berg 		alx_cfg_mac_flowcontrol(hw, fc);
265ab69bde6SJohannes Berg 
266ab69bde6SJohannes Berg 	hw->flowctrl = fc;
2674a5fe57eSJohannes Berg 	mutex_unlock(&alx->mtx);
268ab69bde6SJohannes Berg 
269ab69bde6SJohannes Berg 	return 0;
270ab69bde6SJohannes Berg }
271ab69bde6SJohannes Berg 
alx_get_msglevel(struct net_device * netdev)272ab69bde6SJohannes Berg static u32 alx_get_msglevel(struct net_device *netdev)
273ab69bde6SJohannes Berg {
274ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
275ab69bde6SJohannes Berg 
276ab69bde6SJohannes Berg 	return alx->msg_enable;
277ab69bde6SJohannes Berg }
278ab69bde6SJohannes Berg 
alx_set_msglevel(struct net_device * netdev,u32 data)279ab69bde6SJohannes Berg static void alx_set_msglevel(struct net_device *netdev, u32 data)
280ab69bde6SJohannes Berg {
281ab69bde6SJohannes Berg 	struct alx_priv *alx = netdev_priv(netdev);
282ab69bde6SJohannes Berg 
283ab69bde6SJohannes Berg 	alx->msg_enable = data;
284ab69bde6SJohannes Berg }
285ab69bde6SJohannes Berg 
alx_get_ethtool_stats(struct net_device * netdev,struct ethtool_stats * estats,u64 * data)286b7e6ce18SSabrina Dubroca static void alx_get_ethtool_stats(struct net_device *netdev,
287b7e6ce18SSabrina Dubroca 				  struct ethtool_stats *estats, u64 *data)
288b7e6ce18SSabrina Dubroca {
289b7e6ce18SSabrina Dubroca 	struct alx_priv *alx = netdev_priv(netdev);
290b7e6ce18SSabrina Dubroca 	struct alx_hw *hw = &alx->hw;
291b7e6ce18SSabrina Dubroca 
292b7e6ce18SSabrina Dubroca 	spin_lock(&alx->stats_lock);
293b7e6ce18SSabrina Dubroca 
294b7e6ce18SSabrina Dubroca 	alx_update_hw_stats(hw);
295*3a198c95SGONG, Ruiqi 	BUILD_BUG_ON(sizeof(hw->stats) != ALX_NUM_STATS * sizeof(u64));
296*3a198c95SGONG, Ruiqi 	memcpy(data, &hw->stats, sizeof(hw->stats));
297b7e6ce18SSabrina Dubroca 
298b7e6ce18SSabrina Dubroca 	spin_unlock(&alx->stats_lock);
299b7e6ce18SSabrina Dubroca }
300b7e6ce18SSabrina Dubroca 
alx_get_strings(struct net_device * netdev,u32 stringset,u8 * buf)301b7e6ce18SSabrina Dubroca static void alx_get_strings(struct net_device *netdev, u32 stringset, u8 *buf)
302b7e6ce18SSabrina Dubroca {
303b7e6ce18SSabrina Dubroca 	switch (stringset) {
304b7e6ce18SSabrina Dubroca 	case ETH_SS_STATS:
305b7e6ce18SSabrina Dubroca 		memcpy(buf, &alx_gstrings_stats, sizeof(alx_gstrings_stats));
306b7e6ce18SSabrina Dubroca 		break;
307b7e6ce18SSabrina Dubroca 	default:
308b7e6ce18SSabrina Dubroca 		WARN_ON(1);
309b7e6ce18SSabrina Dubroca 		break;
310b7e6ce18SSabrina Dubroca 	}
311b7e6ce18SSabrina Dubroca }
312b7e6ce18SSabrina Dubroca 
alx_get_sset_count(struct net_device * netdev,int sset)313b7e6ce18SSabrina Dubroca static int alx_get_sset_count(struct net_device *netdev, int sset)
314b7e6ce18SSabrina Dubroca {
315b7e6ce18SSabrina Dubroca 	switch (sset) {
316b7e6ce18SSabrina Dubroca 	case ETH_SS_STATS:
317b7e6ce18SSabrina Dubroca 		return ALX_NUM_STATS;
318b7e6ce18SSabrina Dubroca 	default:
319b7e6ce18SSabrina Dubroca 		return -EINVAL;
320b7e6ce18SSabrina Dubroca 	}
321b7e6ce18SSabrina Dubroca }
322b7e6ce18SSabrina Dubroca 
323ab69bde6SJohannes Berg const struct ethtool_ops alx_ethtool_ops = {
324ab69bde6SJohannes Berg 	.get_pauseparam	= alx_get_pauseparam,
325ab69bde6SJohannes Berg 	.set_pauseparam	= alx_set_pauseparam,
326ab69bde6SJohannes Berg 	.get_msglevel	= alx_get_msglevel,
327ab69bde6SJohannes Berg 	.set_msglevel	= alx_set_msglevel,
328ab69bde6SJohannes Berg 	.get_link	= ethtool_op_get_link,
329b7e6ce18SSabrina Dubroca 	.get_strings	= alx_get_strings,
330b7e6ce18SSabrina Dubroca 	.get_sset_count	= alx_get_sset_count,
331b7e6ce18SSabrina Dubroca 	.get_ethtool_stats	= alx_get_ethtool_stats,
33236a4e690SPhilippe Reynes 	.get_link_ksettings	= alx_get_link_ksettings,
33336a4e690SPhilippe Reynes 	.set_link_ksettings	= alx_set_link_ksettings,
334ab69bde6SJohannes Berg };
335