121c5ea95SDimitris Michailidis // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
221c5ea95SDimitris Michailidis 
321c5ea95SDimitris Michailidis #include <linux/ethtool.h>
421c5ea95SDimitris Michailidis #include <linux/linkmode.h>
521c5ea95SDimitris Michailidis #include <linux/netdevice.h>
621c5ea95SDimitris Michailidis #include <linux/nvme.h>
721c5ea95SDimitris Michailidis #include <linux/io.h>
821c5ea95SDimitris Michailidis #include <linux/io-64-nonatomic-lo-hi.h>
921c5ea95SDimitris Michailidis #include <linux/pci.h>
1021c5ea95SDimitris Michailidis #include <linux/rtnetlink.h>
1121c5ea95SDimitris Michailidis #include "funeth.h"
1221c5ea95SDimitris Michailidis #include "fun_port.h"
1321c5ea95SDimitris Michailidis #include "funeth_txrx.h"
1421c5ea95SDimitris Michailidis 
1521c5ea95SDimitris Michailidis /* Min queue depth. The smallest power-of-2 supporting jumbo frames with 4K
1621c5ea95SDimitris Michailidis  * pages is 8. Require it for all types of queues though some could work with
1721c5ea95SDimitris Michailidis  * fewer entries.
1821c5ea95SDimitris Michailidis  */
1921c5ea95SDimitris Michailidis #define FUNETH_MIN_QDEPTH 8
2021c5ea95SDimitris Michailidis 
2121c5ea95SDimitris Michailidis static const char mac_tx_stat_names[][ETH_GSTRING_LEN] = {
2221c5ea95SDimitris Michailidis 	"mac_tx_octets_total",
2321c5ea95SDimitris Michailidis 	"mac_tx_frames_total",
2421c5ea95SDimitris Michailidis 	"mac_tx_vlan_frames_ok",
2521c5ea95SDimitris Michailidis 	"mac_tx_unicast_frames",
2621c5ea95SDimitris Michailidis 	"mac_tx_multicast_frames",
2721c5ea95SDimitris Michailidis 	"mac_tx_broadcast_frames",
2821c5ea95SDimitris Michailidis 	"mac_tx_errors",
2921c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE0",
3021c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE1",
3121c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE2",
3221c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE3",
3321c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE4",
3421c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE5",
3521c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE6",
3621c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE7",
3721c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE8",
3821c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE9",
3921c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE10",
4021c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE11",
4121c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE12",
4221c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE13",
4321c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE14",
4421c5ea95SDimitris Michailidis 	"mac_tx_CBFCPAUSE15",
4521c5ea95SDimitris Michailidis };
4621c5ea95SDimitris Michailidis 
4721c5ea95SDimitris Michailidis static const char mac_rx_stat_names[][ETH_GSTRING_LEN] = {
4821c5ea95SDimitris Michailidis 	"mac_rx_octets_total",
4921c5ea95SDimitris Michailidis 	"mac_rx_frames_total",
5021c5ea95SDimitris Michailidis 	"mac_rx_VLAN_frames_ok",
5121c5ea95SDimitris Michailidis 	"mac_rx_unicast_frames",
5221c5ea95SDimitris Michailidis 	"mac_rx_multicast_frames",
5321c5ea95SDimitris Michailidis 	"mac_rx_broadcast_frames",
5421c5ea95SDimitris Michailidis 	"mac_rx_drop_events",
5521c5ea95SDimitris Michailidis 	"mac_rx_errors",
5621c5ea95SDimitris Michailidis 	"mac_rx_alignment_errors",
5721c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE0",
5821c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE1",
5921c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE2",
6021c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE3",
6121c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE4",
6221c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE5",
6321c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE6",
6421c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE7",
6521c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE8",
6621c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE9",
6721c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE10",
6821c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE11",
6921c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE12",
7021c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE13",
7121c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE14",
7221c5ea95SDimitris Michailidis 	"mac_rx_CBFCPAUSE15",
7321c5ea95SDimitris Michailidis };
7421c5ea95SDimitris Michailidis 
7521c5ea95SDimitris Michailidis static const char * const txq_stat_names[] = {
7621c5ea95SDimitris Michailidis 	"tx_pkts",
7721c5ea95SDimitris Michailidis 	"tx_bytes",
7821c5ea95SDimitris Michailidis 	"tx_cso",
7921c5ea95SDimitris Michailidis 	"tx_tso",
8021c5ea95SDimitris Michailidis 	"tx_encapsulated_tso",
816ce1df88SDimitris Michailidis 	"tx_uso",
8221c5ea95SDimitris Michailidis 	"tx_more",
8321c5ea95SDimitris Michailidis 	"tx_queue_stops",
8421c5ea95SDimitris Michailidis 	"tx_queue_restarts",
8521c5ea95SDimitris Michailidis 	"tx_mapping_errors",
8621c5ea95SDimitris Michailidis 	"tx_tls_encrypted_packets",
8721c5ea95SDimitris Michailidis 	"tx_tls_encrypted_bytes",
8821c5ea95SDimitris Michailidis 	"tx_tls_ooo",
8921c5ea95SDimitris Michailidis 	"tx_tls_drop_no_sync_data",
9021c5ea95SDimitris Michailidis };
9121c5ea95SDimitris Michailidis 
9221c5ea95SDimitris Michailidis static const char * const xdpq_stat_names[] = {
9321c5ea95SDimitris Michailidis 	"tx_xdp_pkts",
9421c5ea95SDimitris Michailidis 	"tx_xdp_bytes",
9521c5ea95SDimitris Michailidis 	"tx_xdp_full",
9621c5ea95SDimitris Michailidis 	"tx_xdp_mapping_errors",
9721c5ea95SDimitris Michailidis };
9821c5ea95SDimitris Michailidis 
9921c5ea95SDimitris Michailidis static const char * const rxq_stat_names[] = {
10021c5ea95SDimitris Michailidis 	"rx_pkts",
10121c5ea95SDimitris Michailidis 	"rx_bytes",
10221c5ea95SDimitris Michailidis 	"rx_cso",
10321c5ea95SDimitris Michailidis 	"gro_pkts",
10421c5ea95SDimitris Michailidis 	"gro_merged",
10521c5ea95SDimitris Michailidis 	"rx_xdp_tx",
10621c5ea95SDimitris Michailidis 	"rx_xdp_redir",
10721c5ea95SDimitris Michailidis 	"rx_xdp_drops",
10821c5ea95SDimitris Michailidis 	"rx_buffers",
10921c5ea95SDimitris Michailidis 	"rx_page_allocs",
11021c5ea95SDimitris Michailidis 	"rx_drops",
11121c5ea95SDimitris Michailidis 	"rx_budget_exhausted",
11221c5ea95SDimitris Michailidis 	"rx_mapping_errors",
11321c5ea95SDimitris Michailidis };
11421c5ea95SDimitris Michailidis 
11521c5ea95SDimitris Michailidis static const char * const tls_stat_names[] = {
11621c5ea95SDimitris Michailidis 	"tx_tls_ctx",
11721c5ea95SDimitris Michailidis 	"tx_tls_del",
11821c5ea95SDimitris Michailidis 	"tx_tls_resync",
11921c5ea95SDimitris Michailidis };
12021c5ea95SDimitris Michailidis 
fun_link_modes_to_ethtool(u64 modes,unsigned long * ethtool_modes_map)12121c5ea95SDimitris Michailidis static void fun_link_modes_to_ethtool(u64 modes,
12221c5ea95SDimitris Michailidis 				      unsigned long *ethtool_modes_map)
12321c5ea95SDimitris Michailidis {
12421c5ea95SDimitris Michailidis #define ADD_LINK_MODE(mode) \
12521c5ea95SDimitris Michailidis 	__set_bit(ETHTOOL_LINK_MODE_ ## mode ## _BIT, ethtool_modes_map)
12621c5ea95SDimitris Michailidis 
12721c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_AUTONEG)
12821c5ea95SDimitris Michailidis 		ADD_LINK_MODE(Autoneg);
12921c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_1000_X)
13021c5ea95SDimitris Michailidis 		ADD_LINK_MODE(1000baseX_Full);
13121c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_10G_R) {
13221c5ea95SDimitris Michailidis 		ADD_LINK_MODE(10000baseCR_Full);
13321c5ea95SDimitris Michailidis 		ADD_LINK_MODE(10000baseSR_Full);
13421c5ea95SDimitris Michailidis 		ADD_LINK_MODE(10000baseLR_Full);
13521c5ea95SDimitris Michailidis 		ADD_LINK_MODE(10000baseER_Full);
13621c5ea95SDimitris Michailidis 	}
13721c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_25G_R) {
13821c5ea95SDimitris Michailidis 		ADD_LINK_MODE(25000baseCR_Full);
13921c5ea95SDimitris Michailidis 		ADD_LINK_MODE(25000baseSR_Full);
14021c5ea95SDimitris Michailidis 	}
14121c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_40G_R4) {
14221c5ea95SDimitris Michailidis 		ADD_LINK_MODE(40000baseCR4_Full);
14321c5ea95SDimitris Michailidis 		ADD_LINK_MODE(40000baseSR4_Full);
14421c5ea95SDimitris Michailidis 		ADD_LINK_MODE(40000baseLR4_Full);
14521c5ea95SDimitris Michailidis 	}
14621c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_50G_R2) {
14721c5ea95SDimitris Michailidis 		ADD_LINK_MODE(50000baseCR2_Full);
14821c5ea95SDimitris Michailidis 		ADD_LINK_MODE(50000baseSR2_Full);
14921c5ea95SDimitris Michailidis 	}
15021c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_50G_R) {
15121c5ea95SDimitris Michailidis 		ADD_LINK_MODE(50000baseCR_Full);
15221c5ea95SDimitris Michailidis 		ADD_LINK_MODE(50000baseSR_Full);
15321c5ea95SDimitris Michailidis 		ADD_LINK_MODE(50000baseLR_ER_FR_Full);
15421c5ea95SDimitris Michailidis 	}
15521c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_100G_R4) {
15621c5ea95SDimitris Michailidis 		ADD_LINK_MODE(100000baseCR4_Full);
15721c5ea95SDimitris Michailidis 		ADD_LINK_MODE(100000baseSR4_Full);
15821c5ea95SDimitris Michailidis 		ADD_LINK_MODE(100000baseLR4_ER4_Full);
15921c5ea95SDimitris Michailidis 	}
16021c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_100G_R2) {
16121c5ea95SDimitris Michailidis 		ADD_LINK_MODE(100000baseCR2_Full);
16221c5ea95SDimitris Michailidis 		ADD_LINK_MODE(100000baseSR2_Full);
16321c5ea95SDimitris Michailidis 		ADD_LINK_MODE(100000baseLR2_ER2_FR2_Full);
16421c5ea95SDimitris Michailidis 	}
16521c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_FEC_NONE)
16621c5ea95SDimitris Michailidis 		ADD_LINK_MODE(FEC_NONE);
16721c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_FEC_FC)
16821c5ea95SDimitris Michailidis 		ADD_LINK_MODE(FEC_BASER);
16921c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_FEC_RS)
17021c5ea95SDimitris Michailidis 		ADD_LINK_MODE(FEC_RS);
17121c5ea95SDimitris Michailidis 	if (modes & FUN_PORT_CAP_RX_PAUSE)
17221c5ea95SDimitris Michailidis 		ADD_LINK_MODE(Pause);
17321c5ea95SDimitris Michailidis 
17421c5ea95SDimitris Michailidis #undef ADD_LINK_MODE
17521c5ea95SDimitris Michailidis }
17621c5ea95SDimitris Michailidis 
set_asym_pause(u64 advertising,struct ethtool_link_ksettings * ks)17721c5ea95SDimitris Michailidis static void set_asym_pause(u64 advertising, struct ethtool_link_ksettings *ks)
17821c5ea95SDimitris Michailidis {
17921c5ea95SDimitris Michailidis 	bool rx_pause, tx_pause;
18021c5ea95SDimitris Michailidis 
18121c5ea95SDimitris Michailidis 	rx_pause = advertising & FUN_PORT_CAP_RX_PAUSE;
18221c5ea95SDimitris Michailidis 	tx_pause = advertising & FUN_PORT_CAP_TX_PAUSE;
18321c5ea95SDimitris Michailidis 	if (tx_pause ^ rx_pause)
18421c5ea95SDimitris Michailidis 		ethtool_link_ksettings_add_link_mode(ks, advertising,
18521c5ea95SDimitris Michailidis 						     Asym_Pause);
18621c5ea95SDimitris Michailidis }
18721c5ea95SDimitris Michailidis 
fun_port_type(unsigned int xcvr)18821c5ea95SDimitris Michailidis static unsigned int fun_port_type(unsigned int xcvr)
18921c5ea95SDimitris Michailidis {
19021c5ea95SDimitris Michailidis 	if (!xcvr)
19121c5ea95SDimitris Michailidis 		return PORT_NONE;
19221c5ea95SDimitris Michailidis 
19321c5ea95SDimitris Michailidis 	switch (xcvr & 7) {
19421c5ea95SDimitris Michailidis 	case FUN_XCVR_BASET:
19521c5ea95SDimitris Michailidis 		return PORT_TP;
19621c5ea95SDimitris Michailidis 	case FUN_XCVR_CU:
19721c5ea95SDimitris Michailidis 		return PORT_DA;
19821c5ea95SDimitris Michailidis 	default:
19921c5ea95SDimitris Michailidis 		return PORT_FIBRE;
20021c5ea95SDimitris Michailidis 	}
20121c5ea95SDimitris Michailidis }
20221c5ea95SDimitris Michailidis 
fun_get_link_ksettings(struct net_device * netdev,struct ethtool_link_ksettings * ks)20321c5ea95SDimitris Michailidis static int fun_get_link_ksettings(struct net_device *netdev,
20421c5ea95SDimitris Michailidis 				  struct ethtool_link_ksettings *ks)
20521c5ea95SDimitris Michailidis {
20621c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
20721c5ea95SDimitris Michailidis 	unsigned int seq, speed, xcvr;
20821c5ea95SDimitris Michailidis 	u64 lp_advertising;
20921c5ea95SDimitris Michailidis 	bool link_up;
21021c5ea95SDimitris Michailidis 
21121c5ea95SDimitris Michailidis 	ethtool_link_ksettings_zero_link_mode(ks, supported);
21221c5ea95SDimitris Michailidis 	ethtool_link_ksettings_zero_link_mode(ks, advertising);
21321c5ea95SDimitris Michailidis 	ethtool_link_ksettings_zero_link_mode(ks, lp_advertising);
21421c5ea95SDimitris Michailidis 
21521c5ea95SDimitris Michailidis 	/* Link settings change asynchronously, take a consistent snapshot */
21621c5ea95SDimitris Michailidis 	do {
21721c5ea95SDimitris Michailidis 		seq = read_seqcount_begin(&fp->link_seq);
21821c5ea95SDimitris Michailidis 		link_up = netif_carrier_ok(netdev);
21921c5ea95SDimitris Michailidis 		speed = fp->link_speed;
22021c5ea95SDimitris Michailidis 		xcvr = fp->xcvr_type;
22121c5ea95SDimitris Michailidis 		lp_advertising = fp->lp_advertising;
22221c5ea95SDimitris Michailidis 	} while (read_seqcount_retry(&fp->link_seq, seq));
22321c5ea95SDimitris Michailidis 
22421c5ea95SDimitris Michailidis 	if (link_up) {
22521c5ea95SDimitris Michailidis 		ks->base.speed = speed;
22621c5ea95SDimitris Michailidis 		ks->base.duplex = DUPLEX_FULL;
22721c5ea95SDimitris Michailidis 		fun_link_modes_to_ethtool(lp_advertising,
22821c5ea95SDimitris Michailidis 					  ks->link_modes.lp_advertising);
22921c5ea95SDimitris Michailidis 	} else {
23021c5ea95SDimitris Michailidis 		ks->base.speed = SPEED_UNKNOWN;
23121c5ea95SDimitris Michailidis 		ks->base.duplex = DUPLEX_UNKNOWN;
23221c5ea95SDimitris Michailidis 	}
23321c5ea95SDimitris Michailidis 
23421c5ea95SDimitris Michailidis 	ks->base.autoneg = (fp->advertising & FUN_PORT_CAP_AUTONEG) ?
23521c5ea95SDimitris Michailidis 			   AUTONEG_ENABLE : AUTONEG_DISABLE;
23621c5ea95SDimitris Michailidis 	ks->base.port = fun_port_type(xcvr);
23721c5ea95SDimitris Michailidis 
23821c5ea95SDimitris Michailidis 	fun_link_modes_to_ethtool(fp->port_caps, ks->link_modes.supported);
23921c5ea95SDimitris Michailidis 	if (fp->port_caps & (FUN_PORT_CAP_RX_PAUSE | FUN_PORT_CAP_TX_PAUSE))
24021c5ea95SDimitris Michailidis 		ethtool_link_ksettings_add_link_mode(ks, supported, Asym_Pause);
24121c5ea95SDimitris Michailidis 
24221c5ea95SDimitris Michailidis 	fun_link_modes_to_ethtool(fp->advertising, ks->link_modes.advertising);
24321c5ea95SDimitris Michailidis 	set_asym_pause(fp->advertising, ks);
24421c5ea95SDimitris Michailidis 	return 0;
24521c5ea95SDimitris Michailidis }
24621c5ea95SDimitris Michailidis 
fun_advert_modes(const struct ethtool_link_ksettings * ks)24721c5ea95SDimitris Michailidis static u64 fun_advert_modes(const struct ethtool_link_ksettings *ks)
24821c5ea95SDimitris Michailidis {
24921c5ea95SDimitris Michailidis 	u64 modes = 0;
25021c5ea95SDimitris Michailidis 
25121c5ea95SDimitris Michailidis #define HAS_MODE(mode) \
25221c5ea95SDimitris Michailidis 	ethtool_link_ksettings_test_link_mode(ks, advertising, mode)
25321c5ea95SDimitris Michailidis 
25421c5ea95SDimitris Michailidis 	if (HAS_MODE(1000baseX_Full))
25521c5ea95SDimitris Michailidis 		modes |= FUN_PORT_CAP_1000_X;
25621c5ea95SDimitris Michailidis 	if (HAS_MODE(10000baseCR_Full) || HAS_MODE(10000baseSR_Full) ||
25721c5ea95SDimitris Michailidis 	    HAS_MODE(10000baseLR_Full) || HAS_MODE(10000baseER_Full))
25821c5ea95SDimitris Michailidis 		modes |= FUN_PORT_CAP_10G_R;
25921c5ea95SDimitris Michailidis 	if (HAS_MODE(25000baseCR_Full) || HAS_MODE(25000baseSR_Full))
26021c5ea95SDimitris Michailidis 		modes |= FUN_PORT_CAP_25G_R;
26121c5ea95SDimitris Michailidis 	if (HAS_MODE(40000baseCR4_Full) || HAS_MODE(40000baseSR4_Full) ||
26221c5ea95SDimitris Michailidis 	    HAS_MODE(40000baseLR4_Full))
26321c5ea95SDimitris Michailidis 		modes |= FUN_PORT_CAP_40G_R4;
26421c5ea95SDimitris Michailidis 	if (HAS_MODE(50000baseCR2_Full) || HAS_MODE(50000baseSR2_Full))
26521c5ea95SDimitris Michailidis 		modes |= FUN_PORT_CAP_50G_R2;
26621c5ea95SDimitris Michailidis 	if (HAS_MODE(50000baseCR_Full) || HAS_MODE(50000baseSR_Full) ||
26721c5ea95SDimitris Michailidis 	    HAS_MODE(50000baseLR_ER_FR_Full))
26821c5ea95SDimitris Michailidis 		modes |= FUN_PORT_CAP_50G_R;
26921c5ea95SDimitris Michailidis 	if (HAS_MODE(100000baseCR4_Full) || HAS_MODE(100000baseSR4_Full) ||
27021c5ea95SDimitris Michailidis 	    HAS_MODE(100000baseLR4_ER4_Full))
27121c5ea95SDimitris Michailidis 		modes |= FUN_PORT_CAP_100G_R4;
27221c5ea95SDimitris Michailidis 	if (HAS_MODE(100000baseCR2_Full) || HAS_MODE(100000baseSR2_Full) ||
27321c5ea95SDimitris Michailidis 	    HAS_MODE(100000baseLR2_ER2_FR2_Full))
27421c5ea95SDimitris Michailidis 		modes |= FUN_PORT_CAP_100G_R2;
27521c5ea95SDimitris Michailidis 
27621c5ea95SDimitris Michailidis 	return modes;
27721c5ea95SDimitris Michailidis #undef HAS_MODE
27821c5ea95SDimitris Michailidis }
27921c5ea95SDimitris Michailidis 
fun_speed_to_link_mode(unsigned int speed)28021c5ea95SDimitris Michailidis static u64 fun_speed_to_link_mode(unsigned int speed)
28121c5ea95SDimitris Michailidis {
28221c5ea95SDimitris Michailidis 	switch (speed) {
28321c5ea95SDimitris Michailidis 	case SPEED_100000:
28421c5ea95SDimitris Michailidis 		return FUN_PORT_CAP_100G_R4 | FUN_PORT_CAP_100G_R2;
28521c5ea95SDimitris Michailidis 	case SPEED_50000:
28621c5ea95SDimitris Michailidis 		return FUN_PORT_CAP_50G_R | FUN_PORT_CAP_50G_R2;
28721c5ea95SDimitris Michailidis 	case SPEED_40000:
28821c5ea95SDimitris Michailidis 		return FUN_PORT_CAP_40G_R4;
28921c5ea95SDimitris Michailidis 	case SPEED_25000:
29021c5ea95SDimitris Michailidis 		return FUN_PORT_CAP_25G_R;
29121c5ea95SDimitris Michailidis 	case SPEED_10000:
29221c5ea95SDimitris Michailidis 		return FUN_PORT_CAP_10G_R;
29321c5ea95SDimitris Michailidis 	case SPEED_1000:
29421c5ea95SDimitris Michailidis 		return FUN_PORT_CAP_1000_X;
29521c5ea95SDimitris Michailidis 	default:
29621c5ea95SDimitris Michailidis 		return 0;
29721c5ea95SDimitris Michailidis 	}
29821c5ea95SDimitris Michailidis }
29921c5ea95SDimitris Michailidis 
fun_change_advert(struct funeth_priv * fp,u64 new_advert)30021c5ea95SDimitris Michailidis static int fun_change_advert(struct funeth_priv *fp, u64 new_advert)
30121c5ea95SDimitris Michailidis {
30221c5ea95SDimitris Michailidis 	int err;
30321c5ea95SDimitris Michailidis 
30421c5ea95SDimitris Michailidis 	if (new_advert == fp->advertising)
30521c5ea95SDimitris Michailidis 		return 0;
30621c5ea95SDimitris Michailidis 
30721c5ea95SDimitris Michailidis 	err = fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_ADVERT, new_advert);
30821c5ea95SDimitris Michailidis 	if (!err)
30921c5ea95SDimitris Michailidis 		fp->advertising = new_advert;
31021c5ea95SDimitris Michailidis 	return err;
31121c5ea95SDimitris Michailidis }
31221c5ea95SDimitris Michailidis 
31321c5ea95SDimitris Michailidis #define FUN_PORT_CAP_FEC_MASK \
31421c5ea95SDimitris Michailidis 	(FUN_PORT_CAP_FEC_NONE | FUN_PORT_CAP_FEC_FC | FUN_PORT_CAP_FEC_RS)
31521c5ea95SDimitris Michailidis 
fun_set_link_ksettings(struct net_device * netdev,const struct ethtool_link_ksettings * ks)31621c5ea95SDimitris Michailidis static int fun_set_link_ksettings(struct net_device *netdev,
31721c5ea95SDimitris Michailidis 				  const struct ethtool_link_ksettings *ks)
31821c5ea95SDimitris Michailidis {
31921c5ea95SDimitris Michailidis 	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = {};
32021c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
32121c5ea95SDimitris Michailidis 	u64 new_advert;
32221c5ea95SDimitris Michailidis 
32321c5ea95SDimitris Michailidis 	/* eswitch ports don't support mode changes */
32421c5ea95SDimitris Michailidis 	if (fp->port_caps & FUN_PORT_CAP_VPORT)
32521c5ea95SDimitris Michailidis 		return -EOPNOTSUPP;
32621c5ea95SDimitris Michailidis 
32721c5ea95SDimitris Michailidis 	if (ks->base.duplex == DUPLEX_HALF)
32821c5ea95SDimitris Michailidis 		return -EINVAL;
32921c5ea95SDimitris Michailidis 	if (ks->base.autoneg == AUTONEG_ENABLE &&
33021c5ea95SDimitris Michailidis 	    !(fp->port_caps & FUN_PORT_CAP_AUTONEG))
33121c5ea95SDimitris Michailidis 		return -EINVAL;
33221c5ea95SDimitris Michailidis 
33321c5ea95SDimitris Michailidis 	if (ks->base.autoneg == AUTONEG_ENABLE) {
33421c5ea95SDimitris Michailidis 		if (linkmode_empty(ks->link_modes.advertising))
33521c5ea95SDimitris Michailidis 			return -EINVAL;
33621c5ea95SDimitris Michailidis 
33721c5ea95SDimitris Michailidis 		fun_link_modes_to_ethtool(fp->port_caps, supported);
33821c5ea95SDimitris Michailidis 		if (!linkmode_subset(ks->link_modes.advertising, supported))
33921c5ea95SDimitris Michailidis 			return -EINVAL;
34021c5ea95SDimitris Michailidis 
34121c5ea95SDimitris Michailidis 		new_advert = fun_advert_modes(ks) | FUN_PORT_CAP_AUTONEG;
34221c5ea95SDimitris Michailidis 	} else {
34321c5ea95SDimitris Michailidis 		new_advert = fun_speed_to_link_mode(ks->base.speed);
34421c5ea95SDimitris Michailidis 		new_advert &= fp->port_caps;
34521c5ea95SDimitris Michailidis 		if (!new_advert)
34621c5ea95SDimitris Michailidis 			return -EINVAL;
34721c5ea95SDimitris Michailidis 	}
34821c5ea95SDimitris Michailidis 	new_advert |= fp->advertising &
34921c5ea95SDimitris Michailidis 		      (FUN_PORT_CAP_PAUSE_MASK | FUN_PORT_CAP_FEC_MASK);
35021c5ea95SDimitris Michailidis 
35121c5ea95SDimitris Michailidis 	return fun_change_advert(fp, new_advert);
35221c5ea95SDimitris Michailidis }
35321c5ea95SDimitris Michailidis 
fun_get_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)35421c5ea95SDimitris Michailidis static void fun_get_pauseparam(struct net_device *netdev,
35521c5ea95SDimitris Michailidis 			       struct ethtool_pauseparam *pause)
35621c5ea95SDimitris Michailidis {
35721c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
35821c5ea95SDimitris Michailidis 	u8 active_pause = fp->active_fc;
35921c5ea95SDimitris Michailidis 
36021c5ea95SDimitris Michailidis 	pause->rx_pause = !!(active_pause & FUN_PORT_CAP_RX_PAUSE);
36121c5ea95SDimitris Michailidis 	pause->tx_pause = !!(active_pause & FUN_PORT_CAP_TX_PAUSE);
36221c5ea95SDimitris Michailidis 	pause->autoneg = !!(fp->advertising & FUN_PORT_CAP_AUTONEG);
36321c5ea95SDimitris Michailidis }
36421c5ea95SDimitris Michailidis 
fun_set_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)36521c5ea95SDimitris Michailidis static int fun_set_pauseparam(struct net_device *netdev,
36621c5ea95SDimitris Michailidis 			      struct ethtool_pauseparam *pause)
36721c5ea95SDimitris Michailidis {
36821c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
36921c5ea95SDimitris Michailidis 	u64 new_advert;
37021c5ea95SDimitris Michailidis 
37121c5ea95SDimitris Michailidis 	if (fp->port_caps & FUN_PORT_CAP_VPORT)
37221c5ea95SDimitris Michailidis 		return -EOPNOTSUPP;
37321c5ea95SDimitris Michailidis 	/* Forcing PAUSE settings with AN enabled is unsupported. */
37421c5ea95SDimitris Michailidis 	if (!pause->autoneg && (fp->advertising & FUN_PORT_CAP_AUTONEG))
37521c5ea95SDimitris Michailidis 		return -EOPNOTSUPP;
37621c5ea95SDimitris Michailidis 	if (pause->autoneg && !(fp->advertising & FUN_PORT_CAP_AUTONEG))
37721c5ea95SDimitris Michailidis 		return -EINVAL;
37821c5ea95SDimitris Michailidis 	if (pause->tx_pause && !(fp->port_caps & FUN_PORT_CAP_TX_PAUSE))
37921c5ea95SDimitris Michailidis 		return -EINVAL;
38021c5ea95SDimitris Michailidis 	if (pause->rx_pause && !(fp->port_caps & FUN_PORT_CAP_RX_PAUSE))
38121c5ea95SDimitris Michailidis 		return -EINVAL;
38221c5ea95SDimitris Michailidis 
38321c5ea95SDimitris Michailidis 	new_advert = fp->advertising & ~FUN_PORT_CAP_PAUSE_MASK;
38421c5ea95SDimitris Michailidis 	if (pause->tx_pause)
38521c5ea95SDimitris Michailidis 		new_advert |= FUN_PORT_CAP_TX_PAUSE;
38621c5ea95SDimitris Michailidis 	if (pause->rx_pause)
38721c5ea95SDimitris Michailidis 		new_advert |= FUN_PORT_CAP_RX_PAUSE;
38821c5ea95SDimitris Michailidis 
38921c5ea95SDimitris Michailidis 	return fun_change_advert(fp, new_advert);
39021c5ea95SDimitris Michailidis }
39121c5ea95SDimitris Michailidis 
fun_restart_an(struct net_device * netdev)39221c5ea95SDimitris Michailidis static int fun_restart_an(struct net_device *netdev)
39321c5ea95SDimitris Michailidis {
39421c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
39521c5ea95SDimitris Michailidis 
39621c5ea95SDimitris Michailidis 	if (!(fp->advertising & FUN_PORT_CAP_AUTONEG))
39721c5ea95SDimitris Michailidis 		return -EOPNOTSUPP;
39821c5ea95SDimitris Michailidis 
39921c5ea95SDimitris Michailidis 	return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_ADVERT,
40021c5ea95SDimitris Michailidis 				  FUN_PORT_CAP_AUTONEG);
40121c5ea95SDimitris Michailidis }
40221c5ea95SDimitris Michailidis 
fun_set_phys_id(struct net_device * netdev,enum ethtool_phys_id_state state)40321c5ea95SDimitris Michailidis static int fun_set_phys_id(struct net_device *netdev,
40421c5ea95SDimitris Michailidis 			   enum ethtool_phys_id_state state)
40521c5ea95SDimitris Michailidis {
40621c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
40721c5ea95SDimitris Michailidis 	unsigned int beacon;
40821c5ea95SDimitris Michailidis 
40921c5ea95SDimitris Michailidis 	if (fp->port_caps & FUN_PORT_CAP_VPORT)
41021c5ea95SDimitris Michailidis 		return -EOPNOTSUPP;
41121c5ea95SDimitris Michailidis 	if (state != ETHTOOL_ID_ACTIVE && state != ETHTOOL_ID_INACTIVE)
41221c5ea95SDimitris Michailidis 		return -EOPNOTSUPP;
41321c5ea95SDimitris Michailidis 
41421c5ea95SDimitris Michailidis 	beacon = state == ETHTOOL_ID_ACTIVE ? FUN_PORT_LED_BEACON_ON :
41521c5ea95SDimitris Michailidis 					      FUN_PORT_LED_BEACON_OFF;
41621c5ea95SDimitris Michailidis 	return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_LED, beacon);
41721c5ea95SDimitris Michailidis }
41821c5ea95SDimitris Michailidis 
fun_get_drvinfo(struct net_device * netdev,struct ethtool_drvinfo * info)41921c5ea95SDimitris Michailidis static void fun_get_drvinfo(struct net_device *netdev,
42021c5ea95SDimitris Michailidis 			    struct ethtool_drvinfo *info)
42121c5ea95SDimitris Michailidis {
42221c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
42321c5ea95SDimitris Michailidis 
42421c5ea95SDimitris Michailidis 	strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
42521c5ea95SDimitris Michailidis 	strscpy(info->bus_info, pci_name(fp->pdev), sizeof(info->bus_info));
42621c5ea95SDimitris Michailidis }
42721c5ea95SDimitris Michailidis 
fun_get_msglevel(struct net_device * netdev)42821c5ea95SDimitris Michailidis static u32 fun_get_msglevel(struct net_device *netdev)
42921c5ea95SDimitris Michailidis {
43021c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
43121c5ea95SDimitris Michailidis 
43221c5ea95SDimitris Michailidis 	return fp->msg_enable;
43321c5ea95SDimitris Michailidis }
43421c5ea95SDimitris Michailidis 
fun_set_msglevel(struct net_device * netdev,u32 value)43521c5ea95SDimitris Michailidis static void fun_set_msglevel(struct net_device *netdev, u32 value)
43621c5ea95SDimitris Michailidis {
43721c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
43821c5ea95SDimitris Michailidis 
43921c5ea95SDimitris Michailidis 	fp->msg_enable = value;
44021c5ea95SDimitris Michailidis }
44121c5ea95SDimitris Michailidis 
fun_get_regs_len(struct net_device * dev)44221c5ea95SDimitris Michailidis static int fun_get_regs_len(struct net_device *dev)
44321c5ea95SDimitris Michailidis {
44421c5ea95SDimitris Michailidis 	return NVME_REG_ACQ + sizeof(u64);
44521c5ea95SDimitris Michailidis }
44621c5ea95SDimitris Michailidis 
fun_get_regs(struct net_device * dev,struct ethtool_regs * regs,void * buf)44721c5ea95SDimitris Michailidis static void fun_get_regs(struct net_device *dev, struct ethtool_regs *regs,
44821c5ea95SDimitris Michailidis 			 void *buf)
44921c5ea95SDimitris Michailidis {
45021c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(dev);
45121c5ea95SDimitris Michailidis 	void __iomem *bar = fp->fdev->bar;
45221c5ea95SDimitris Michailidis 
45321c5ea95SDimitris Michailidis 	regs->version = 0;
45421c5ea95SDimitris Michailidis 	*(u64 *)(buf + NVME_REG_CAP)   = readq(bar + NVME_REG_CAP);
45521c5ea95SDimitris Michailidis 	*(u32 *)(buf + NVME_REG_VS)    = readl(bar + NVME_REG_VS);
45621c5ea95SDimitris Michailidis 	*(u32 *)(buf + NVME_REG_INTMS) = readl(bar + NVME_REG_INTMS);
45721c5ea95SDimitris Michailidis 	*(u32 *)(buf + NVME_REG_INTMC) = readl(bar + NVME_REG_INTMC);
45821c5ea95SDimitris Michailidis 	*(u32 *)(buf + NVME_REG_CC)    = readl(bar + NVME_REG_CC);
45921c5ea95SDimitris Michailidis 	*(u32 *)(buf + NVME_REG_CSTS)  = readl(bar + NVME_REG_CSTS);
46021c5ea95SDimitris Michailidis 	*(u32 *)(buf + NVME_REG_AQA)   = readl(bar + NVME_REG_AQA);
46121c5ea95SDimitris Michailidis 	*(u64 *)(buf + NVME_REG_ASQ)   = readq(bar + NVME_REG_ASQ);
46221c5ea95SDimitris Michailidis 	*(u64 *)(buf + NVME_REG_ACQ)   = readq(bar + NVME_REG_ACQ);
46321c5ea95SDimitris Michailidis }
46421c5ea95SDimitris Michailidis 
fun_get_coalesce(struct net_device * netdev,struct ethtool_coalesce * coal,struct kernel_ethtool_coalesce * kcoal,struct netlink_ext_ack * ext_ack)46521c5ea95SDimitris Michailidis static int fun_get_coalesce(struct net_device *netdev,
46621c5ea95SDimitris Michailidis 			    struct ethtool_coalesce *coal,
46721c5ea95SDimitris Michailidis 			    struct kernel_ethtool_coalesce *kcoal,
46821c5ea95SDimitris Michailidis 			    struct netlink_ext_ack *ext_ack)
46921c5ea95SDimitris Michailidis {
47021c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
47121c5ea95SDimitris Michailidis 
47221c5ea95SDimitris Michailidis 	coal->rx_coalesce_usecs        = fp->rx_coal_usec;
47321c5ea95SDimitris Michailidis 	coal->rx_max_coalesced_frames  = fp->rx_coal_count;
47421c5ea95SDimitris Michailidis 	coal->use_adaptive_rx_coalesce = !fp->cq_irq_db;
47521c5ea95SDimitris Michailidis 	coal->tx_coalesce_usecs        = fp->tx_coal_usec;
47621c5ea95SDimitris Michailidis 	coal->tx_max_coalesced_frames  = fp->tx_coal_count;
47721c5ea95SDimitris Michailidis 	return 0;
47821c5ea95SDimitris Michailidis }
47921c5ea95SDimitris Michailidis 
fun_set_coalesce(struct net_device * netdev,struct ethtool_coalesce * coal,struct kernel_ethtool_coalesce * kcoal,struct netlink_ext_ack * ext_ack)48021c5ea95SDimitris Michailidis static int fun_set_coalesce(struct net_device *netdev,
48121c5ea95SDimitris Michailidis 			    struct ethtool_coalesce *coal,
48221c5ea95SDimitris Michailidis 			    struct kernel_ethtool_coalesce *kcoal,
48321c5ea95SDimitris Michailidis 			    struct netlink_ext_ack *ext_ack)
48421c5ea95SDimitris Michailidis {
48521c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
48621c5ea95SDimitris Michailidis 	struct funeth_rxq **rxqs;
48721c5ea95SDimitris Michailidis 	unsigned int i, db_val;
48821c5ea95SDimitris Michailidis 
48921c5ea95SDimitris Michailidis 	if (coal->rx_coalesce_usecs > FUN_DB_INTCOAL_USEC_M ||
49021c5ea95SDimitris Michailidis 	    coal->rx_max_coalesced_frames > FUN_DB_INTCOAL_ENTRIES_M ||
49121c5ea95SDimitris Michailidis 	    (coal->rx_coalesce_usecs | coal->rx_max_coalesced_frames) == 0 ||
49221c5ea95SDimitris Michailidis 	    coal->tx_coalesce_usecs > FUN_DB_INTCOAL_USEC_M ||
49321c5ea95SDimitris Michailidis 	    coal->tx_max_coalesced_frames > FUN_DB_INTCOAL_ENTRIES_M ||
49421c5ea95SDimitris Michailidis 	    (coal->tx_coalesce_usecs | coal->tx_max_coalesced_frames) == 0)
49521c5ea95SDimitris Michailidis 		return -EINVAL;
49621c5ea95SDimitris Michailidis 
49721c5ea95SDimitris Michailidis 	/* a timer is required if there's any coalescing */
49821c5ea95SDimitris Michailidis 	if ((coal->rx_max_coalesced_frames > 1 && !coal->rx_coalesce_usecs) ||
49921c5ea95SDimitris Michailidis 	    (coal->tx_max_coalesced_frames > 1 && !coal->tx_coalesce_usecs))
50021c5ea95SDimitris Michailidis 		return -EINVAL;
50121c5ea95SDimitris Michailidis 
50221c5ea95SDimitris Michailidis 	fp->rx_coal_usec  = coal->rx_coalesce_usecs;
50321c5ea95SDimitris Michailidis 	fp->rx_coal_count = coal->rx_max_coalesced_frames;
50421c5ea95SDimitris Michailidis 	fp->tx_coal_usec  = coal->tx_coalesce_usecs;
50521c5ea95SDimitris Michailidis 	fp->tx_coal_count = coal->tx_max_coalesced_frames;
50621c5ea95SDimitris Michailidis 
50721c5ea95SDimitris Michailidis 	db_val = FUN_IRQ_CQ_DB(fp->rx_coal_usec, fp->rx_coal_count);
50821c5ea95SDimitris Michailidis 	WRITE_ONCE(fp->cq_irq_db, db_val);
50921c5ea95SDimitris Michailidis 
51021c5ea95SDimitris Michailidis 	rxqs = rtnl_dereference(fp->rxqs);
51121c5ea95SDimitris Michailidis 	if (!rxqs)
51221c5ea95SDimitris Michailidis 		return 0;
51321c5ea95SDimitris Michailidis 
51421c5ea95SDimitris Michailidis 	for (i = 0; i < netdev->real_num_rx_queues; i++)
51521c5ea95SDimitris Michailidis 		WRITE_ONCE(rxqs[i]->irq_db_val, db_val);
51621c5ea95SDimitris Michailidis 
51721c5ea95SDimitris Michailidis 	db_val = FUN_IRQ_SQ_DB(fp->tx_coal_usec, fp->tx_coal_count);
51821c5ea95SDimitris Michailidis 	for (i = 0; i < netdev->real_num_tx_queues; i++)
51921c5ea95SDimitris Michailidis 		WRITE_ONCE(fp->txqs[i]->irq_db_val, db_val);
52021c5ea95SDimitris Michailidis 
52121c5ea95SDimitris Michailidis 	return 0;
52221c5ea95SDimitris Michailidis }
52321c5ea95SDimitris Michailidis 
fun_get_channels(struct net_device * netdev,struct ethtool_channels * chan)52421c5ea95SDimitris Michailidis static void fun_get_channels(struct net_device *netdev,
52521c5ea95SDimitris Michailidis 			     struct ethtool_channels *chan)
52621c5ea95SDimitris Michailidis {
52721c5ea95SDimitris Michailidis 	chan->max_rx   = netdev->num_rx_queues;
52821c5ea95SDimitris Michailidis 	chan->rx_count = netdev->real_num_rx_queues;
52921c5ea95SDimitris Michailidis 
53021c5ea95SDimitris Michailidis 	chan->max_tx   = netdev->num_tx_queues;
53121c5ea95SDimitris Michailidis 	chan->tx_count = netdev->real_num_tx_queues;
53221c5ea95SDimitris Michailidis }
53321c5ea95SDimitris Michailidis 
fun_set_channels(struct net_device * netdev,struct ethtool_channels * chan)53421c5ea95SDimitris Michailidis static int fun_set_channels(struct net_device *netdev,
53521c5ea95SDimitris Michailidis 			    struct ethtool_channels *chan)
53621c5ea95SDimitris Michailidis {
53721c5ea95SDimitris Michailidis 	if (!chan->tx_count || !chan->rx_count)
53821c5ea95SDimitris Michailidis 		return -EINVAL;
53921c5ea95SDimitris Michailidis 
54021c5ea95SDimitris Michailidis 	if (chan->tx_count == netdev->real_num_tx_queues &&
54121c5ea95SDimitris Michailidis 	    chan->rx_count == netdev->real_num_rx_queues)
54221c5ea95SDimitris Michailidis 		return 0;
54321c5ea95SDimitris Michailidis 
54421c5ea95SDimitris Michailidis 	if (netif_running(netdev))
54521c5ea95SDimitris Michailidis 		return fun_change_num_queues(netdev, chan->tx_count,
54621c5ea95SDimitris Michailidis 					     chan->rx_count);
54721c5ea95SDimitris Michailidis 
54821c5ea95SDimitris Michailidis 	fun_set_ring_count(netdev, chan->tx_count, chan->rx_count);
54921c5ea95SDimitris Michailidis 	return 0;
55021c5ea95SDimitris Michailidis }
55121c5ea95SDimitris Michailidis 
fun_get_ringparam(struct net_device * netdev,struct ethtool_ringparam * ring,struct kernel_ethtool_ringparam * kring,struct netlink_ext_ack * extack)55221c5ea95SDimitris Michailidis static void fun_get_ringparam(struct net_device *netdev,
55321c5ea95SDimitris Michailidis 			      struct ethtool_ringparam *ring,
55421c5ea95SDimitris Michailidis 			      struct kernel_ethtool_ringparam *kring,
55521c5ea95SDimitris Michailidis 			      struct netlink_ext_ack *extack)
55621c5ea95SDimitris Michailidis {
55721c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
55821c5ea95SDimitris Michailidis 	unsigned int max_depth = fp->fdev->q_depth;
55921c5ea95SDimitris Michailidis 
56021c5ea95SDimitris Michailidis 	/* We size CQs to be twice the RQ depth so max RQ depth is half the
56121c5ea95SDimitris Michailidis 	 * max queue depth.
56221c5ea95SDimitris Michailidis 	 */
56321c5ea95SDimitris Michailidis 	ring->rx_max_pending = max_depth / 2;
56421c5ea95SDimitris Michailidis 	ring->tx_max_pending = max_depth;
56521c5ea95SDimitris Michailidis 
56621c5ea95SDimitris Michailidis 	ring->rx_pending = fp->rq_depth;
56721c5ea95SDimitris Michailidis 	ring->tx_pending = fp->sq_depth;
56821c5ea95SDimitris Michailidis 
56921c5ea95SDimitris Michailidis 	kring->rx_buf_len = PAGE_SIZE;
57021c5ea95SDimitris Michailidis 	kring->cqe_size = FUNETH_CQE_SIZE;
57121c5ea95SDimitris Michailidis }
57221c5ea95SDimitris Michailidis 
fun_set_ringparam(struct net_device * netdev,struct ethtool_ringparam * ring,struct kernel_ethtool_ringparam * kring,struct netlink_ext_ack * extack)57321c5ea95SDimitris Michailidis static int fun_set_ringparam(struct net_device *netdev,
57421c5ea95SDimitris Michailidis 			     struct ethtool_ringparam *ring,
57521c5ea95SDimitris Michailidis 			     struct kernel_ethtool_ringparam *kring,
57621c5ea95SDimitris Michailidis 			     struct netlink_ext_ack *extack)
57721c5ea95SDimitris Michailidis {
57821c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
57921c5ea95SDimitris Michailidis 	int rc;
58021c5ea95SDimitris Michailidis 
58121c5ea95SDimitris Michailidis 	if (ring->rx_mini_pending || ring->rx_jumbo_pending)
58221c5ea95SDimitris Michailidis 		return -EINVAL;
58321c5ea95SDimitris Michailidis 
58421c5ea95SDimitris Michailidis 	/* queue depths must be powers-of-2 */
58521c5ea95SDimitris Michailidis 	if (!is_power_of_2(ring->rx_pending) ||
58621c5ea95SDimitris Michailidis 	    !is_power_of_2(ring->tx_pending))
58721c5ea95SDimitris Michailidis 		return -EINVAL;
58821c5ea95SDimitris Michailidis 
58921c5ea95SDimitris Michailidis 	if (ring->rx_pending < FUNETH_MIN_QDEPTH ||
59021c5ea95SDimitris Michailidis 	    ring->tx_pending < FUNETH_MIN_QDEPTH)
59121c5ea95SDimitris Michailidis 		return -EINVAL;
59221c5ea95SDimitris Michailidis 
59321c5ea95SDimitris Michailidis 	if (fp->sq_depth == ring->tx_pending &&
59421c5ea95SDimitris Michailidis 	    fp->rq_depth == ring->rx_pending)
59521c5ea95SDimitris Michailidis 		return 0;
59621c5ea95SDimitris Michailidis 
59721c5ea95SDimitris Michailidis 	if (netif_running(netdev)) {
59821c5ea95SDimitris Michailidis 		struct fun_qset req = {
59921c5ea95SDimitris Michailidis 			.cq_depth = 2 * ring->rx_pending,
60021c5ea95SDimitris Michailidis 			.rq_depth = ring->rx_pending,
60121c5ea95SDimitris Michailidis 			.sq_depth = ring->tx_pending
60221c5ea95SDimitris Michailidis 		};
60321c5ea95SDimitris Michailidis 
60421c5ea95SDimitris Michailidis 		rc = fun_replace_queues(netdev, &req, extack);
60521c5ea95SDimitris Michailidis 		if (rc)
60621c5ea95SDimitris Michailidis 			return rc;
60721c5ea95SDimitris Michailidis 	}
60821c5ea95SDimitris Michailidis 
60921c5ea95SDimitris Michailidis 	fp->sq_depth = ring->tx_pending;
61021c5ea95SDimitris Michailidis 	fp->rq_depth = ring->rx_pending;
61121c5ea95SDimitris Michailidis 	fp->cq_depth = 2 * fp->rq_depth;
61221c5ea95SDimitris Michailidis 	return 0;
61321c5ea95SDimitris Michailidis }
61421c5ea95SDimitris Michailidis 
fun_get_sset_count(struct net_device * dev,int sset)61521c5ea95SDimitris Michailidis static int fun_get_sset_count(struct net_device *dev, int sset)
61621c5ea95SDimitris Michailidis {
61721c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(dev);
61821c5ea95SDimitris Michailidis 	int n;
61921c5ea95SDimitris Michailidis 
62021c5ea95SDimitris Michailidis 	switch (sset) {
62121c5ea95SDimitris Michailidis 	case ETH_SS_STATS:
62221c5ea95SDimitris Michailidis 		n = (dev->real_num_tx_queues + 1) * ARRAY_SIZE(txq_stat_names) +
62321c5ea95SDimitris Michailidis 		    (dev->real_num_rx_queues + 1) * ARRAY_SIZE(rxq_stat_names) +
62421c5ea95SDimitris Michailidis 		    (fp->num_xdpqs + 1) * ARRAY_SIZE(xdpq_stat_names) +
62521c5ea95SDimitris Michailidis 		    ARRAY_SIZE(tls_stat_names);
62621c5ea95SDimitris Michailidis 		if (fp->port_caps & FUN_PORT_CAP_STATS) {
62721c5ea95SDimitris Michailidis 			n += ARRAY_SIZE(mac_tx_stat_names) +
62821c5ea95SDimitris Michailidis 			     ARRAY_SIZE(mac_rx_stat_names);
62921c5ea95SDimitris Michailidis 		}
63021c5ea95SDimitris Michailidis 		return n;
63121c5ea95SDimitris Michailidis 	default:
63221c5ea95SDimitris Michailidis 		break;
63321c5ea95SDimitris Michailidis 	}
63421c5ea95SDimitris Michailidis 	return 0;
63521c5ea95SDimitris Michailidis }
63621c5ea95SDimitris Michailidis 
fun_get_strings(struct net_device * netdev,u32 sset,u8 * data)63721c5ea95SDimitris Michailidis static void fun_get_strings(struct net_device *netdev, u32 sset, u8 *data)
63821c5ea95SDimitris Michailidis {
63921c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
64021c5ea95SDimitris Michailidis 	unsigned int i, j;
64121c5ea95SDimitris Michailidis 	u8 *p = data;
64221c5ea95SDimitris Michailidis 
64321c5ea95SDimitris Michailidis 	switch (sset) {
64421c5ea95SDimitris Michailidis 	case ETH_SS_STATS:
64521c5ea95SDimitris Michailidis 		if (fp->port_caps & FUN_PORT_CAP_STATS) {
64621c5ea95SDimitris Michailidis 			memcpy(p, mac_tx_stat_names, sizeof(mac_tx_stat_names));
64721c5ea95SDimitris Michailidis 			p += sizeof(mac_tx_stat_names);
64821c5ea95SDimitris Michailidis 			memcpy(p, mac_rx_stat_names, sizeof(mac_rx_stat_names));
64921c5ea95SDimitris Michailidis 			p += sizeof(mac_rx_stat_names);
65021c5ea95SDimitris Michailidis 		}
65121c5ea95SDimitris Michailidis 
65221c5ea95SDimitris Michailidis 		for (i = 0; i < netdev->real_num_tx_queues; i++) {
65321c5ea95SDimitris Michailidis 			for (j = 0; j < ARRAY_SIZE(txq_stat_names); j++)
65421c5ea95SDimitris Michailidis 				ethtool_sprintf(&p, "%s[%u]", txq_stat_names[j],
65521c5ea95SDimitris Michailidis 						i);
65621c5ea95SDimitris Michailidis 		}
65721c5ea95SDimitris Michailidis 		for (j = 0; j < ARRAY_SIZE(txq_stat_names); j++)
658e403cfffSjustinstitt@google.com 			ethtool_puts(&p, txq_stat_names[j]);
65921c5ea95SDimitris Michailidis 
66021c5ea95SDimitris Michailidis 		for (i = 0; i < fp->num_xdpqs; i++) {
66121c5ea95SDimitris Michailidis 			for (j = 0; j < ARRAY_SIZE(xdpq_stat_names); j++)
66221c5ea95SDimitris Michailidis 				ethtool_sprintf(&p, "%s[%u]",
66321c5ea95SDimitris Michailidis 						xdpq_stat_names[j], i);
66421c5ea95SDimitris Michailidis 		}
66521c5ea95SDimitris Michailidis 		for (j = 0; j < ARRAY_SIZE(xdpq_stat_names); j++)
666e403cfffSjustinstitt@google.com 			ethtool_puts(&p, xdpq_stat_names[j]);
66721c5ea95SDimitris Michailidis 
66821c5ea95SDimitris Michailidis 		for (i = 0; i < netdev->real_num_rx_queues; i++) {
66921c5ea95SDimitris Michailidis 			for (j = 0; j < ARRAY_SIZE(rxq_stat_names); j++)
67021c5ea95SDimitris Michailidis 				ethtool_sprintf(&p, "%s[%u]", rxq_stat_names[j],
67121c5ea95SDimitris Michailidis 						i);
67221c5ea95SDimitris Michailidis 		}
67321c5ea95SDimitris Michailidis 		for (j = 0; j < ARRAY_SIZE(rxq_stat_names); j++)
674e403cfffSjustinstitt@google.com 			ethtool_puts(&p, rxq_stat_names[j]);
67521c5ea95SDimitris Michailidis 
67621c5ea95SDimitris Michailidis 		for (j = 0; j < ARRAY_SIZE(tls_stat_names); j++)
677e403cfffSjustinstitt@google.com 			ethtool_puts(&p, tls_stat_names[j]);
67821c5ea95SDimitris Michailidis 		break;
67921c5ea95SDimitris Michailidis 	default:
68021c5ea95SDimitris Michailidis 		break;
68121c5ea95SDimitris Michailidis 	}
68221c5ea95SDimitris Michailidis }
68321c5ea95SDimitris Michailidis 
get_mac_stats(const struct funeth_priv * fp,u64 * data)68421c5ea95SDimitris Michailidis static u64 *get_mac_stats(const struct funeth_priv *fp, u64 *data)
68521c5ea95SDimitris Michailidis {
68621c5ea95SDimitris Michailidis #define TX_STAT(s) \
68721c5ea95SDimitris Michailidis 	*data++ = be64_to_cpu(fp->stats[PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_##s])
68821c5ea95SDimitris Michailidis 
68921c5ea95SDimitris Michailidis 	TX_STAT(etherStatsOctets);
69021c5ea95SDimitris Michailidis 	TX_STAT(etherStatsPkts);
69121c5ea95SDimitris Michailidis 	TX_STAT(VLANTransmittedOK);
69221c5ea95SDimitris Michailidis 	TX_STAT(ifOutUcastPkts);
69321c5ea95SDimitris Michailidis 	TX_STAT(ifOutMulticastPkts);
69421c5ea95SDimitris Michailidis 	TX_STAT(ifOutBroadcastPkts);
69521c5ea95SDimitris Michailidis 	TX_STAT(ifOutErrors);
69621c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_0);
69721c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_1);
69821c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_2);
69921c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_3);
70021c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_4);
70121c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_5);
70221c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_6);
70321c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_7);
70421c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_8);
70521c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_9);
70621c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_10);
70721c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_11);
70821c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_12);
70921c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_13);
71021c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_14);
71121c5ea95SDimitris Michailidis 	TX_STAT(CBFCPAUSEFramesTransmitted_15);
71221c5ea95SDimitris Michailidis 
71321c5ea95SDimitris Michailidis #define RX_STAT(s) *data++ = be64_to_cpu(fp->stats[PORT_MAC_RX_##s])
71421c5ea95SDimitris Michailidis 
71521c5ea95SDimitris Michailidis 	RX_STAT(etherStatsOctets);
71621c5ea95SDimitris Michailidis 	RX_STAT(etherStatsPkts);
71721c5ea95SDimitris Michailidis 	RX_STAT(VLANReceivedOK);
71821c5ea95SDimitris Michailidis 	RX_STAT(ifInUcastPkts);
71921c5ea95SDimitris Michailidis 	RX_STAT(ifInMulticastPkts);
72021c5ea95SDimitris Michailidis 	RX_STAT(ifInBroadcastPkts);
72121c5ea95SDimitris Michailidis 	RX_STAT(etherStatsDropEvents);
72221c5ea95SDimitris Michailidis 	RX_STAT(ifInErrors);
72321c5ea95SDimitris Michailidis 	RX_STAT(aAlignmentErrors);
72421c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_0);
72521c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_1);
72621c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_2);
72721c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_3);
72821c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_4);
72921c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_5);
73021c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_6);
73121c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_7);
73221c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_8);
73321c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_9);
73421c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_10);
73521c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_11);
73621c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_12);
73721c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_13);
73821c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_14);
73921c5ea95SDimitris Michailidis 	RX_STAT(CBFCPAUSEFramesReceived_15);
74021c5ea95SDimitris Michailidis 
74121c5ea95SDimitris Michailidis 	return data;
74221c5ea95SDimitris Michailidis 
74321c5ea95SDimitris Michailidis #undef TX_STAT
74421c5ea95SDimitris Michailidis #undef RX_STAT
74521c5ea95SDimitris Michailidis }
74621c5ea95SDimitris Michailidis 
fun_get_ethtool_stats(struct net_device * netdev,struct ethtool_stats * stats,u64 * data)74721c5ea95SDimitris Michailidis static void fun_get_ethtool_stats(struct net_device *netdev,
74821c5ea95SDimitris Michailidis 				  struct ethtool_stats *stats, u64 *data)
74921c5ea95SDimitris Michailidis {
75021c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
75121c5ea95SDimitris Michailidis 	struct funeth_txq_stats txs;
75221c5ea95SDimitris Michailidis 	struct funeth_rxq_stats rxs;
75321c5ea95SDimitris Michailidis 	struct funeth_txq **xdpqs;
75421c5ea95SDimitris Michailidis 	struct funeth_rxq **rxqs;
75521c5ea95SDimitris Michailidis 	unsigned int i, start;
75621c5ea95SDimitris Michailidis 	u64 *totals, *tot;
75721c5ea95SDimitris Michailidis 
75821c5ea95SDimitris Michailidis 	if (fp->port_caps & FUN_PORT_CAP_STATS)
75921c5ea95SDimitris Michailidis 		data = get_mac_stats(fp, data);
76021c5ea95SDimitris Michailidis 
76121c5ea95SDimitris Michailidis 	rxqs = rtnl_dereference(fp->rxqs);
76221c5ea95SDimitris Michailidis 	if (!rxqs)
76321c5ea95SDimitris Michailidis 		return;
76421c5ea95SDimitris Michailidis 
76521c5ea95SDimitris Michailidis #define ADD_STAT(cnt) do { \
76621c5ea95SDimitris Michailidis 	*data = (cnt); *tot++ += *data++; \
76721c5ea95SDimitris Michailidis } while (0)
76821c5ea95SDimitris Michailidis 
76921c5ea95SDimitris Michailidis 	/* Tx queues */
77021c5ea95SDimitris Michailidis 	totals = data + netdev->real_num_tx_queues * ARRAY_SIZE(txq_stat_names);
77121c5ea95SDimitris Michailidis 
77221c5ea95SDimitris Michailidis 	for (i = 0; i < netdev->real_num_tx_queues; i++) {
77321c5ea95SDimitris Michailidis 		tot = totals;
77421c5ea95SDimitris Michailidis 
77521c5ea95SDimitris Michailidis 		FUN_QSTAT_READ(fp->txqs[i], start, txs);
77621c5ea95SDimitris Michailidis 
77721c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_pkts);
77821c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_bytes);
77921c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_cso);
78021c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_tso);
78121c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_encap_tso);
7826ce1df88SDimitris Michailidis 		ADD_STAT(txs.tx_uso);
78321c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_more);
78421c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_nstops);
78521c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_nrestarts);
78621c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_map_err);
78721c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_tls_pkts);
78821c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_tls_bytes);
78921c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_tls_fallback);
79021c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_tls_drops);
79121c5ea95SDimitris Michailidis 	}
79221c5ea95SDimitris Michailidis 	data += ARRAY_SIZE(txq_stat_names);
79321c5ea95SDimitris Michailidis 
79421c5ea95SDimitris Michailidis 	/* XDP Tx queues */
79521c5ea95SDimitris Michailidis 	xdpqs = rtnl_dereference(fp->xdpqs);
79621c5ea95SDimitris Michailidis 	totals = data + fp->num_xdpqs * ARRAY_SIZE(xdpq_stat_names);
79721c5ea95SDimitris Michailidis 
79821c5ea95SDimitris Michailidis 	for (i = 0; i < fp->num_xdpqs; i++) {
79921c5ea95SDimitris Michailidis 		tot = totals;
80021c5ea95SDimitris Michailidis 
80121c5ea95SDimitris Michailidis 		FUN_QSTAT_READ(xdpqs[i], start, txs);
80221c5ea95SDimitris Michailidis 
80321c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_pkts);
80421c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_bytes);
80521c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_xdp_full);
80621c5ea95SDimitris Michailidis 		ADD_STAT(txs.tx_map_err);
80721c5ea95SDimitris Michailidis 	}
80821c5ea95SDimitris Michailidis 	data += ARRAY_SIZE(xdpq_stat_names);
80921c5ea95SDimitris Michailidis 
81021c5ea95SDimitris Michailidis 	/* Rx queues */
81121c5ea95SDimitris Michailidis 	totals = data + netdev->real_num_rx_queues * ARRAY_SIZE(rxq_stat_names);
81221c5ea95SDimitris Michailidis 
81321c5ea95SDimitris Michailidis 	for (i = 0; i < netdev->real_num_rx_queues; i++) {
81421c5ea95SDimitris Michailidis 		tot = totals;
81521c5ea95SDimitris Michailidis 
81621c5ea95SDimitris Michailidis 		FUN_QSTAT_READ(rxqs[i], start, rxs);
81721c5ea95SDimitris Michailidis 
81821c5ea95SDimitris Michailidis 		ADD_STAT(rxs.rx_pkts);
81921c5ea95SDimitris Michailidis 		ADD_STAT(rxs.rx_bytes);
82021c5ea95SDimitris Michailidis 		ADD_STAT(rxs.rx_cso);
82121c5ea95SDimitris Michailidis 		ADD_STAT(rxs.gro_pkts);
82221c5ea95SDimitris Michailidis 		ADD_STAT(rxs.gro_merged);
82321c5ea95SDimitris Michailidis 		ADD_STAT(rxs.xdp_tx);
82421c5ea95SDimitris Michailidis 		ADD_STAT(rxs.xdp_redir);
82521c5ea95SDimitris Michailidis 		ADD_STAT(rxs.xdp_drops);
82621c5ea95SDimitris Michailidis 		ADD_STAT(rxs.rx_bufs);
82721c5ea95SDimitris Michailidis 		ADD_STAT(rxs.rx_page_alloc);
82821c5ea95SDimitris Michailidis 		ADD_STAT(rxs.rx_mem_drops + rxs.xdp_err);
82921c5ea95SDimitris Michailidis 		ADD_STAT(rxs.rx_budget);
83021c5ea95SDimitris Michailidis 		ADD_STAT(rxs.rx_map_err);
83121c5ea95SDimitris Michailidis 	}
83221c5ea95SDimitris Michailidis 	data += ARRAY_SIZE(rxq_stat_names);
83321c5ea95SDimitris Michailidis #undef ADD_STAT
83421c5ea95SDimitris Michailidis 
83521c5ea95SDimitris Michailidis 	*data++ = atomic64_read(&fp->tx_tls_add);
83621c5ea95SDimitris Michailidis 	*data++ = atomic64_read(&fp->tx_tls_del);
83721c5ea95SDimitris Michailidis 	*data++ = atomic64_read(&fp->tx_tls_resync);
83821c5ea95SDimitris Michailidis }
83921c5ea95SDimitris Michailidis 
84021c5ea95SDimitris Michailidis #define RX_STAT(fp, s) be64_to_cpu((fp)->stats[PORT_MAC_RX_##s])
84121c5ea95SDimitris Michailidis #define TX_STAT(fp, s) \
84221c5ea95SDimitris Michailidis 	be64_to_cpu((fp)->stats[PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_##s])
84321c5ea95SDimitris Michailidis #define FEC_STAT(fp, s) \
84421c5ea95SDimitris Michailidis 	be64_to_cpu((fp)->stats[PORT_MAC_RX_STATS_MAX + \
84521c5ea95SDimitris Michailidis 				PORT_MAC_TX_STATS_MAX + PORT_MAC_FEC_##s])
84621c5ea95SDimitris Michailidis 
fun_get_pause_stats(struct net_device * netdev,struct ethtool_pause_stats * stats)84721c5ea95SDimitris Michailidis static void fun_get_pause_stats(struct net_device *netdev,
84821c5ea95SDimitris Michailidis 				struct ethtool_pause_stats *stats)
84921c5ea95SDimitris Michailidis {
85021c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
85121c5ea95SDimitris Michailidis 
85221c5ea95SDimitris Michailidis 	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
85321c5ea95SDimitris Michailidis 		return;
85421c5ea95SDimitris Michailidis 
85521c5ea95SDimitris Michailidis 	stats->tx_pause_frames = TX_STAT(fp, aPAUSEMACCtrlFramesTransmitted);
85621c5ea95SDimitris Michailidis 	stats->rx_pause_frames = RX_STAT(fp, aPAUSEMACCtrlFramesReceived);
85721c5ea95SDimitris Michailidis }
85821c5ea95SDimitris Michailidis 
fun_get_802_3_stats(struct net_device * netdev,struct ethtool_eth_mac_stats * stats)85921c5ea95SDimitris Michailidis static void fun_get_802_3_stats(struct net_device *netdev,
86021c5ea95SDimitris Michailidis 				struct ethtool_eth_mac_stats *stats)
86121c5ea95SDimitris Michailidis {
86221c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
86321c5ea95SDimitris Michailidis 
86421c5ea95SDimitris Michailidis 	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
86521c5ea95SDimitris Michailidis 		return;
86621c5ea95SDimitris Michailidis 
86721c5ea95SDimitris Michailidis 	stats->FramesTransmittedOK = TX_STAT(fp, aFramesTransmittedOK);
86821c5ea95SDimitris Michailidis 	stats->FramesReceivedOK = RX_STAT(fp, aFramesReceivedOK);
86921c5ea95SDimitris Michailidis 	stats->FrameCheckSequenceErrors = RX_STAT(fp, aFrameCheckSequenceErrors);
87021c5ea95SDimitris Michailidis 	stats->OctetsTransmittedOK = TX_STAT(fp, OctetsTransmittedOK);
87121c5ea95SDimitris Michailidis 	stats->OctetsReceivedOK = RX_STAT(fp, OctetsReceivedOK);
87221c5ea95SDimitris Michailidis 	stats->InRangeLengthErrors = RX_STAT(fp, aInRangeLengthErrors);
87321c5ea95SDimitris Michailidis 	stats->FrameTooLongErrors = RX_STAT(fp, aFrameTooLongErrors);
87421c5ea95SDimitris Michailidis }
87521c5ea95SDimitris Michailidis 
fun_get_802_3_ctrl_stats(struct net_device * netdev,struct ethtool_eth_ctrl_stats * stats)87621c5ea95SDimitris Michailidis static void fun_get_802_3_ctrl_stats(struct net_device *netdev,
87721c5ea95SDimitris Michailidis 				     struct ethtool_eth_ctrl_stats *stats)
87821c5ea95SDimitris Michailidis {
87921c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
88021c5ea95SDimitris Michailidis 
88121c5ea95SDimitris Michailidis 	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
88221c5ea95SDimitris Michailidis 		return;
88321c5ea95SDimitris Michailidis 
88421c5ea95SDimitris Michailidis 	stats->MACControlFramesTransmitted = TX_STAT(fp, MACControlFramesTransmitted);
88521c5ea95SDimitris Michailidis 	stats->MACControlFramesReceived = RX_STAT(fp, MACControlFramesReceived);
88621c5ea95SDimitris Michailidis }
88721c5ea95SDimitris Michailidis 
fun_get_rmon_stats(struct net_device * netdev,struct ethtool_rmon_stats * stats,const struct ethtool_rmon_hist_range ** ranges)88821c5ea95SDimitris Michailidis static void fun_get_rmon_stats(struct net_device *netdev,
88921c5ea95SDimitris Michailidis 			       struct ethtool_rmon_stats *stats,
89021c5ea95SDimitris Michailidis 			       const struct ethtool_rmon_hist_range **ranges)
89121c5ea95SDimitris Michailidis {
89221c5ea95SDimitris Michailidis 	static const struct ethtool_rmon_hist_range rmon_ranges[] = {
89321c5ea95SDimitris Michailidis 		{   64,    64 },
89421c5ea95SDimitris Michailidis 		{   65,   127 },
89521c5ea95SDimitris Michailidis 		{  128,   255 },
89621c5ea95SDimitris Michailidis 		{  256,   511 },
89721c5ea95SDimitris Michailidis 		{  512,  1023 },
89821c5ea95SDimitris Michailidis 		{ 1024,  1518 },
89921c5ea95SDimitris Michailidis 		{ 1519, 32767 },
90021c5ea95SDimitris Michailidis 		{}
90121c5ea95SDimitris Michailidis 	};
90221c5ea95SDimitris Michailidis 
90321c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
90421c5ea95SDimitris Michailidis 
90521c5ea95SDimitris Michailidis 	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
90621c5ea95SDimitris Michailidis 		return;
90721c5ea95SDimitris Michailidis 
90821c5ea95SDimitris Michailidis 	stats->undersize_pkts = RX_STAT(fp, etherStatsUndersizePkts);
90921c5ea95SDimitris Michailidis 	stats->oversize_pkts = RX_STAT(fp, etherStatsOversizePkts);
91021c5ea95SDimitris Michailidis 	stats->fragments = RX_STAT(fp, etherStatsFragments);
91121c5ea95SDimitris Michailidis 	stats->jabbers = RX_STAT(fp, etherStatsJabbers);
91221c5ea95SDimitris Michailidis 
91321c5ea95SDimitris Michailidis 	stats->hist[0] = RX_STAT(fp, etherStatsPkts64Octets);
91421c5ea95SDimitris Michailidis 	stats->hist[1] = RX_STAT(fp, etherStatsPkts65to127Octets);
91521c5ea95SDimitris Michailidis 	stats->hist[2] = RX_STAT(fp, etherStatsPkts128to255Octets);
91621c5ea95SDimitris Michailidis 	stats->hist[3] = RX_STAT(fp, etherStatsPkts256to511Octets);
91721c5ea95SDimitris Michailidis 	stats->hist[4] = RX_STAT(fp, etherStatsPkts512to1023Octets);
91821c5ea95SDimitris Michailidis 	stats->hist[5] = RX_STAT(fp, etherStatsPkts1024to1518Octets);
91921c5ea95SDimitris Michailidis 	stats->hist[6] = RX_STAT(fp, etherStatsPkts1519toMaxOctets);
92021c5ea95SDimitris Michailidis 
92121c5ea95SDimitris Michailidis 	stats->hist_tx[0] = TX_STAT(fp, etherStatsPkts64Octets);
92221c5ea95SDimitris Michailidis 	stats->hist_tx[1] = TX_STAT(fp, etherStatsPkts65to127Octets);
92321c5ea95SDimitris Michailidis 	stats->hist_tx[2] = TX_STAT(fp, etherStatsPkts128to255Octets);
92421c5ea95SDimitris Michailidis 	stats->hist_tx[3] = TX_STAT(fp, etherStatsPkts256to511Octets);
92521c5ea95SDimitris Michailidis 	stats->hist_tx[4] = TX_STAT(fp, etherStatsPkts512to1023Octets);
92621c5ea95SDimitris Michailidis 	stats->hist_tx[5] = TX_STAT(fp, etherStatsPkts1024to1518Octets);
92721c5ea95SDimitris Michailidis 	stats->hist_tx[6] = TX_STAT(fp, etherStatsPkts1519toMaxOctets);
92821c5ea95SDimitris Michailidis 
92921c5ea95SDimitris Michailidis 	*ranges = rmon_ranges;
93021c5ea95SDimitris Michailidis }
93121c5ea95SDimitris Michailidis 
fun_get_fec_stats(struct net_device * netdev,struct ethtool_fec_stats * stats)93221c5ea95SDimitris Michailidis static void fun_get_fec_stats(struct net_device *netdev,
93321c5ea95SDimitris Michailidis 			      struct ethtool_fec_stats *stats)
93421c5ea95SDimitris Michailidis {
93521c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
93621c5ea95SDimitris Michailidis 
93721c5ea95SDimitris Michailidis 	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
93821c5ea95SDimitris Michailidis 		return;
93921c5ea95SDimitris Michailidis 
94021c5ea95SDimitris Michailidis 	stats->corrected_blocks.total = FEC_STAT(fp, Correctable);
94121c5ea95SDimitris Michailidis 	stats->uncorrectable_blocks.total = FEC_STAT(fp, Uncorrectable);
94221c5ea95SDimitris Michailidis }
94321c5ea95SDimitris Michailidis 
94421c5ea95SDimitris Michailidis #undef RX_STAT
94521c5ea95SDimitris Michailidis #undef TX_STAT
94621c5ea95SDimitris Michailidis #undef FEC_STAT
94721c5ea95SDimitris Michailidis 
fun_get_rxnfc(struct net_device * netdev,struct ethtool_rxnfc * cmd,u32 * rule_locs)94821c5ea95SDimitris Michailidis static int fun_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
94921c5ea95SDimitris Michailidis 			 u32 *rule_locs)
95021c5ea95SDimitris Michailidis {
95121c5ea95SDimitris Michailidis 	switch (cmd->cmd) {
95221c5ea95SDimitris Michailidis 	case ETHTOOL_GRXRINGS:
95321c5ea95SDimitris Michailidis 		cmd->data = netdev->real_num_rx_queues;
95421c5ea95SDimitris Michailidis 		return 0;
95521c5ea95SDimitris Michailidis 	default:
95621c5ea95SDimitris Michailidis 		break;
95721c5ea95SDimitris Michailidis 	}
95821c5ea95SDimitris Michailidis 	return -EOPNOTSUPP;
95921c5ea95SDimitris Michailidis }
96021c5ea95SDimitris Michailidis 
fun_set_rxnfc(struct net_device * netdev,struct ethtool_rxnfc * info)96121c5ea95SDimitris Michailidis static int fun_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info)
96221c5ea95SDimitris Michailidis {
96321c5ea95SDimitris Michailidis 	return 0;
96421c5ea95SDimitris Michailidis }
96521c5ea95SDimitris Michailidis 
fun_get_rxfh_indir_size(struct net_device * netdev)96621c5ea95SDimitris Michailidis static u32 fun_get_rxfh_indir_size(struct net_device *netdev)
96721c5ea95SDimitris Michailidis {
96821c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
96921c5ea95SDimitris Michailidis 
97021c5ea95SDimitris Michailidis 	return fp->indir_table_nentries;
97121c5ea95SDimitris Michailidis }
97221c5ea95SDimitris Michailidis 
fun_get_rxfh_key_size(struct net_device * netdev)97321c5ea95SDimitris Michailidis static u32 fun_get_rxfh_key_size(struct net_device *netdev)
97421c5ea95SDimitris Michailidis {
97521c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
97621c5ea95SDimitris Michailidis 
97721c5ea95SDimitris Michailidis 	return sizeof(fp->rss_key);
97821c5ea95SDimitris Michailidis }
97921c5ea95SDimitris Michailidis 
fun_get_rxfh(struct net_device * netdev,struct ethtool_rxfh_param * rxfh)980*fb6e30a7SAhmed Zaki static int fun_get_rxfh(struct net_device *netdev,
981*fb6e30a7SAhmed Zaki 			struct ethtool_rxfh_param *rxfh)
98221c5ea95SDimitris Michailidis {
98321c5ea95SDimitris Michailidis 	const struct funeth_priv *fp = netdev_priv(netdev);
98421c5ea95SDimitris Michailidis 
98521c5ea95SDimitris Michailidis 	if (!fp->rss_cfg)
98621c5ea95SDimitris Michailidis 		return -EOPNOTSUPP;
98721c5ea95SDimitris Michailidis 
988*fb6e30a7SAhmed Zaki 	if (rxfh->indir)
989*fb6e30a7SAhmed Zaki 		memcpy(rxfh->indir, fp->indir_table,
99021c5ea95SDimitris Michailidis 		       sizeof(u32) * fp->indir_table_nentries);
99121c5ea95SDimitris Michailidis 
992*fb6e30a7SAhmed Zaki 	if (rxfh->key)
993*fb6e30a7SAhmed Zaki 		memcpy(rxfh->key, fp->rss_key, sizeof(fp->rss_key));
99421c5ea95SDimitris Michailidis 
995*fb6e30a7SAhmed Zaki 	rxfh->hfunc = fp->hash_algo == FUN_ETH_RSS_ALG_TOEPLITZ ?
99621c5ea95SDimitris Michailidis 			ETH_RSS_HASH_TOP : ETH_RSS_HASH_CRC32;
99721c5ea95SDimitris Michailidis 
99821c5ea95SDimitris Michailidis 	return 0;
99921c5ea95SDimitris Michailidis }
100021c5ea95SDimitris Michailidis 
fun_set_rxfh(struct net_device * netdev,struct ethtool_rxfh_param * rxfh,struct netlink_ext_ack * extack)1001*fb6e30a7SAhmed Zaki static int fun_set_rxfh(struct net_device *netdev,
1002*fb6e30a7SAhmed Zaki 			struct ethtool_rxfh_param *rxfh,
1003*fb6e30a7SAhmed Zaki 			struct netlink_ext_ack *extack)
100421c5ea95SDimitris Michailidis {
100521c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
1006*fb6e30a7SAhmed Zaki 	const u32 *rss_indir = rxfh->indir ? rxfh->indir : fp->indir_table;
1007*fb6e30a7SAhmed Zaki 	const u8 *rss_key = rxfh->key ? rxfh->key : fp->rss_key;
100821c5ea95SDimitris Michailidis 	enum fun_eth_hash_alg algo;
100921c5ea95SDimitris Michailidis 
101021c5ea95SDimitris Michailidis 	if (!fp->rss_cfg)
101121c5ea95SDimitris Michailidis 		return -EOPNOTSUPP;
101221c5ea95SDimitris Michailidis 
1013*fb6e30a7SAhmed Zaki 	if (rxfh->hfunc == ETH_RSS_HASH_NO_CHANGE)
101421c5ea95SDimitris Michailidis 		algo = fp->hash_algo;
1015*fb6e30a7SAhmed Zaki 	else if (rxfh->hfunc == ETH_RSS_HASH_CRC32)
101621c5ea95SDimitris Michailidis 		algo = FUN_ETH_RSS_ALG_CRC32;
1017*fb6e30a7SAhmed Zaki 	else if (rxfh->hfunc == ETH_RSS_HASH_TOP)
101821c5ea95SDimitris Michailidis 		algo = FUN_ETH_RSS_ALG_TOEPLITZ;
101921c5ea95SDimitris Michailidis 	else
102021c5ea95SDimitris Michailidis 		return -EINVAL;
102121c5ea95SDimitris Michailidis 
102221c5ea95SDimitris Michailidis 	/* If the port is enabled try to reconfigure RSS and keep the new
102321c5ea95SDimitris Michailidis 	 * settings if successful. If it is down we update the RSS settings
102421c5ea95SDimitris Michailidis 	 * and apply them at the next UP time.
102521c5ea95SDimitris Michailidis 	 */
102621c5ea95SDimitris Michailidis 	if (netif_running(netdev)) {
102721c5ea95SDimitris Michailidis 		int rc = fun_config_rss(netdev, algo, rss_key, rss_indir,
102821c5ea95SDimitris Michailidis 					FUN_ADMIN_SUBOP_MODIFY);
102921c5ea95SDimitris Michailidis 		if (rc)
103021c5ea95SDimitris Michailidis 			return rc;
103121c5ea95SDimitris Michailidis 	}
103221c5ea95SDimitris Michailidis 
103321c5ea95SDimitris Michailidis 	fp->hash_algo = algo;
1034*fb6e30a7SAhmed Zaki 	if (rxfh->key)
1035*fb6e30a7SAhmed Zaki 		memcpy(fp->rss_key, rxfh->key, sizeof(fp->rss_key));
1036*fb6e30a7SAhmed Zaki 	if (rxfh->indir)
1037*fb6e30a7SAhmed Zaki 		memcpy(fp->indir_table, rxfh->indir,
103821c5ea95SDimitris Michailidis 		       sizeof(u32) * fp->indir_table_nentries);
103921c5ea95SDimitris Michailidis 	return 0;
104021c5ea95SDimitris Michailidis }
104121c5ea95SDimitris Michailidis 
fun_get_ts_info(struct net_device * netdev,struct ethtool_ts_info * info)104221c5ea95SDimitris Michailidis static int fun_get_ts_info(struct net_device *netdev,
104321c5ea95SDimitris Michailidis 			   struct ethtool_ts_info *info)
104421c5ea95SDimitris Michailidis {
104521c5ea95SDimitris Michailidis 	info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
104621c5ea95SDimitris Michailidis 				SOF_TIMESTAMPING_RX_HARDWARE |
104721c5ea95SDimitris Michailidis 				SOF_TIMESTAMPING_TX_SOFTWARE |
104821c5ea95SDimitris Michailidis 				SOF_TIMESTAMPING_SOFTWARE |
104921c5ea95SDimitris Michailidis 				SOF_TIMESTAMPING_RAW_HARDWARE;
105021c5ea95SDimitris Michailidis 	info->phc_index = -1;
105121c5ea95SDimitris Michailidis 	info->tx_types = BIT(HWTSTAMP_TX_OFF);
105221c5ea95SDimitris Michailidis 	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
105321c5ea95SDimitris Michailidis 	return 0;
105421c5ea95SDimitris Michailidis }
105521c5ea95SDimitris Michailidis 
to_ethtool_fec(unsigned int fun_fec)105621c5ea95SDimitris Michailidis static unsigned int to_ethtool_fec(unsigned int fun_fec)
105721c5ea95SDimitris Michailidis {
105821c5ea95SDimitris Michailidis 	unsigned int fec = 0;
105921c5ea95SDimitris Michailidis 
106021c5ea95SDimitris Michailidis 	if (fun_fec == FUN_PORT_FEC_NA)
106121c5ea95SDimitris Michailidis 		fec |= ETHTOOL_FEC_NONE;
106221c5ea95SDimitris Michailidis 	if (fun_fec & FUN_PORT_FEC_OFF)
106321c5ea95SDimitris Michailidis 		fec |= ETHTOOL_FEC_OFF;
106421c5ea95SDimitris Michailidis 	if (fun_fec & FUN_PORT_FEC_RS)
106521c5ea95SDimitris Michailidis 		fec |= ETHTOOL_FEC_RS;
106621c5ea95SDimitris Michailidis 	if (fun_fec & FUN_PORT_FEC_FC)
106721c5ea95SDimitris Michailidis 		fec |= ETHTOOL_FEC_BASER;
106821c5ea95SDimitris Michailidis 	if (fun_fec & FUN_PORT_FEC_AUTO)
106921c5ea95SDimitris Michailidis 		fec |= ETHTOOL_FEC_AUTO;
107021c5ea95SDimitris Michailidis 	return fec;
107121c5ea95SDimitris Michailidis }
107221c5ea95SDimitris Michailidis 
fun_get_fecparam(struct net_device * netdev,struct ethtool_fecparam * fec)107321c5ea95SDimitris Michailidis static int fun_get_fecparam(struct net_device *netdev,
107421c5ea95SDimitris Michailidis 			    struct ethtool_fecparam *fec)
107521c5ea95SDimitris Michailidis {
107621c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
107721c5ea95SDimitris Michailidis 	u64 fec_data;
107821c5ea95SDimitris Michailidis 	int rc;
107921c5ea95SDimitris Michailidis 
108021c5ea95SDimitris Michailidis 	rc = fun_port_read_cmd(fp, FUN_ADMIN_PORT_KEY_FEC, &fec_data);
108121c5ea95SDimitris Michailidis 	if (rc)
108221c5ea95SDimitris Michailidis 		return rc;
108321c5ea95SDimitris Michailidis 
108421c5ea95SDimitris Michailidis 	fec->active_fec = to_ethtool_fec(fec_data & 0xff);
108521c5ea95SDimitris Michailidis 	fec->fec = to_ethtool_fec(fec_data >> 8);
108621c5ea95SDimitris Michailidis 	return 0;
108721c5ea95SDimitris Michailidis }
108821c5ea95SDimitris Michailidis 
fun_set_fecparam(struct net_device * netdev,struct ethtool_fecparam * fec)108921c5ea95SDimitris Michailidis static int fun_set_fecparam(struct net_device *netdev,
109021c5ea95SDimitris Michailidis 			    struct ethtool_fecparam *fec)
109121c5ea95SDimitris Michailidis {
109221c5ea95SDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
109321c5ea95SDimitris Michailidis 	u64 fec_mode;
109421c5ea95SDimitris Michailidis 
109521c5ea95SDimitris Michailidis 	switch (fec->fec) {
109621c5ea95SDimitris Michailidis 	case ETHTOOL_FEC_AUTO:
109721c5ea95SDimitris Michailidis 		fec_mode = FUN_PORT_FEC_AUTO;
109821c5ea95SDimitris Michailidis 		break;
109921c5ea95SDimitris Michailidis 	case ETHTOOL_FEC_OFF:
110021c5ea95SDimitris Michailidis 		if (!(fp->port_caps & FUN_PORT_CAP_FEC_NONE))
110121c5ea95SDimitris Michailidis 			return -EINVAL;
110221c5ea95SDimitris Michailidis 		fec_mode = FUN_PORT_FEC_OFF;
110321c5ea95SDimitris Michailidis 		break;
110421c5ea95SDimitris Michailidis 	case ETHTOOL_FEC_BASER:
110521c5ea95SDimitris Michailidis 		if (!(fp->port_caps & FUN_PORT_CAP_FEC_FC))
110621c5ea95SDimitris Michailidis 			return -EINVAL;
110721c5ea95SDimitris Michailidis 		fec_mode = FUN_PORT_FEC_FC;
110821c5ea95SDimitris Michailidis 		break;
110921c5ea95SDimitris Michailidis 	case ETHTOOL_FEC_RS:
111021c5ea95SDimitris Michailidis 		if (!(fp->port_caps & FUN_PORT_CAP_FEC_RS))
111121c5ea95SDimitris Michailidis 			return -EINVAL;
111221c5ea95SDimitris Michailidis 		fec_mode = FUN_PORT_FEC_RS;
111321c5ea95SDimitris Michailidis 		break;
111421c5ea95SDimitris Michailidis 	default:
111521c5ea95SDimitris Michailidis 		return -EINVAL;
111621c5ea95SDimitris Michailidis 	}
111721c5ea95SDimitris Michailidis 
111821c5ea95SDimitris Michailidis 	return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_FEC, fec_mode);
111921c5ea95SDimitris Michailidis }
112021c5ea95SDimitris Michailidis 
fun_get_port_module_page(struct net_device * netdev,const struct ethtool_module_eeprom * req,struct netlink_ext_ack * extack)1121f03c8a1eSDimitris Michailidis static int fun_get_port_module_page(struct net_device *netdev,
1122f03c8a1eSDimitris Michailidis 				    const struct ethtool_module_eeprom *req,
1123f03c8a1eSDimitris Michailidis 				    struct netlink_ext_ack *extack)
1124f03c8a1eSDimitris Michailidis {
1125f03c8a1eSDimitris Michailidis 	union {
1126f03c8a1eSDimitris Michailidis 		struct fun_admin_port_req req;
1127f03c8a1eSDimitris Michailidis 		struct fun_admin_port_xcvr_read_rsp rsp;
1128f03c8a1eSDimitris Michailidis 	} cmd;
1129f03c8a1eSDimitris Michailidis 	struct funeth_priv *fp = netdev_priv(netdev);
1130f03c8a1eSDimitris Michailidis 	int rc;
1131f03c8a1eSDimitris Michailidis 
1132f03c8a1eSDimitris Michailidis 	if (fp->port_caps & FUN_PORT_CAP_VPORT) {
1133f03c8a1eSDimitris Michailidis 		NL_SET_ERR_MSG_MOD(extack,
1134f03c8a1eSDimitris Michailidis 				   "Specified port is virtual, only physical ports have modules");
1135f03c8a1eSDimitris Michailidis 		return -EOPNOTSUPP;
1136f03c8a1eSDimitris Michailidis 	}
1137f03c8a1eSDimitris Michailidis 
1138f03c8a1eSDimitris Michailidis 	cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_PORT,
1139f03c8a1eSDimitris Michailidis 						    sizeof(cmd.req));
1140f03c8a1eSDimitris Michailidis 	cmd.req.u.xcvr_read =
1141f03c8a1eSDimitris Michailidis 		FUN_ADMIN_PORT_XCVR_READ_REQ_INIT(0, netdev->dev_port,
1142f03c8a1eSDimitris Michailidis 						  req->bank, req->page,
1143f03c8a1eSDimitris Michailidis 						  req->offset, req->length,
1144f03c8a1eSDimitris Michailidis 						  req->i2c_address);
1145f03c8a1eSDimitris Michailidis 	rc = fun_submit_admin_sync_cmd(fp->fdev, &cmd.req.common, &cmd.rsp,
1146f03c8a1eSDimitris Michailidis 				       sizeof(cmd.rsp), 0);
1147f03c8a1eSDimitris Michailidis 	if (rc)
1148f03c8a1eSDimitris Michailidis 		return rc;
1149f03c8a1eSDimitris Michailidis 
1150f03c8a1eSDimitris Michailidis 	memcpy(req->data, cmd.rsp.data, req->length);
1151f03c8a1eSDimitris Michailidis 	return req->length;
1152f03c8a1eSDimitris Michailidis }
1153f03c8a1eSDimitris Michailidis 
115421c5ea95SDimitris Michailidis static const struct ethtool_ops fun_ethtool_ops = {
115521c5ea95SDimitris Michailidis 	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
115621c5ea95SDimitris Michailidis 				     ETHTOOL_COALESCE_MAX_FRAMES,
115721c5ea95SDimitris Michailidis 	.get_link_ksettings  = fun_get_link_ksettings,
115821c5ea95SDimitris Michailidis 	.set_link_ksettings  = fun_set_link_ksettings,
115921c5ea95SDimitris Michailidis 	.set_phys_id         = fun_set_phys_id,
116021c5ea95SDimitris Michailidis 	.get_drvinfo         = fun_get_drvinfo,
116121c5ea95SDimitris Michailidis 	.get_msglevel        = fun_get_msglevel,
116221c5ea95SDimitris Michailidis 	.set_msglevel        = fun_set_msglevel,
116321c5ea95SDimitris Michailidis 	.get_regs_len        = fun_get_regs_len,
116421c5ea95SDimitris Michailidis 	.get_regs            = fun_get_regs,
116521c5ea95SDimitris Michailidis 	.get_link	     = ethtool_op_get_link,
116621c5ea95SDimitris Michailidis 	.get_coalesce        = fun_get_coalesce,
116721c5ea95SDimitris Michailidis 	.set_coalesce        = fun_set_coalesce,
116821c5ea95SDimitris Michailidis 	.get_ts_info         = fun_get_ts_info,
116921c5ea95SDimitris Michailidis 	.get_ringparam       = fun_get_ringparam,
117021c5ea95SDimitris Michailidis 	.set_ringparam       = fun_set_ringparam,
117121c5ea95SDimitris Michailidis 	.get_sset_count      = fun_get_sset_count,
117221c5ea95SDimitris Michailidis 	.get_strings         = fun_get_strings,
117321c5ea95SDimitris Michailidis 	.get_ethtool_stats   = fun_get_ethtool_stats,
117421c5ea95SDimitris Michailidis 	.get_rxnfc	     = fun_get_rxnfc,
117521c5ea95SDimitris Michailidis 	.set_rxnfc           = fun_set_rxnfc,
117621c5ea95SDimitris Michailidis 	.get_rxfh_indir_size = fun_get_rxfh_indir_size,
117721c5ea95SDimitris Michailidis 	.get_rxfh_key_size   = fun_get_rxfh_key_size,
117821c5ea95SDimitris Michailidis 	.get_rxfh            = fun_get_rxfh,
117921c5ea95SDimitris Michailidis 	.set_rxfh            = fun_set_rxfh,
118021c5ea95SDimitris Michailidis 	.get_channels        = fun_get_channels,
118121c5ea95SDimitris Michailidis 	.set_channels        = fun_set_channels,
118221c5ea95SDimitris Michailidis 	.get_fecparam	     = fun_get_fecparam,
118321c5ea95SDimitris Michailidis 	.set_fecparam	     = fun_set_fecparam,
118421c5ea95SDimitris Michailidis 	.get_pauseparam      = fun_get_pauseparam,
118521c5ea95SDimitris Michailidis 	.set_pauseparam      = fun_set_pauseparam,
118621c5ea95SDimitris Michailidis 	.nway_reset          = fun_restart_an,
118721c5ea95SDimitris Michailidis 	.get_pause_stats     = fun_get_pause_stats,
118821c5ea95SDimitris Michailidis 	.get_fec_stats       = fun_get_fec_stats,
118921c5ea95SDimitris Michailidis 	.get_eth_mac_stats   = fun_get_802_3_stats,
119021c5ea95SDimitris Michailidis 	.get_eth_ctrl_stats  = fun_get_802_3_ctrl_stats,
119121c5ea95SDimitris Michailidis 	.get_rmon_stats      = fun_get_rmon_stats,
1192f03c8a1eSDimitris Michailidis 	.get_module_eeprom_by_page = fun_get_port_module_page,
119321c5ea95SDimitris Michailidis };
119421c5ea95SDimitris Michailidis 
fun_set_ethtool_ops(struct net_device * netdev)119521c5ea95SDimitris Michailidis void fun_set_ethtool_ops(struct net_device *netdev)
119621c5ea95SDimitris Michailidis {
119721c5ea95SDimitris Michailidis 	netdev->ethtool_ops = &fun_ethtool_ops;
119821c5ea95SDimitris Michailidis }
1199