162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/ethtool.h>
462306a36Sopenharmony_ci#include <linux/linkmode.h>
562306a36Sopenharmony_ci#include <linux/netdevice.h>
662306a36Sopenharmony_ci#include <linux/nvme.h>
762306a36Sopenharmony_ci#include <linux/io.h>
862306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h>
962306a36Sopenharmony_ci#include <linux/pci.h>
1062306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1162306a36Sopenharmony_ci#include "funeth.h"
1262306a36Sopenharmony_ci#include "fun_port.h"
1362306a36Sopenharmony_ci#include "funeth_txrx.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* Min queue depth. The smallest power-of-2 supporting jumbo frames with 4K
1662306a36Sopenharmony_ci * pages is 8. Require it for all types of queues though some could work with
1762306a36Sopenharmony_ci * fewer entries.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci#define FUNETH_MIN_QDEPTH 8
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const char mac_tx_stat_names[][ETH_GSTRING_LEN] = {
2262306a36Sopenharmony_ci	"mac_tx_octets_total",
2362306a36Sopenharmony_ci	"mac_tx_frames_total",
2462306a36Sopenharmony_ci	"mac_tx_vlan_frames_ok",
2562306a36Sopenharmony_ci	"mac_tx_unicast_frames",
2662306a36Sopenharmony_ci	"mac_tx_multicast_frames",
2762306a36Sopenharmony_ci	"mac_tx_broadcast_frames",
2862306a36Sopenharmony_ci	"mac_tx_errors",
2962306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE0",
3062306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE1",
3162306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE2",
3262306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE3",
3362306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE4",
3462306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE5",
3562306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE6",
3662306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE7",
3762306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE8",
3862306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE9",
3962306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE10",
4062306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE11",
4162306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE12",
4262306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE13",
4362306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE14",
4462306a36Sopenharmony_ci	"mac_tx_CBFCPAUSE15",
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic const char mac_rx_stat_names[][ETH_GSTRING_LEN] = {
4862306a36Sopenharmony_ci	"mac_rx_octets_total",
4962306a36Sopenharmony_ci	"mac_rx_frames_total",
5062306a36Sopenharmony_ci	"mac_rx_VLAN_frames_ok",
5162306a36Sopenharmony_ci	"mac_rx_unicast_frames",
5262306a36Sopenharmony_ci	"mac_rx_multicast_frames",
5362306a36Sopenharmony_ci	"mac_rx_broadcast_frames",
5462306a36Sopenharmony_ci	"mac_rx_drop_events",
5562306a36Sopenharmony_ci	"mac_rx_errors",
5662306a36Sopenharmony_ci	"mac_rx_alignment_errors",
5762306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE0",
5862306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE1",
5962306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE2",
6062306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE3",
6162306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE4",
6262306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE5",
6362306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE6",
6462306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE7",
6562306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE8",
6662306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE9",
6762306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE10",
6862306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE11",
6962306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE12",
7062306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE13",
7162306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE14",
7262306a36Sopenharmony_ci	"mac_rx_CBFCPAUSE15",
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic const char * const txq_stat_names[] = {
7662306a36Sopenharmony_ci	"tx_pkts",
7762306a36Sopenharmony_ci	"tx_bytes",
7862306a36Sopenharmony_ci	"tx_cso",
7962306a36Sopenharmony_ci	"tx_tso",
8062306a36Sopenharmony_ci	"tx_encapsulated_tso",
8162306a36Sopenharmony_ci	"tx_uso",
8262306a36Sopenharmony_ci	"tx_more",
8362306a36Sopenharmony_ci	"tx_queue_stops",
8462306a36Sopenharmony_ci	"tx_queue_restarts",
8562306a36Sopenharmony_ci	"tx_mapping_errors",
8662306a36Sopenharmony_ci	"tx_tls_encrypted_packets",
8762306a36Sopenharmony_ci	"tx_tls_encrypted_bytes",
8862306a36Sopenharmony_ci	"tx_tls_ooo",
8962306a36Sopenharmony_ci	"tx_tls_drop_no_sync_data",
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic const char * const xdpq_stat_names[] = {
9362306a36Sopenharmony_ci	"tx_xdp_pkts",
9462306a36Sopenharmony_ci	"tx_xdp_bytes",
9562306a36Sopenharmony_ci	"tx_xdp_full",
9662306a36Sopenharmony_ci	"tx_xdp_mapping_errors",
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const char * const rxq_stat_names[] = {
10062306a36Sopenharmony_ci	"rx_pkts",
10162306a36Sopenharmony_ci	"rx_bytes",
10262306a36Sopenharmony_ci	"rx_cso",
10362306a36Sopenharmony_ci	"gro_pkts",
10462306a36Sopenharmony_ci	"gro_merged",
10562306a36Sopenharmony_ci	"rx_xdp_tx",
10662306a36Sopenharmony_ci	"rx_xdp_redir",
10762306a36Sopenharmony_ci	"rx_xdp_drops",
10862306a36Sopenharmony_ci	"rx_buffers",
10962306a36Sopenharmony_ci	"rx_page_allocs",
11062306a36Sopenharmony_ci	"rx_drops",
11162306a36Sopenharmony_ci	"rx_budget_exhausted",
11262306a36Sopenharmony_ci	"rx_mapping_errors",
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic const char * const tls_stat_names[] = {
11662306a36Sopenharmony_ci	"tx_tls_ctx",
11762306a36Sopenharmony_ci	"tx_tls_del",
11862306a36Sopenharmony_ci	"tx_tls_resync",
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void fun_link_modes_to_ethtool(u64 modes,
12262306a36Sopenharmony_ci				      unsigned long *ethtool_modes_map)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci#define ADD_LINK_MODE(mode) \
12562306a36Sopenharmony_ci	__set_bit(ETHTOOL_LINK_MODE_ ## mode ## _BIT, ethtool_modes_map)
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_AUTONEG)
12862306a36Sopenharmony_ci		ADD_LINK_MODE(Autoneg);
12962306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_1000_X)
13062306a36Sopenharmony_ci		ADD_LINK_MODE(1000baseX_Full);
13162306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_10G_R) {
13262306a36Sopenharmony_ci		ADD_LINK_MODE(10000baseCR_Full);
13362306a36Sopenharmony_ci		ADD_LINK_MODE(10000baseSR_Full);
13462306a36Sopenharmony_ci		ADD_LINK_MODE(10000baseLR_Full);
13562306a36Sopenharmony_ci		ADD_LINK_MODE(10000baseER_Full);
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_25G_R) {
13862306a36Sopenharmony_ci		ADD_LINK_MODE(25000baseCR_Full);
13962306a36Sopenharmony_ci		ADD_LINK_MODE(25000baseSR_Full);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_40G_R4) {
14262306a36Sopenharmony_ci		ADD_LINK_MODE(40000baseCR4_Full);
14362306a36Sopenharmony_ci		ADD_LINK_MODE(40000baseSR4_Full);
14462306a36Sopenharmony_ci		ADD_LINK_MODE(40000baseLR4_Full);
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_50G_R2) {
14762306a36Sopenharmony_ci		ADD_LINK_MODE(50000baseCR2_Full);
14862306a36Sopenharmony_ci		ADD_LINK_MODE(50000baseSR2_Full);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_50G_R) {
15162306a36Sopenharmony_ci		ADD_LINK_MODE(50000baseCR_Full);
15262306a36Sopenharmony_ci		ADD_LINK_MODE(50000baseSR_Full);
15362306a36Sopenharmony_ci		ADD_LINK_MODE(50000baseLR_ER_FR_Full);
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_100G_R4) {
15662306a36Sopenharmony_ci		ADD_LINK_MODE(100000baseCR4_Full);
15762306a36Sopenharmony_ci		ADD_LINK_MODE(100000baseSR4_Full);
15862306a36Sopenharmony_ci		ADD_LINK_MODE(100000baseLR4_ER4_Full);
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_100G_R2) {
16162306a36Sopenharmony_ci		ADD_LINK_MODE(100000baseCR2_Full);
16262306a36Sopenharmony_ci		ADD_LINK_MODE(100000baseSR2_Full);
16362306a36Sopenharmony_ci		ADD_LINK_MODE(100000baseLR2_ER2_FR2_Full);
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_FEC_NONE)
16662306a36Sopenharmony_ci		ADD_LINK_MODE(FEC_NONE);
16762306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_FEC_FC)
16862306a36Sopenharmony_ci		ADD_LINK_MODE(FEC_BASER);
16962306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_FEC_RS)
17062306a36Sopenharmony_ci		ADD_LINK_MODE(FEC_RS);
17162306a36Sopenharmony_ci	if (modes & FUN_PORT_CAP_RX_PAUSE)
17262306a36Sopenharmony_ci		ADD_LINK_MODE(Pause);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci#undef ADD_LINK_MODE
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void set_asym_pause(u64 advertising, struct ethtool_link_ksettings *ks)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	bool rx_pause, tx_pause;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	rx_pause = advertising & FUN_PORT_CAP_RX_PAUSE;
18262306a36Sopenharmony_ci	tx_pause = advertising & FUN_PORT_CAP_TX_PAUSE;
18362306a36Sopenharmony_ci	if (tx_pause ^ rx_pause)
18462306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(ks, advertising,
18562306a36Sopenharmony_ci						     Asym_Pause);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic unsigned int fun_port_type(unsigned int xcvr)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	if (!xcvr)
19162306a36Sopenharmony_ci		return PORT_NONE;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	switch (xcvr & 7) {
19462306a36Sopenharmony_ci	case FUN_XCVR_BASET:
19562306a36Sopenharmony_ci		return PORT_TP;
19662306a36Sopenharmony_ci	case FUN_XCVR_CU:
19762306a36Sopenharmony_ci		return PORT_DA;
19862306a36Sopenharmony_ci	default:
19962306a36Sopenharmony_ci		return PORT_FIBRE;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int fun_get_link_ksettings(struct net_device *netdev,
20462306a36Sopenharmony_ci				  struct ethtool_link_ksettings *ks)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
20762306a36Sopenharmony_ci	unsigned int seq, speed, xcvr;
20862306a36Sopenharmony_ci	u64 lp_advertising;
20962306a36Sopenharmony_ci	bool link_up;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(ks, supported);
21262306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(ks, advertising);
21362306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(ks, lp_advertising);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Link settings change asynchronously, take a consistent snapshot */
21662306a36Sopenharmony_ci	do {
21762306a36Sopenharmony_ci		seq = read_seqcount_begin(&fp->link_seq);
21862306a36Sopenharmony_ci		link_up = netif_carrier_ok(netdev);
21962306a36Sopenharmony_ci		speed = fp->link_speed;
22062306a36Sopenharmony_ci		xcvr = fp->xcvr_type;
22162306a36Sopenharmony_ci		lp_advertising = fp->lp_advertising;
22262306a36Sopenharmony_ci	} while (read_seqcount_retry(&fp->link_seq, seq));
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (link_up) {
22562306a36Sopenharmony_ci		ks->base.speed = speed;
22662306a36Sopenharmony_ci		ks->base.duplex = DUPLEX_FULL;
22762306a36Sopenharmony_ci		fun_link_modes_to_ethtool(lp_advertising,
22862306a36Sopenharmony_ci					  ks->link_modes.lp_advertising);
22962306a36Sopenharmony_ci	} else {
23062306a36Sopenharmony_ci		ks->base.speed = SPEED_UNKNOWN;
23162306a36Sopenharmony_ci		ks->base.duplex = DUPLEX_UNKNOWN;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	ks->base.autoneg = (fp->advertising & FUN_PORT_CAP_AUTONEG) ?
23562306a36Sopenharmony_ci			   AUTONEG_ENABLE : AUTONEG_DISABLE;
23662306a36Sopenharmony_ci	ks->base.port = fun_port_type(xcvr);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	fun_link_modes_to_ethtool(fp->port_caps, ks->link_modes.supported);
23962306a36Sopenharmony_ci	if (fp->port_caps & (FUN_PORT_CAP_RX_PAUSE | FUN_PORT_CAP_TX_PAUSE))
24062306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(ks, supported, Asym_Pause);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	fun_link_modes_to_ethtool(fp->advertising, ks->link_modes.advertising);
24362306a36Sopenharmony_ci	set_asym_pause(fp->advertising, ks);
24462306a36Sopenharmony_ci	return 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic u64 fun_advert_modes(const struct ethtool_link_ksettings *ks)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	u64 modes = 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci#define HAS_MODE(mode) \
25262306a36Sopenharmony_ci	ethtool_link_ksettings_test_link_mode(ks, advertising, mode)
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (HAS_MODE(1000baseX_Full))
25562306a36Sopenharmony_ci		modes |= FUN_PORT_CAP_1000_X;
25662306a36Sopenharmony_ci	if (HAS_MODE(10000baseCR_Full) || HAS_MODE(10000baseSR_Full) ||
25762306a36Sopenharmony_ci	    HAS_MODE(10000baseLR_Full) || HAS_MODE(10000baseER_Full))
25862306a36Sopenharmony_ci		modes |= FUN_PORT_CAP_10G_R;
25962306a36Sopenharmony_ci	if (HAS_MODE(25000baseCR_Full) || HAS_MODE(25000baseSR_Full))
26062306a36Sopenharmony_ci		modes |= FUN_PORT_CAP_25G_R;
26162306a36Sopenharmony_ci	if (HAS_MODE(40000baseCR4_Full) || HAS_MODE(40000baseSR4_Full) ||
26262306a36Sopenharmony_ci	    HAS_MODE(40000baseLR4_Full))
26362306a36Sopenharmony_ci		modes |= FUN_PORT_CAP_40G_R4;
26462306a36Sopenharmony_ci	if (HAS_MODE(50000baseCR2_Full) || HAS_MODE(50000baseSR2_Full))
26562306a36Sopenharmony_ci		modes |= FUN_PORT_CAP_50G_R2;
26662306a36Sopenharmony_ci	if (HAS_MODE(50000baseCR_Full) || HAS_MODE(50000baseSR_Full) ||
26762306a36Sopenharmony_ci	    HAS_MODE(50000baseLR_ER_FR_Full))
26862306a36Sopenharmony_ci		modes |= FUN_PORT_CAP_50G_R;
26962306a36Sopenharmony_ci	if (HAS_MODE(100000baseCR4_Full) || HAS_MODE(100000baseSR4_Full) ||
27062306a36Sopenharmony_ci	    HAS_MODE(100000baseLR4_ER4_Full))
27162306a36Sopenharmony_ci		modes |= FUN_PORT_CAP_100G_R4;
27262306a36Sopenharmony_ci	if (HAS_MODE(100000baseCR2_Full) || HAS_MODE(100000baseSR2_Full) ||
27362306a36Sopenharmony_ci	    HAS_MODE(100000baseLR2_ER2_FR2_Full))
27462306a36Sopenharmony_ci		modes |= FUN_PORT_CAP_100G_R2;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return modes;
27762306a36Sopenharmony_ci#undef HAS_MODE
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic u64 fun_speed_to_link_mode(unsigned int speed)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	switch (speed) {
28362306a36Sopenharmony_ci	case SPEED_100000:
28462306a36Sopenharmony_ci		return FUN_PORT_CAP_100G_R4 | FUN_PORT_CAP_100G_R2;
28562306a36Sopenharmony_ci	case SPEED_50000:
28662306a36Sopenharmony_ci		return FUN_PORT_CAP_50G_R | FUN_PORT_CAP_50G_R2;
28762306a36Sopenharmony_ci	case SPEED_40000:
28862306a36Sopenharmony_ci		return FUN_PORT_CAP_40G_R4;
28962306a36Sopenharmony_ci	case SPEED_25000:
29062306a36Sopenharmony_ci		return FUN_PORT_CAP_25G_R;
29162306a36Sopenharmony_ci	case SPEED_10000:
29262306a36Sopenharmony_ci		return FUN_PORT_CAP_10G_R;
29362306a36Sopenharmony_ci	case SPEED_1000:
29462306a36Sopenharmony_ci		return FUN_PORT_CAP_1000_X;
29562306a36Sopenharmony_ci	default:
29662306a36Sopenharmony_ci		return 0;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int fun_change_advert(struct funeth_priv *fp, u64 new_advert)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	int err;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (new_advert == fp->advertising)
30562306a36Sopenharmony_ci		return 0;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	err = fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_ADVERT, new_advert);
30862306a36Sopenharmony_ci	if (!err)
30962306a36Sopenharmony_ci		fp->advertising = new_advert;
31062306a36Sopenharmony_ci	return err;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci#define FUN_PORT_CAP_FEC_MASK \
31462306a36Sopenharmony_ci	(FUN_PORT_CAP_FEC_NONE | FUN_PORT_CAP_FEC_FC | FUN_PORT_CAP_FEC_RS)
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic int fun_set_link_ksettings(struct net_device *netdev,
31762306a36Sopenharmony_ci				  const struct ethtool_link_ksettings *ks)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = {};
32062306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
32162306a36Sopenharmony_ci	u64 new_advert;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* eswitch ports don't support mode changes */
32462306a36Sopenharmony_ci	if (fp->port_caps & FUN_PORT_CAP_VPORT)
32562306a36Sopenharmony_ci		return -EOPNOTSUPP;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (ks->base.duplex == DUPLEX_HALF)
32862306a36Sopenharmony_ci		return -EINVAL;
32962306a36Sopenharmony_ci	if (ks->base.autoneg == AUTONEG_ENABLE &&
33062306a36Sopenharmony_ci	    !(fp->port_caps & FUN_PORT_CAP_AUTONEG))
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (ks->base.autoneg == AUTONEG_ENABLE) {
33462306a36Sopenharmony_ci		if (linkmode_empty(ks->link_modes.advertising))
33562306a36Sopenharmony_ci			return -EINVAL;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		fun_link_modes_to_ethtool(fp->port_caps, supported);
33862306a36Sopenharmony_ci		if (!linkmode_subset(ks->link_modes.advertising, supported))
33962306a36Sopenharmony_ci			return -EINVAL;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		new_advert = fun_advert_modes(ks) | FUN_PORT_CAP_AUTONEG;
34262306a36Sopenharmony_ci	} else {
34362306a36Sopenharmony_ci		new_advert = fun_speed_to_link_mode(ks->base.speed);
34462306a36Sopenharmony_ci		new_advert &= fp->port_caps;
34562306a36Sopenharmony_ci		if (!new_advert)
34662306a36Sopenharmony_ci			return -EINVAL;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	new_advert |= fp->advertising &
34962306a36Sopenharmony_ci		      (FUN_PORT_CAP_PAUSE_MASK | FUN_PORT_CAP_FEC_MASK);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return fun_change_advert(fp, new_advert);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void fun_get_pauseparam(struct net_device *netdev,
35562306a36Sopenharmony_ci			       struct ethtool_pauseparam *pause)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
35862306a36Sopenharmony_ci	u8 active_pause = fp->active_fc;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	pause->rx_pause = !!(active_pause & FUN_PORT_CAP_RX_PAUSE);
36162306a36Sopenharmony_ci	pause->tx_pause = !!(active_pause & FUN_PORT_CAP_TX_PAUSE);
36262306a36Sopenharmony_ci	pause->autoneg = !!(fp->advertising & FUN_PORT_CAP_AUTONEG);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int fun_set_pauseparam(struct net_device *netdev,
36662306a36Sopenharmony_ci			      struct ethtool_pauseparam *pause)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
36962306a36Sopenharmony_ci	u64 new_advert;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (fp->port_caps & FUN_PORT_CAP_VPORT)
37262306a36Sopenharmony_ci		return -EOPNOTSUPP;
37362306a36Sopenharmony_ci	/* Forcing PAUSE settings with AN enabled is unsupported. */
37462306a36Sopenharmony_ci	if (!pause->autoneg && (fp->advertising & FUN_PORT_CAP_AUTONEG))
37562306a36Sopenharmony_ci		return -EOPNOTSUPP;
37662306a36Sopenharmony_ci	if (pause->autoneg && !(fp->advertising & FUN_PORT_CAP_AUTONEG))
37762306a36Sopenharmony_ci		return -EINVAL;
37862306a36Sopenharmony_ci	if (pause->tx_pause && !(fp->port_caps & FUN_PORT_CAP_TX_PAUSE))
37962306a36Sopenharmony_ci		return -EINVAL;
38062306a36Sopenharmony_ci	if (pause->rx_pause && !(fp->port_caps & FUN_PORT_CAP_RX_PAUSE))
38162306a36Sopenharmony_ci		return -EINVAL;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	new_advert = fp->advertising & ~FUN_PORT_CAP_PAUSE_MASK;
38462306a36Sopenharmony_ci	if (pause->tx_pause)
38562306a36Sopenharmony_ci		new_advert |= FUN_PORT_CAP_TX_PAUSE;
38662306a36Sopenharmony_ci	if (pause->rx_pause)
38762306a36Sopenharmony_ci		new_advert |= FUN_PORT_CAP_RX_PAUSE;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	return fun_change_advert(fp, new_advert);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int fun_restart_an(struct net_device *netdev)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (!(fp->advertising & FUN_PORT_CAP_AUTONEG))
39762306a36Sopenharmony_ci		return -EOPNOTSUPP;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_ADVERT,
40062306a36Sopenharmony_ci				  FUN_PORT_CAP_AUTONEG);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int fun_set_phys_id(struct net_device *netdev,
40462306a36Sopenharmony_ci			   enum ethtool_phys_id_state state)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
40762306a36Sopenharmony_ci	unsigned int beacon;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (fp->port_caps & FUN_PORT_CAP_VPORT)
41062306a36Sopenharmony_ci		return -EOPNOTSUPP;
41162306a36Sopenharmony_ci	if (state != ETHTOOL_ID_ACTIVE && state != ETHTOOL_ID_INACTIVE)
41262306a36Sopenharmony_ci		return -EOPNOTSUPP;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	beacon = state == ETHTOOL_ID_ACTIVE ? FUN_PORT_LED_BEACON_ON :
41562306a36Sopenharmony_ci					      FUN_PORT_LED_BEACON_OFF;
41662306a36Sopenharmony_ci	return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_LED, beacon);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic void fun_get_drvinfo(struct net_device *netdev,
42062306a36Sopenharmony_ci			    struct ethtool_drvinfo *info)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
42562306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(fp->pdev), sizeof(info->bus_info));
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic u32 fun_get_msglevel(struct net_device *netdev)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return fp->msg_enable;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void fun_set_msglevel(struct net_device *netdev, u32 value)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	fp->msg_enable = value;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int fun_get_regs_len(struct net_device *dev)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	return NVME_REG_ACQ + sizeof(u64);
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void fun_get_regs(struct net_device *dev, struct ethtool_regs *regs,
44862306a36Sopenharmony_ci			 void *buf)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(dev);
45162306a36Sopenharmony_ci	void __iomem *bar = fp->fdev->bar;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	regs->version = 0;
45462306a36Sopenharmony_ci	*(u64 *)(buf + NVME_REG_CAP)   = readq(bar + NVME_REG_CAP);
45562306a36Sopenharmony_ci	*(u32 *)(buf + NVME_REG_VS)    = readl(bar + NVME_REG_VS);
45662306a36Sopenharmony_ci	*(u32 *)(buf + NVME_REG_INTMS) = readl(bar + NVME_REG_INTMS);
45762306a36Sopenharmony_ci	*(u32 *)(buf + NVME_REG_INTMC) = readl(bar + NVME_REG_INTMC);
45862306a36Sopenharmony_ci	*(u32 *)(buf + NVME_REG_CC)    = readl(bar + NVME_REG_CC);
45962306a36Sopenharmony_ci	*(u32 *)(buf + NVME_REG_CSTS)  = readl(bar + NVME_REG_CSTS);
46062306a36Sopenharmony_ci	*(u32 *)(buf + NVME_REG_AQA)   = readl(bar + NVME_REG_AQA);
46162306a36Sopenharmony_ci	*(u64 *)(buf + NVME_REG_ASQ)   = readq(bar + NVME_REG_ASQ);
46262306a36Sopenharmony_ci	*(u64 *)(buf + NVME_REG_ACQ)   = readq(bar + NVME_REG_ACQ);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int fun_get_coalesce(struct net_device *netdev,
46662306a36Sopenharmony_ci			    struct ethtool_coalesce *coal,
46762306a36Sopenharmony_ci			    struct kernel_ethtool_coalesce *kcoal,
46862306a36Sopenharmony_ci			    struct netlink_ext_ack *ext_ack)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	coal->rx_coalesce_usecs        = fp->rx_coal_usec;
47362306a36Sopenharmony_ci	coal->rx_max_coalesced_frames  = fp->rx_coal_count;
47462306a36Sopenharmony_ci	coal->use_adaptive_rx_coalesce = !fp->cq_irq_db;
47562306a36Sopenharmony_ci	coal->tx_coalesce_usecs        = fp->tx_coal_usec;
47662306a36Sopenharmony_ci	coal->tx_max_coalesced_frames  = fp->tx_coal_count;
47762306a36Sopenharmony_ci	return 0;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int fun_set_coalesce(struct net_device *netdev,
48162306a36Sopenharmony_ci			    struct ethtool_coalesce *coal,
48262306a36Sopenharmony_ci			    struct kernel_ethtool_coalesce *kcoal,
48362306a36Sopenharmony_ci			    struct netlink_ext_ack *ext_ack)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
48662306a36Sopenharmony_ci	struct funeth_rxq **rxqs;
48762306a36Sopenharmony_ci	unsigned int i, db_val;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (coal->rx_coalesce_usecs > FUN_DB_INTCOAL_USEC_M ||
49062306a36Sopenharmony_ci	    coal->rx_max_coalesced_frames > FUN_DB_INTCOAL_ENTRIES_M ||
49162306a36Sopenharmony_ci	    (coal->rx_coalesce_usecs | coal->rx_max_coalesced_frames) == 0 ||
49262306a36Sopenharmony_ci	    coal->tx_coalesce_usecs > FUN_DB_INTCOAL_USEC_M ||
49362306a36Sopenharmony_ci	    coal->tx_max_coalesced_frames > FUN_DB_INTCOAL_ENTRIES_M ||
49462306a36Sopenharmony_ci	    (coal->tx_coalesce_usecs | coal->tx_max_coalesced_frames) == 0)
49562306a36Sopenharmony_ci		return -EINVAL;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* a timer is required if there's any coalescing */
49862306a36Sopenharmony_ci	if ((coal->rx_max_coalesced_frames > 1 && !coal->rx_coalesce_usecs) ||
49962306a36Sopenharmony_ci	    (coal->tx_max_coalesced_frames > 1 && !coal->tx_coalesce_usecs))
50062306a36Sopenharmony_ci		return -EINVAL;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	fp->rx_coal_usec  = coal->rx_coalesce_usecs;
50362306a36Sopenharmony_ci	fp->rx_coal_count = coal->rx_max_coalesced_frames;
50462306a36Sopenharmony_ci	fp->tx_coal_usec  = coal->tx_coalesce_usecs;
50562306a36Sopenharmony_ci	fp->tx_coal_count = coal->tx_max_coalesced_frames;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	db_val = FUN_IRQ_CQ_DB(fp->rx_coal_usec, fp->rx_coal_count);
50862306a36Sopenharmony_ci	WRITE_ONCE(fp->cq_irq_db, db_val);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	rxqs = rtnl_dereference(fp->rxqs);
51162306a36Sopenharmony_ci	if (!rxqs)
51262306a36Sopenharmony_ci		return 0;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	for (i = 0; i < netdev->real_num_rx_queues; i++)
51562306a36Sopenharmony_ci		WRITE_ONCE(rxqs[i]->irq_db_val, db_val);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	db_val = FUN_IRQ_SQ_DB(fp->tx_coal_usec, fp->tx_coal_count);
51862306a36Sopenharmony_ci	for (i = 0; i < netdev->real_num_tx_queues; i++)
51962306a36Sopenharmony_ci		WRITE_ONCE(fp->txqs[i]->irq_db_val, db_val);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic void fun_get_channels(struct net_device *netdev,
52562306a36Sopenharmony_ci			     struct ethtool_channels *chan)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	chan->max_rx   = netdev->num_rx_queues;
52862306a36Sopenharmony_ci	chan->rx_count = netdev->real_num_rx_queues;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	chan->max_tx   = netdev->num_tx_queues;
53162306a36Sopenharmony_ci	chan->tx_count = netdev->real_num_tx_queues;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic int fun_set_channels(struct net_device *netdev,
53562306a36Sopenharmony_ci			    struct ethtool_channels *chan)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	if (!chan->tx_count || !chan->rx_count)
53862306a36Sopenharmony_ci		return -EINVAL;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (chan->tx_count == netdev->real_num_tx_queues &&
54162306a36Sopenharmony_ci	    chan->rx_count == netdev->real_num_rx_queues)
54262306a36Sopenharmony_ci		return 0;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (netif_running(netdev))
54562306a36Sopenharmony_ci		return fun_change_num_queues(netdev, chan->tx_count,
54662306a36Sopenharmony_ci					     chan->rx_count);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	fun_set_ring_count(netdev, chan->tx_count, chan->rx_count);
54962306a36Sopenharmony_ci	return 0;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic void fun_get_ringparam(struct net_device *netdev,
55362306a36Sopenharmony_ci			      struct ethtool_ringparam *ring,
55462306a36Sopenharmony_ci			      struct kernel_ethtool_ringparam *kring,
55562306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
55862306a36Sopenharmony_ci	unsigned int max_depth = fp->fdev->q_depth;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* We size CQs to be twice the RQ depth so max RQ depth is half the
56162306a36Sopenharmony_ci	 * max queue depth.
56262306a36Sopenharmony_ci	 */
56362306a36Sopenharmony_ci	ring->rx_max_pending = max_depth / 2;
56462306a36Sopenharmony_ci	ring->tx_max_pending = max_depth;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	ring->rx_pending = fp->rq_depth;
56762306a36Sopenharmony_ci	ring->tx_pending = fp->sq_depth;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	kring->rx_buf_len = PAGE_SIZE;
57062306a36Sopenharmony_ci	kring->cqe_size = FUNETH_CQE_SIZE;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int fun_set_ringparam(struct net_device *netdev,
57462306a36Sopenharmony_ci			     struct ethtool_ringparam *ring,
57562306a36Sopenharmony_ci			     struct kernel_ethtool_ringparam *kring,
57662306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
57962306a36Sopenharmony_ci	int rc;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (ring->rx_mini_pending || ring->rx_jumbo_pending)
58262306a36Sopenharmony_ci		return -EINVAL;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* queue depths must be powers-of-2 */
58562306a36Sopenharmony_ci	if (!is_power_of_2(ring->rx_pending) ||
58662306a36Sopenharmony_ci	    !is_power_of_2(ring->tx_pending))
58762306a36Sopenharmony_ci		return -EINVAL;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (ring->rx_pending < FUNETH_MIN_QDEPTH ||
59062306a36Sopenharmony_ci	    ring->tx_pending < FUNETH_MIN_QDEPTH)
59162306a36Sopenharmony_ci		return -EINVAL;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (fp->sq_depth == ring->tx_pending &&
59462306a36Sopenharmony_ci	    fp->rq_depth == ring->rx_pending)
59562306a36Sopenharmony_ci		return 0;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (netif_running(netdev)) {
59862306a36Sopenharmony_ci		struct fun_qset req = {
59962306a36Sopenharmony_ci			.cq_depth = 2 * ring->rx_pending,
60062306a36Sopenharmony_ci			.rq_depth = ring->rx_pending,
60162306a36Sopenharmony_ci			.sq_depth = ring->tx_pending
60262306a36Sopenharmony_ci		};
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		rc = fun_replace_queues(netdev, &req, extack);
60562306a36Sopenharmony_ci		if (rc)
60662306a36Sopenharmony_ci			return rc;
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	fp->sq_depth = ring->tx_pending;
61062306a36Sopenharmony_ci	fp->rq_depth = ring->rx_pending;
61162306a36Sopenharmony_ci	fp->cq_depth = 2 * fp->rq_depth;
61262306a36Sopenharmony_ci	return 0;
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic int fun_get_sset_count(struct net_device *dev, int sset)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(dev);
61862306a36Sopenharmony_ci	int n;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	switch (sset) {
62162306a36Sopenharmony_ci	case ETH_SS_STATS:
62262306a36Sopenharmony_ci		n = (dev->real_num_tx_queues + 1) * ARRAY_SIZE(txq_stat_names) +
62362306a36Sopenharmony_ci		    (dev->real_num_rx_queues + 1) * ARRAY_SIZE(rxq_stat_names) +
62462306a36Sopenharmony_ci		    (fp->num_xdpqs + 1) * ARRAY_SIZE(xdpq_stat_names) +
62562306a36Sopenharmony_ci		    ARRAY_SIZE(tls_stat_names);
62662306a36Sopenharmony_ci		if (fp->port_caps & FUN_PORT_CAP_STATS) {
62762306a36Sopenharmony_ci			n += ARRAY_SIZE(mac_tx_stat_names) +
62862306a36Sopenharmony_ci			     ARRAY_SIZE(mac_rx_stat_names);
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci		return n;
63162306a36Sopenharmony_ci	default:
63262306a36Sopenharmony_ci		break;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	return 0;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic void fun_get_strings(struct net_device *netdev, u32 sset, u8 *data)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
64062306a36Sopenharmony_ci	unsigned int i, j;
64162306a36Sopenharmony_ci	u8 *p = data;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	switch (sset) {
64462306a36Sopenharmony_ci	case ETH_SS_STATS:
64562306a36Sopenharmony_ci		if (fp->port_caps & FUN_PORT_CAP_STATS) {
64662306a36Sopenharmony_ci			memcpy(p, mac_tx_stat_names, sizeof(mac_tx_stat_names));
64762306a36Sopenharmony_ci			p += sizeof(mac_tx_stat_names);
64862306a36Sopenharmony_ci			memcpy(p, mac_rx_stat_names, sizeof(mac_rx_stat_names));
64962306a36Sopenharmony_ci			p += sizeof(mac_rx_stat_names);
65062306a36Sopenharmony_ci		}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		for (i = 0; i < netdev->real_num_tx_queues; i++) {
65362306a36Sopenharmony_ci			for (j = 0; j < ARRAY_SIZE(txq_stat_names); j++)
65462306a36Sopenharmony_ci				ethtool_sprintf(&p, "%s[%u]", txq_stat_names[j],
65562306a36Sopenharmony_ci						i);
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(txq_stat_names); j++)
65862306a36Sopenharmony_ci			ethtool_sprintf(&p, txq_stat_names[j]);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		for (i = 0; i < fp->num_xdpqs; i++) {
66162306a36Sopenharmony_ci			for (j = 0; j < ARRAY_SIZE(xdpq_stat_names); j++)
66262306a36Sopenharmony_ci				ethtool_sprintf(&p, "%s[%u]",
66362306a36Sopenharmony_ci						xdpq_stat_names[j], i);
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(xdpq_stat_names); j++)
66662306a36Sopenharmony_ci			ethtool_sprintf(&p, xdpq_stat_names[j]);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		for (i = 0; i < netdev->real_num_rx_queues; i++) {
66962306a36Sopenharmony_ci			for (j = 0; j < ARRAY_SIZE(rxq_stat_names); j++)
67062306a36Sopenharmony_ci				ethtool_sprintf(&p, "%s[%u]", rxq_stat_names[j],
67162306a36Sopenharmony_ci						i);
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(rxq_stat_names); j++)
67462306a36Sopenharmony_ci			ethtool_sprintf(&p, rxq_stat_names[j]);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(tls_stat_names); j++)
67762306a36Sopenharmony_ci			ethtool_sprintf(&p, tls_stat_names[j]);
67862306a36Sopenharmony_ci		break;
67962306a36Sopenharmony_ci	default:
68062306a36Sopenharmony_ci		break;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic u64 *get_mac_stats(const struct funeth_priv *fp, u64 *data)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci#define TX_STAT(s) \
68762306a36Sopenharmony_ci	*data++ = be64_to_cpu(fp->stats[PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_##s])
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	TX_STAT(etherStatsOctets);
69062306a36Sopenharmony_ci	TX_STAT(etherStatsPkts);
69162306a36Sopenharmony_ci	TX_STAT(VLANTransmittedOK);
69262306a36Sopenharmony_ci	TX_STAT(ifOutUcastPkts);
69362306a36Sopenharmony_ci	TX_STAT(ifOutMulticastPkts);
69462306a36Sopenharmony_ci	TX_STAT(ifOutBroadcastPkts);
69562306a36Sopenharmony_ci	TX_STAT(ifOutErrors);
69662306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_0);
69762306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_1);
69862306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_2);
69962306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_3);
70062306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_4);
70162306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_5);
70262306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_6);
70362306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_7);
70462306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_8);
70562306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_9);
70662306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_10);
70762306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_11);
70862306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_12);
70962306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_13);
71062306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_14);
71162306a36Sopenharmony_ci	TX_STAT(CBFCPAUSEFramesTransmitted_15);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci#define RX_STAT(s) *data++ = be64_to_cpu(fp->stats[PORT_MAC_RX_##s])
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	RX_STAT(etherStatsOctets);
71662306a36Sopenharmony_ci	RX_STAT(etherStatsPkts);
71762306a36Sopenharmony_ci	RX_STAT(VLANReceivedOK);
71862306a36Sopenharmony_ci	RX_STAT(ifInUcastPkts);
71962306a36Sopenharmony_ci	RX_STAT(ifInMulticastPkts);
72062306a36Sopenharmony_ci	RX_STAT(ifInBroadcastPkts);
72162306a36Sopenharmony_ci	RX_STAT(etherStatsDropEvents);
72262306a36Sopenharmony_ci	RX_STAT(ifInErrors);
72362306a36Sopenharmony_ci	RX_STAT(aAlignmentErrors);
72462306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_0);
72562306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_1);
72662306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_2);
72762306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_3);
72862306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_4);
72962306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_5);
73062306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_6);
73162306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_7);
73262306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_8);
73362306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_9);
73462306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_10);
73562306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_11);
73662306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_12);
73762306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_13);
73862306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_14);
73962306a36Sopenharmony_ci	RX_STAT(CBFCPAUSEFramesReceived_15);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	return data;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci#undef TX_STAT
74462306a36Sopenharmony_ci#undef RX_STAT
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic void fun_get_ethtool_stats(struct net_device *netdev,
74862306a36Sopenharmony_ci				  struct ethtool_stats *stats, u64 *data)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
75162306a36Sopenharmony_ci	struct funeth_txq_stats txs;
75262306a36Sopenharmony_ci	struct funeth_rxq_stats rxs;
75362306a36Sopenharmony_ci	struct funeth_txq **xdpqs;
75462306a36Sopenharmony_ci	struct funeth_rxq **rxqs;
75562306a36Sopenharmony_ci	unsigned int i, start;
75662306a36Sopenharmony_ci	u64 *totals, *tot;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (fp->port_caps & FUN_PORT_CAP_STATS)
75962306a36Sopenharmony_ci		data = get_mac_stats(fp, data);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	rxqs = rtnl_dereference(fp->rxqs);
76262306a36Sopenharmony_ci	if (!rxqs)
76362306a36Sopenharmony_ci		return;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci#define ADD_STAT(cnt) do { \
76662306a36Sopenharmony_ci	*data = (cnt); *tot++ += *data++; \
76762306a36Sopenharmony_ci} while (0)
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/* Tx queues */
77062306a36Sopenharmony_ci	totals = data + netdev->real_num_tx_queues * ARRAY_SIZE(txq_stat_names);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	for (i = 0; i < netdev->real_num_tx_queues; i++) {
77362306a36Sopenharmony_ci		tot = totals;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		FUN_QSTAT_READ(fp->txqs[i], start, txs);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		ADD_STAT(txs.tx_pkts);
77862306a36Sopenharmony_ci		ADD_STAT(txs.tx_bytes);
77962306a36Sopenharmony_ci		ADD_STAT(txs.tx_cso);
78062306a36Sopenharmony_ci		ADD_STAT(txs.tx_tso);
78162306a36Sopenharmony_ci		ADD_STAT(txs.tx_encap_tso);
78262306a36Sopenharmony_ci		ADD_STAT(txs.tx_uso);
78362306a36Sopenharmony_ci		ADD_STAT(txs.tx_more);
78462306a36Sopenharmony_ci		ADD_STAT(txs.tx_nstops);
78562306a36Sopenharmony_ci		ADD_STAT(txs.tx_nrestarts);
78662306a36Sopenharmony_ci		ADD_STAT(txs.tx_map_err);
78762306a36Sopenharmony_ci		ADD_STAT(txs.tx_tls_pkts);
78862306a36Sopenharmony_ci		ADD_STAT(txs.tx_tls_bytes);
78962306a36Sopenharmony_ci		ADD_STAT(txs.tx_tls_fallback);
79062306a36Sopenharmony_ci		ADD_STAT(txs.tx_tls_drops);
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci	data += ARRAY_SIZE(txq_stat_names);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* XDP Tx queues */
79562306a36Sopenharmony_ci	xdpqs = rtnl_dereference(fp->xdpqs);
79662306a36Sopenharmony_ci	totals = data + fp->num_xdpqs * ARRAY_SIZE(xdpq_stat_names);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	for (i = 0; i < fp->num_xdpqs; i++) {
79962306a36Sopenharmony_ci		tot = totals;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		FUN_QSTAT_READ(xdpqs[i], start, txs);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		ADD_STAT(txs.tx_pkts);
80462306a36Sopenharmony_ci		ADD_STAT(txs.tx_bytes);
80562306a36Sopenharmony_ci		ADD_STAT(txs.tx_xdp_full);
80662306a36Sopenharmony_ci		ADD_STAT(txs.tx_map_err);
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci	data += ARRAY_SIZE(xdpq_stat_names);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* Rx queues */
81162306a36Sopenharmony_ci	totals = data + netdev->real_num_rx_queues * ARRAY_SIZE(rxq_stat_names);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	for (i = 0; i < netdev->real_num_rx_queues; i++) {
81462306a36Sopenharmony_ci		tot = totals;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		FUN_QSTAT_READ(rxqs[i], start, rxs);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		ADD_STAT(rxs.rx_pkts);
81962306a36Sopenharmony_ci		ADD_STAT(rxs.rx_bytes);
82062306a36Sopenharmony_ci		ADD_STAT(rxs.rx_cso);
82162306a36Sopenharmony_ci		ADD_STAT(rxs.gro_pkts);
82262306a36Sopenharmony_ci		ADD_STAT(rxs.gro_merged);
82362306a36Sopenharmony_ci		ADD_STAT(rxs.xdp_tx);
82462306a36Sopenharmony_ci		ADD_STAT(rxs.xdp_redir);
82562306a36Sopenharmony_ci		ADD_STAT(rxs.xdp_drops);
82662306a36Sopenharmony_ci		ADD_STAT(rxs.rx_bufs);
82762306a36Sopenharmony_ci		ADD_STAT(rxs.rx_page_alloc);
82862306a36Sopenharmony_ci		ADD_STAT(rxs.rx_mem_drops + rxs.xdp_err);
82962306a36Sopenharmony_ci		ADD_STAT(rxs.rx_budget);
83062306a36Sopenharmony_ci		ADD_STAT(rxs.rx_map_err);
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci	data += ARRAY_SIZE(rxq_stat_names);
83362306a36Sopenharmony_ci#undef ADD_STAT
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	*data++ = atomic64_read(&fp->tx_tls_add);
83662306a36Sopenharmony_ci	*data++ = atomic64_read(&fp->tx_tls_del);
83762306a36Sopenharmony_ci	*data++ = atomic64_read(&fp->tx_tls_resync);
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci#define RX_STAT(fp, s) be64_to_cpu((fp)->stats[PORT_MAC_RX_##s])
84162306a36Sopenharmony_ci#define TX_STAT(fp, s) \
84262306a36Sopenharmony_ci	be64_to_cpu((fp)->stats[PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_##s])
84362306a36Sopenharmony_ci#define FEC_STAT(fp, s) \
84462306a36Sopenharmony_ci	be64_to_cpu((fp)->stats[PORT_MAC_RX_STATS_MAX + \
84562306a36Sopenharmony_ci				PORT_MAC_TX_STATS_MAX + PORT_MAC_FEC_##s])
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic void fun_get_pause_stats(struct net_device *netdev,
84862306a36Sopenharmony_ci				struct ethtool_pause_stats *stats)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
85362306a36Sopenharmony_ci		return;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	stats->tx_pause_frames = TX_STAT(fp, aPAUSEMACCtrlFramesTransmitted);
85662306a36Sopenharmony_ci	stats->rx_pause_frames = RX_STAT(fp, aPAUSEMACCtrlFramesReceived);
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic void fun_get_802_3_stats(struct net_device *netdev,
86062306a36Sopenharmony_ci				struct ethtool_eth_mac_stats *stats)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
86562306a36Sopenharmony_ci		return;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	stats->FramesTransmittedOK = TX_STAT(fp, aFramesTransmittedOK);
86862306a36Sopenharmony_ci	stats->FramesReceivedOK = RX_STAT(fp, aFramesReceivedOK);
86962306a36Sopenharmony_ci	stats->FrameCheckSequenceErrors = RX_STAT(fp, aFrameCheckSequenceErrors);
87062306a36Sopenharmony_ci	stats->OctetsTransmittedOK = TX_STAT(fp, OctetsTransmittedOK);
87162306a36Sopenharmony_ci	stats->OctetsReceivedOK = RX_STAT(fp, OctetsReceivedOK);
87262306a36Sopenharmony_ci	stats->InRangeLengthErrors = RX_STAT(fp, aInRangeLengthErrors);
87362306a36Sopenharmony_ci	stats->FrameTooLongErrors = RX_STAT(fp, aFrameTooLongErrors);
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cistatic void fun_get_802_3_ctrl_stats(struct net_device *netdev,
87762306a36Sopenharmony_ci				     struct ethtool_eth_ctrl_stats *stats)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
88262306a36Sopenharmony_ci		return;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	stats->MACControlFramesTransmitted = TX_STAT(fp, MACControlFramesTransmitted);
88562306a36Sopenharmony_ci	stats->MACControlFramesReceived = RX_STAT(fp, MACControlFramesReceived);
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic void fun_get_rmon_stats(struct net_device *netdev,
88962306a36Sopenharmony_ci			       struct ethtool_rmon_stats *stats,
89062306a36Sopenharmony_ci			       const struct ethtool_rmon_hist_range **ranges)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	static const struct ethtool_rmon_hist_range rmon_ranges[] = {
89362306a36Sopenharmony_ci		{   64,    64 },
89462306a36Sopenharmony_ci		{   65,   127 },
89562306a36Sopenharmony_ci		{  128,   255 },
89662306a36Sopenharmony_ci		{  256,   511 },
89762306a36Sopenharmony_ci		{  512,  1023 },
89862306a36Sopenharmony_ci		{ 1024,  1518 },
89962306a36Sopenharmony_ci		{ 1519, 32767 },
90062306a36Sopenharmony_ci		{}
90162306a36Sopenharmony_ci	};
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
90662306a36Sopenharmony_ci		return;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	stats->undersize_pkts = RX_STAT(fp, etherStatsUndersizePkts);
90962306a36Sopenharmony_ci	stats->oversize_pkts = RX_STAT(fp, etherStatsOversizePkts);
91062306a36Sopenharmony_ci	stats->fragments = RX_STAT(fp, etherStatsFragments);
91162306a36Sopenharmony_ci	stats->jabbers = RX_STAT(fp, etherStatsJabbers);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	stats->hist[0] = RX_STAT(fp, etherStatsPkts64Octets);
91462306a36Sopenharmony_ci	stats->hist[1] = RX_STAT(fp, etherStatsPkts65to127Octets);
91562306a36Sopenharmony_ci	stats->hist[2] = RX_STAT(fp, etherStatsPkts128to255Octets);
91662306a36Sopenharmony_ci	stats->hist[3] = RX_STAT(fp, etherStatsPkts256to511Octets);
91762306a36Sopenharmony_ci	stats->hist[4] = RX_STAT(fp, etherStatsPkts512to1023Octets);
91862306a36Sopenharmony_ci	stats->hist[5] = RX_STAT(fp, etherStatsPkts1024to1518Octets);
91962306a36Sopenharmony_ci	stats->hist[6] = RX_STAT(fp, etherStatsPkts1519toMaxOctets);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	stats->hist_tx[0] = TX_STAT(fp, etherStatsPkts64Octets);
92262306a36Sopenharmony_ci	stats->hist_tx[1] = TX_STAT(fp, etherStatsPkts65to127Octets);
92362306a36Sopenharmony_ci	stats->hist_tx[2] = TX_STAT(fp, etherStatsPkts128to255Octets);
92462306a36Sopenharmony_ci	stats->hist_tx[3] = TX_STAT(fp, etherStatsPkts256to511Octets);
92562306a36Sopenharmony_ci	stats->hist_tx[4] = TX_STAT(fp, etherStatsPkts512to1023Octets);
92662306a36Sopenharmony_ci	stats->hist_tx[5] = TX_STAT(fp, etherStatsPkts1024to1518Octets);
92762306a36Sopenharmony_ci	stats->hist_tx[6] = TX_STAT(fp, etherStatsPkts1519toMaxOctets);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	*ranges = rmon_ranges;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic void fun_get_fec_stats(struct net_device *netdev,
93362306a36Sopenharmony_ci			      struct ethtool_fec_stats *stats)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (!(fp->port_caps & FUN_PORT_CAP_STATS))
93862306a36Sopenharmony_ci		return;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	stats->corrected_blocks.total = FEC_STAT(fp, Correctable);
94162306a36Sopenharmony_ci	stats->uncorrectable_blocks.total = FEC_STAT(fp, Uncorrectable);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci#undef RX_STAT
94562306a36Sopenharmony_ci#undef TX_STAT
94662306a36Sopenharmony_ci#undef FEC_STAT
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic int fun_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
94962306a36Sopenharmony_ci			 u32 *rule_locs)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	switch (cmd->cmd) {
95262306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
95362306a36Sopenharmony_ci		cmd->data = netdev->real_num_rx_queues;
95462306a36Sopenharmony_ci		return 0;
95562306a36Sopenharmony_ci	default:
95662306a36Sopenharmony_ci		break;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci	return -EOPNOTSUPP;
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic int fun_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	return 0;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic u32 fun_get_rxfh_indir_size(struct net_device *netdev)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	return fp->indir_table_nentries;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic u32 fun_get_rxfh_key_size(struct net_device *netdev)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return sizeof(fp->rss_key);
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic int fun_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
98162306a36Sopenharmony_ci			u8 *hfunc)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	const struct funeth_priv *fp = netdev_priv(netdev);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	if (!fp->rss_cfg)
98662306a36Sopenharmony_ci		return -EOPNOTSUPP;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (indir)
98962306a36Sopenharmony_ci		memcpy(indir, fp->indir_table,
99062306a36Sopenharmony_ci		       sizeof(u32) * fp->indir_table_nentries);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (key)
99362306a36Sopenharmony_ci		memcpy(key, fp->rss_key, sizeof(fp->rss_key));
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (hfunc)
99662306a36Sopenharmony_ci		*hfunc = fp->hash_algo == FUN_ETH_RSS_ALG_TOEPLITZ ?
99762306a36Sopenharmony_ci				ETH_RSS_HASH_TOP : ETH_RSS_HASH_CRC32;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	return 0;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cistatic int fun_set_rxfh(struct net_device *netdev, const u32 *indir,
100362306a36Sopenharmony_ci			const u8 *key, const u8 hfunc)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
100662306a36Sopenharmony_ci	const u32 *rss_indir = indir ? indir : fp->indir_table;
100762306a36Sopenharmony_ci	const u8 *rss_key = key ? key : fp->rss_key;
100862306a36Sopenharmony_ci	enum fun_eth_hash_alg algo;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (!fp->rss_cfg)
101162306a36Sopenharmony_ci		return -EOPNOTSUPP;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	if (hfunc == ETH_RSS_HASH_NO_CHANGE)
101462306a36Sopenharmony_ci		algo = fp->hash_algo;
101562306a36Sopenharmony_ci	else if (hfunc == ETH_RSS_HASH_CRC32)
101662306a36Sopenharmony_ci		algo = FUN_ETH_RSS_ALG_CRC32;
101762306a36Sopenharmony_ci	else if (hfunc == ETH_RSS_HASH_TOP)
101862306a36Sopenharmony_ci		algo = FUN_ETH_RSS_ALG_TOEPLITZ;
101962306a36Sopenharmony_ci	else
102062306a36Sopenharmony_ci		return -EINVAL;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/* If the port is enabled try to reconfigure RSS and keep the new
102362306a36Sopenharmony_ci	 * settings if successful. If it is down we update the RSS settings
102462306a36Sopenharmony_ci	 * and apply them at the next UP time.
102562306a36Sopenharmony_ci	 */
102662306a36Sopenharmony_ci	if (netif_running(netdev)) {
102762306a36Sopenharmony_ci		int rc = fun_config_rss(netdev, algo, rss_key, rss_indir,
102862306a36Sopenharmony_ci					FUN_ADMIN_SUBOP_MODIFY);
102962306a36Sopenharmony_ci		if (rc)
103062306a36Sopenharmony_ci			return rc;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	fp->hash_algo = algo;
103462306a36Sopenharmony_ci	if (key)
103562306a36Sopenharmony_ci		memcpy(fp->rss_key, key, sizeof(fp->rss_key));
103662306a36Sopenharmony_ci	if (indir)
103762306a36Sopenharmony_ci		memcpy(fp->indir_table, indir,
103862306a36Sopenharmony_ci		       sizeof(u32) * fp->indir_table_nentries);
103962306a36Sopenharmony_ci	return 0;
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic int fun_get_ts_info(struct net_device *netdev,
104362306a36Sopenharmony_ci			   struct ethtool_ts_info *info)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
104662306a36Sopenharmony_ci				SOF_TIMESTAMPING_RX_HARDWARE |
104762306a36Sopenharmony_ci				SOF_TIMESTAMPING_TX_SOFTWARE |
104862306a36Sopenharmony_ci				SOF_TIMESTAMPING_SOFTWARE |
104962306a36Sopenharmony_ci				SOF_TIMESTAMPING_RAW_HARDWARE;
105062306a36Sopenharmony_ci	info->phc_index = -1;
105162306a36Sopenharmony_ci	info->tx_types = BIT(HWTSTAMP_TX_OFF);
105262306a36Sopenharmony_ci	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
105362306a36Sopenharmony_ci	return 0;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic unsigned int to_ethtool_fec(unsigned int fun_fec)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	unsigned int fec = 0;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (fun_fec == FUN_PORT_FEC_NA)
106162306a36Sopenharmony_ci		fec |= ETHTOOL_FEC_NONE;
106262306a36Sopenharmony_ci	if (fun_fec & FUN_PORT_FEC_OFF)
106362306a36Sopenharmony_ci		fec |= ETHTOOL_FEC_OFF;
106462306a36Sopenharmony_ci	if (fun_fec & FUN_PORT_FEC_RS)
106562306a36Sopenharmony_ci		fec |= ETHTOOL_FEC_RS;
106662306a36Sopenharmony_ci	if (fun_fec & FUN_PORT_FEC_FC)
106762306a36Sopenharmony_ci		fec |= ETHTOOL_FEC_BASER;
106862306a36Sopenharmony_ci	if (fun_fec & FUN_PORT_FEC_AUTO)
106962306a36Sopenharmony_ci		fec |= ETHTOOL_FEC_AUTO;
107062306a36Sopenharmony_ci	return fec;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic int fun_get_fecparam(struct net_device *netdev,
107462306a36Sopenharmony_ci			    struct ethtool_fecparam *fec)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
107762306a36Sopenharmony_ci	u64 fec_data;
107862306a36Sopenharmony_ci	int rc;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	rc = fun_port_read_cmd(fp, FUN_ADMIN_PORT_KEY_FEC, &fec_data);
108162306a36Sopenharmony_ci	if (rc)
108262306a36Sopenharmony_ci		return rc;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	fec->active_fec = to_ethtool_fec(fec_data & 0xff);
108562306a36Sopenharmony_ci	fec->fec = to_ethtool_fec(fec_data >> 8);
108662306a36Sopenharmony_ci	return 0;
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic int fun_set_fecparam(struct net_device *netdev,
109062306a36Sopenharmony_ci			    struct ethtool_fecparam *fec)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
109362306a36Sopenharmony_ci	u64 fec_mode;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	switch (fec->fec) {
109662306a36Sopenharmony_ci	case ETHTOOL_FEC_AUTO:
109762306a36Sopenharmony_ci		fec_mode = FUN_PORT_FEC_AUTO;
109862306a36Sopenharmony_ci		break;
109962306a36Sopenharmony_ci	case ETHTOOL_FEC_OFF:
110062306a36Sopenharmony_ci		if (!(fp->port_caps & FUN_PORT_CAP_FEC_NONE))
110162306a36Sopenharmony_ci			return -EINVAL;
110262306a36Sopenharmony_ci		fec_mode = FUN_PORT_FEC_OFF;
110362306a36Sopenharmony_ci		break;
110462306a36Sopenharmony_ci	case ETHTOOL_FEC_BASER:
110562306a36Sopenharmony_ci		if (!(fp->port_caps & FUN_PORT_CAP_FEC_FC))
110662306a36Sopenharmony_ci			return -EINVAL;
110762306a36Sopenharmony_ci		fec_mode = FUN_PORT_FEC_FC;
110862306a36Sopenharmony_ci		break;
110962306a36Sopenharmony_ci	case ETHTOOL_FEC_RS:
111062306a36Sopenharmony_ci		if (!(fp->port_caps & FUN_PORT_CAP_FEC_RS))
111162306a36Sopenharmony_ci			return -EINVAL;
111262306a36Sopenharmony_ci		fec_mode = FUN_PORT_FEC_RS;
111362306a36Sopenharmony_ci		break;
111462306a36Sopenharmony_ci	default:
111562306a36Sopenharmony_ci		return -EINVAL;
111662306a36Sopenharmony_ci	}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_FEC, fec_mode);
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_cistatic int fun_get_port_module_page(struct net_device *netdev,
112262306a36Sopenharmony_ci				    const struct ethtool_module_eeprom *req,
112362306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	union {
112662306a36Sopenharmony_ci		struct fun_admin_port_req req;
112762306a36Sopenharmony_ci		struct fun_admin_port_xcvr_read_rsp rsp;
112862306a36Sopenharmony_ci	} cmd;
112962306a36Sopenharmony_ci	struct funeth_priv *fp = netdev_priv(netdev);
113062306a36Sopenharmony_ci	int rc;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (fp->port_caps & FUN_PORT_CAP_VPORT) {
113362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
113462306a36Sopenharmony_ci				   "Specified port is virtual, only physical ports have modules");
113562306a36Sopenharmony_ci		return -EOPNOTSUPP;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_PORT,
113962306a36Sopenharmony_ci						    sizeof(cmd.req));
114062306a36Sopenharmony_ci	cmd.req.u.xcvr_read =
114162306a36Sopenharmony_ci		FUN_ADMIN_PORT_XCVR_READ_REQ_INIT(0, netdev->dev_port,
114262306a36Sopenharmony_ci						  req->bank, req->page,
114362306a36Sopenharmony_ci						  req->offset, req->length,
114462306a36Sopenharmony_ci						  req->i2c_address);
114562306a36Sopenharmony_ci	rc = fun_submit_admin_sync_cmd(fp->fdev, &cmd.req.common, &cmd.rsp,
114662306a36Sopenharmony_ci				       sizeof(cmd.rsp), 0);
114762306a36Sopenharmony_ci	if (rc)
114862306a36Sopenharmony_ci		return rc;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	memcpy(req->data, cmd.rsp.data, req->length);
115162306a36Sopenharmony_ci	return req->length;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic const struct ethtool_ops fun_ethtool_ops = {
115562306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
115662306a36Sopenharmony_ci				     ETHTOOL_COALESCE_MAX_FRAMES,
115762306a36Sopenharmony_ci	.get_link_ksettings  = fun_get_link_ksettings,
115862306a36Sopenharmony_ci	.set_link_ksettings  = fun_set_link_ksettings,
115962306a36Sopenharmony_ci	.set_phys_id         = fun_set_phys_id,
116062306a36Sopenharmony_ci	.get_drvinfo         = fun_get_drvinfo,
116162306a36Sopenharmony_ci	.get_msglevel        = fun_get_msglevel,
116262306a36Sopenharmony_ci	.set_msglevel        = fun_set_msglevel,
116362306a36Sopenharmony_ci	.get_regs_len        = fun_get_regs_len,
116462306a36Sopenharmony_ci	.get_regs            = fun_get_regs,
116562306a36Sopenharmony_ci	.get_link	     = ethtool_op_get_link,
116662306a36Sopenharmony_ci	.get_coalesce        = fun_get_coalesce,
116762306a36Sopenharmony_ci	.set_coalesce        = fun_set_coalesce,
116862306a36Sopenharmony_ci	.get_ts_info         = fun_get_ts_info,
116962306a36Sopenharmony_ci	.get_ringparam       = fun_get_ringparam,
117062306a36Sopenharmony_ci	.set_ringparam       = fun_set_ringparam,
117162306a36Sopenharmony_ci	.get_sset_count      = fun_get_sset_count,
117262306a36Sopenharmony_ci	.get_strings         = fun_get_strings,
117362306a36Sopenharmony_ci	.get_ethtool_stats   = fun_get_ethtool_stats,
117462306a36Sopenharmony_ci	.get_rxnfc	     = fun_get_rxnfc,
117562306a36Sopenharmony_ci	.set_rxnfc           = fun_set_rxnfc,
117662306a36Sopenharmony_ci	.get_rxfh_indir_size = fun_get_rxfh_indir_size,
117762306a36Sopenharmony_ci	.get_rxfh_key_size   = fun_get_rxfh_key_size,
117862306a36Sopenharmony_ci	.get_rxfh            = fun_get_rxfh,
117962306a36Sopenharmony_ci	.set_rxfh            = fun_set_rxfh,
118062306a36Sopenharmony_ci	.get_channels        = fun_get_channels,
118162306a36Sopenharmony_ci	.set_channels        = fun_set_channels,
118262306a36Sopenharmony_ci	.get_fecparam	     = fun_get_fecparam,
118362306a36Sopenharmony_ci	.set_fecparam	     = fun_set_fecparam,
118462306a36Sopenharmony_ci	.get_pauseparam      = fun_get_pauseparam,
118562306a36Sopenharmony_ci	.set_pauseparam      = fun_set_pauseparam,
118662306a36Sopenharmony_ci	.nway_reset          = fun_restart_an,
118762306a36Sopenharmony_ci	.get_pause_stats     = fun_get_pause_stats,
118862306a36Sopenharmony_ci	.get_fec_stats       = fun_get_fec_stats,
118962306a36Sopenharmony_ci	.get_eth_mac_stats   = fun_get_802_3_stats,
119062306a36Sopenharmony_ci	.get_eth_ctrl_stats  = fun_get_802_3_ctrl_stats,
119162306a36Sopenharmony_ci	.get_rmon_stats      = fun_get_rmon_stats,
119262306a36Sopenharmony_ci	.get_module_eeprom_by_page = fun_get_port_module_page,
119362306a36Sopenharmony_ci};
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_civoid fun_set_ethtool_ops(struct net_device *netdev)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	netdev->ethtool_ops = &fun_ethtool_ops;
119862306a36Sopenharmony_ci}
1199