cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

prestera_ethtool.c (23216B)


      1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
      2/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
      3
      4#include <linux/ethtool.h>
      5#include <linux/kernel.h>
      6#include <linux/netdevice.h>
      7
      8#include "prestera_ethtool.h"
      9#include "prestera.h"
     10#include "prestera_hw.h"
     11
     12#define PRESTERA_STATS_CNT \
     13	(sizeof(struct prestera_port_stats) / sizeof(u64))
     14#define PRESTERA_STATS_IDX(name) \
     15	(offsetof(struct prestera_port_stats, name) / sizeof(u64))
     16#define PRESTERA_STATS_FIELD(name)	\
     17	[PRESTERA_STATS_IDX(name)] = __stringify(name)
     18
     19static const char driver_kind[] = "prestera";
     20
     21static const struct prestera_link_mode {
     22	enum ethtool_link_mode_bit_indices eth_mode;
     23	u32 speed;
     24	u64 pr_mask;
     25	u8 duplex;
     26	u8 port_type;
     27} port_link_modes[PRESTERA_LINK_MODE_MAX] = {
     28	[PRESTERA_LINK_MODE_10baseT_Half] = {
     29		.eth_mode =  ETHTOOL_LINK_MODE_10baseT_Half_BIT,
     30		.speed = 10,
     31		.pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Half,
     32		.duplex = PRESTERA_PORT_DUPLEX_HALF,
     33		.port_type = PRESTERA_PORT_TYPE_TP,
     34	},
     35	[PRESTERA_LINK_MODE_10baseT_Full] = {
     36		.eth_mode =  ETHTOOL_LINK_MODE_10baseT_Full_BIT,
     37		.speed = 10,
     38		.pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Full,
     39		.duplex = PRESTERA_PORT_DUPLEX_FULL,
     40		.port_type = PRESTERA_PORT_TYPE_TP,
     41	},
     42	[PRESTERA_LINK_MODE_100baseT_Half] = {
     43		.eth_mode =  ETHTOOL_LINK_MODE_100baseT_Half_BIT,
     44		.speed = 100,
     45		.pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Half,
     46		.duplex = PRESTERA_PORT_DUPLEX_HALF,
     47		.port_type = PRESTERA_PORT_TYPE_TP,
     48	},
     49	[PRESTERA_LINK_MODE_100baseT_Full] = {
     50		.eth_mode =  ETHTOOL_LINK_MODE_100baseT_Full_BIT,
     51		.speed = 100,
     52		.pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Full,
     53		.duplex = PRESTERA_PORT_DUPLEX_FULL,
     54		.port_type = PRESTERA_PORT_TYPE_TP,
     55	},
     56	[PRESTERA_LINK_MODE_1000baseT_Half] = {
     57		.eth_mode =  ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
     58		.speed = 1000,
     59		.pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Half,
     60		.duplex = PRESTERA_PORT_DUPLEX_HALF,
     61		.port_type = PRESTERA_PORT_TYPE_TP,
     62	},
     63	[PRESTERA_LINK_MODE_1000baseT_Full] = {
     64		.eth_mode =  ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
     65		.speed = 1000,
     66		.pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Full,
     67		.duplex = PRESTERA_PORT_DUPLEX_FULL,
     68		.port_type = PRESTERA_PORT_TYPE_TP,
     69	},
     70	[PRESTERA_LINK_MODE_1000baseX_Full] = {
     71		.eth_mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
     72		.speed = 1000,
     73		.pr_mask = 1 << PRESTERA_LINK_MODE_1000baseX_Full,
     74		.duplex = PRESTERA_PORT_DUPLEX_FULL,
     75		.port_type = PRESTERA_PORT_TYPE_FIBRE,
     76	},
     77	[PRESTERA_LINK_MODE_1000baseKX_Full] = {
     78		.eth_mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
     79		.speed = 1000,
     80		.pr_mask = 1 << PRESTERA_LINK_MODE_1000baseKX_Full,
     81		.duplex = PRESTERA_PORT_DUPLEX_FULL,
     82		.port_type = PRESTERA_PORT_TYPE_TP,
     83	},
     84	[PRESTERA_LINK_MODE_2500baseX_Full] = {
     85		.eth_mode =  ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
     86		.speed = 2500,
     87		.pr_mask = 1 << PRESTERA_LINK_MODE_2500baseX_Full,
     88		.duplex = PRESTERA_PORT_DUPLEX_FULL,
     89	},
     90	[PRESTERA_LINK_MODE_10GbaseKR_Full] = {
     91		.eth_mode = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
     92		.speed = 10000,
     93		.pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseKR_Full,
     94		.duplex = PRESTERA_PORT_DUPLEX_FULL,
     95		.port_type = PRESTERA_PORT_TYPE_TP,
     96	},
     97	[PRESTERA_LINK_MODE_10GbaseSR_Full] = {
     98		.eth_mode = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
     99		.speed = 10000,
    100		.pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseSR_Full,
    101		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    102		.port_type = PRESTERA_PORT_TYPE_FIBRE,
    103	},
    104	[PRESTERA_LINK_MODE_10GbaseLR_Full] = {
    105		.eth_mode = ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
    106		.speed = 10000,
    107		.pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseLR_Full,
    108		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    109		.port_type = PRESTERA_PORT_TYPE_FIBRE,
    110	},
    111	[PRESTERA_LINK_MODE_20GbaseKR2_Full] = {
    112		.eth_mode = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
    113		.speed = 20000,
    114		.pr_mask = 1 << PRESTERA_LINK_MODE_20GbaseKR2_Full,
    115		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    116		.port_type = PRESTERA_PORT_TYPE_TP,
    117	},
    118	[PRESTERA_LINK_MODE_25GbaseCR_Full] = {
    119		.eth_mode = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
    120		.speed = 25000,
    121		.pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseCR_Full,
    122		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    123		.port_type = PRESTERA_PORT_TYPE_DA,
    124	},
    125	[PRESTERA_LINK_MODE_25GbaseKR_Full] = {
    126		.eth_mode = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
    127		.speed = 25000,
    128		.pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseKR_Full,
    129		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    130		.port_type = PRESTERA_PORT_TYPE_TP,
    131	},
    132	[PRESTERA_LINK_MODE_25GbaseSR_Full] = {
    133		.eth_mode = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
    134		.speed = 25000,
    135		.pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseSR_Full,
    136		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    137		.port_type = PRESTERA_PORT_TYPE_FIBRE,
    138	},
    139	[PRESTERA_LINK_MODE_40GbaseKR4_Full] = {
    140		.eth_mode = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
    141		.speed = 40000,
    142		.pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseKR4_Full,
    143		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    144		.port_type = PRESTERA_PORT_TYPE_TP,
    145	},
    146	[PRESTERA_LINK_MODE_40GbaseCR4_Full] = {
    147		.eth_mode = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
    148		.speed = 40000,
    149		.pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseCR4_Full,
    150		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    151		.port_type = PRESTERA_PORT_TYPE_DA,
    152	},
    153	[PRESTERA_LINK_MODE_40GbaseSR4_Full] = {
    154		.eth_mode = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
    155		.speed = 40000,
    156		.pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseSR4_Full,
    157		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    158		.port_type = PRESTERA_PORT_TYPE_FIBRE,
    159	},
    160	[PRESTERA_LINK_MODE_50GbaseCR2_Full] = {
    161		.eth_mode = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
    162		.speed = 50000,
    163		.pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseCR2_Full,
    164		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    165		.port_type = PRESTERA_PORT_TYPE_DA,
    166	},
    167	[PRESTERA_LINK_MODE_50GbaseKR2_Full] = {
    168		.eth_mode = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
    169		.speed = 50000,
    170		.pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseKR2_Full,
    171		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    172		.port_type = PRESTERA_PORT_TYPE_TP,
    173	},
    174	[PRESTERA_LINK_MODE_50GbaseSR2_Full] = {
    175		.eth_mode = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
    176		.speed = 50000,
    177		.pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseSR2_Full,
    178		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    179		.port_type = PRESTERA_PORT_TYPE_FIBRE,
    180	},
    181	[PRESTERA_LINK_MODE_100GbaseKR4_Full] = {
    182		.eth_mode = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
    183		.speed = 100000,
    184		.pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseKR4_Full,
    185		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    186		.port_type = PRESTERA_PORT_TYPE_TP,
    187	},
    188	[PRESTERA_LINK_MODE_100GbaseSR4_Full] = {
    189		.eth_mode = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
    190		.speed = 100000,
    191		.pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseSR4_Full,
    192		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    193		.port_type = PRESTERA_PORT_TYPE_FIBRE,
    194	},
    195	[PRESTERA_LINK_MODE_100GbaseCR4_Full] = {
    196		.eth_mode = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
    197		.speed = 100000,
    198		.pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseCR4_Full,
    199		.duplex = PRESTERA_PORT_DUPLEX_FULL,
    200		.port_type = PRESTERA_PORT_TYPE_DA,
    201	}
    202};
    203
    204static const struct prestera_fec {
    205	u32 eth_fec;
    206	enum ethtool_link_mode_bit_indices eth_mode;
    207	u8 pr_fec;
    208} port_fec_caps[PRESTERA_PORT_FEC_MAX] = {
    209	[PRESTERA_PORT_FEC_OFF] = {
    210		.eth_fec = ETHTOOL_FEC_OFF,
    211		.eth_mode = ETHTOOL_LINK_MODE_FEC_NONE_BIT,
    212		.pr_fec = 1 << PRESTERA_PORT_FEC_OFF,
    213	},
    214	[PRESTERA_PORT_FEC_BASER] = {
    215		.eth_fec = ETHTOOL_FEC_BASER,
    216		.eth_mode = ETHTOOL_LINK_MODE_FEC_BASER_BIT,
    217		.pr_fec = 1 << PRESTERA_PORT_FEC_BASER,
    218	},
    219	[PRESTERA_PORT_FEC_RS] = {
    220		.eth_fec = ETHTOOL_FEC_RS,
    221		.eth_mode = ETHTOOL_LINK_MODE_FEC_RS_BIT,
    222		.pr_fec = 1 << PRESTERA_PORT_FEC_RS,
    223	}
    224};
    225
    226static const struct prestera_port_type {
    227	enum ethtool_link_mode_bit_indices eth_mode;
    228	u8 eth_type;
    229} port_types[PRESTERA_PORT_TYPE_MAX] = {
    230	[PRESTERA_PORT_TYPE_NONE] = {
    231		.eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS,
    232		.eth_type = PORT_NONE,
    233	},
    234	[PRESTERA_PORT_TYPE_TP] = {
    235		.eth_mode = ETHTOOL_LINK_MODE_TP_BIT,
    236		.eth_type = PORT_TP,
    237	},
    238	[PRESTERA_PORT_TYPE_AUI] = {
    239		.eth_mode = ETHTOOL_LINK_MODE_AUI_BIT,
    240		.eth_type = PORT_AUI,
    241	},
    242	[PRESTERA_PORT_TYPE_MII] = {
    243		.eth_mode = ETHTOOL_LINK_MODE_MII_BIT,
    244		.eth_type = PORT_MII,
    245	},
    246	[PRESTERA_PORT_TYPE_FIBRE] = {
    247		.eth_mode = ETHTOOL_LINK_MODE_FIBRE_BIT,
    248		.eth_type = PORT_FIBRE,
    249	},
    250	[PRESTERA_PORT_TYPE_BNC] = {
    251		.eth_mode = ETHTOOL_LINK_MODE_BNC_BIT,
    252		.eth_type = PORT_BNC,
    253	},
    254	[PRESTERA_PORT_TYPE_DA] = {
    255		.eth_mode = ETHTOOL_LINK_MODE_TP_BIT,
    256		.eth_type = PORT_TP,
    257	},
    258	[PRESTERA_PORT_TYPE_OTHER] = {
    259		.eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS,
    260		.eth_type = PORT_OTHER,
    261	}
    262};
    263
    264static const char prestera_cnt_name[PRESTERA_STATS_CNT][ETH_GSTRING_LEN] = {
    265	PRESTERA_STATS_FIELD(good_octets_received),
    266	PRESTERA_STATS_FIELD(bad_octets_received),
    267	PRESTERA_STATS_FIELD(mac_trans_error),
    268	PRESTERA_STATS_FIELD(broadcast_frames_received),
    269	PRESTERA_STATS_FIELD(multicast_frames_received),
    270	PRESTERA_STATS_FIELD(frames_64_octets),
    271	PRESTERA_STATS_FIELD(frames_65_to_127_octets),
    272	PRESTERA_STATS_FIELD(frames_128_to_255_octets),
    273	PRESTERA_STATS_FIELD(frames_256_to_511_octets),
    274	PRESTERA_STATS_FIELD(frames_512_to_1023_octets),
    275	PRESTERA_STATS_FIELD(frames_1024_to_max_octets),
    276	PRESTERA_STATS_FIELD(excessive_collision),
    277	PRESTERA_STATS_FIELD(multicast_frames_sent),
    278	PRESTERA_STATS_FIELD(broadcast_frames_sent),
    279	PRESTERA_STATS_FIELD(fc_sent),
    280	PRESTERA_STATS_FIELD(fc_received),
    281	PRESTERA_STATS_FIELD(buffer_overrun),
    282	PRESTERA_STATS_FIELD(undersize),
    283	PRESTERA_STATS_FIELD(fragments),
    284	PRESTERA_STATS_FIELD(oversize),
    285	PRESTERA_STATS_FIELD(jabber),
    286	PRESTERA_STATS_FIELD(rx_error_frame_received),
    287	PRESTERA_STATS_FIELD(bad_crc),
    288	PRESTERA_STATS_FIELD(collisions),
    289	PRESTERA_STATS_FIELD(late_collision),
    290	PRESTERA_STATS_FIELD(unicast_frames_received),
    291	PRESTERA_STATS_FIELD(unicast_frames_sent),
    292	PRESTERA_STATS_FIELD(sent_multiple),
    293	PRESTERA_STATS_FIELD(sent_deferred),
    294	PRESTERA_STATS_FIELD(good_octets_sent),
    295};
    296
    297static void prestera_ethtool_get_drvinfo(struct net_device *dev,
    298					 struct ethtool_drvinfo *drvinfo)
    299{
    300	struct prestera_port *port = netdev_priv(dev);
    301	struct prestera_switch *sw = port->sw;
    302
    303	strlcpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver));
    304	strlcpy(drvinfo->bus_info, dev_name(prestera_dev(sw)),
    305		sizeof(drvinfo->bus_info));
    306	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
    307		 "%d.%d.%d",
    308		 sw->dev->fw_rev.maj,
    309		 sw->dev->fw_rev.min,
    310		 sw->dev->fw_rev.sub);
    311}
    312
    313static u8 prestera_port_type_get(struct prestera_port *port)
    314{
    315	if (port->caps.type < PRESTERA_PORT_TYPE_MAX)
    316		return port_types[port->caps.type].eth_type;
    317
    318	return PORT_OTHER;
    319}
    320
    321static int prestera_port_type_set(const struct ethtool_link_ksettings *ecmd,
    322				  struct prestera_port *port)
    323{
    324	u32 new_mode = PRESTERA_LINK_MODE_MAX;
    325	u32 type, mode;
    326
    327	for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) {
    328		if (port_types[type].eth_type == ecmd->base.port &&
    329		    test_bit(port_types[type].eth_mode,
    330			     ecmd->link_modes.supported)) {
    331			break;
    332		}
    333	}
    334
    335	if (type == port->caps.type)
    336		return 0;
    337	if (type != port->caps.type && ecmd->base.autoneg == AUTONEG_ENABLE)
    338		return -EINVAL;
    339	if (type == PRESTERA_PORT_TYPE_MAX)
    340		return -EOPNOTSUPP;
    341
    342	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
    343		if ((port_link_modes[mode].pr_mask &
    344		    port->caps.supp_link_modes) &&
    345		    type == port_link_modes[mode].port_type) {
    346			new_mode = mode;
    347		}
    348	}
    349
    350	if (new_mode >= PRESTERA_LINK_MODE_MAX)
    351		return -EINVAL;
    352
    353	port->caps.type = type;
    354	port->autoneg = false;
    355
    356	return 0;
    357}
    358
    359static void prestera_modes_to_eth(unsigned long *eth_modes, u64 link_modes,
    360				  u8 fec, u8 type)
    361{
    362	u32 mode;
    363
    364	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
    365		if ((port_link_modes[mode].pr_mask & link_modes) == 0)
    366			continue;
    367
    368		if (type != PRESTERA_PORT_TYPE_NONE &&
    369		    port_link_modes[mode].port_type != type)
    370			continue;
    371
    372		__set_bit(port_link_modes[mode].eth_mode, eth_modes);
    373	}
    374
    375	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
    376		if ((port_fec_caps[mode].pr_fec & fec) == 0)
    377			continue;
    378
    379		__set_bit(port_fec_caps[mode].eth_mode, eth_modes);
    380	}
    381}
    382
    383static void prestera_modes_from_eth(const unsigned long *eth_modes,
    384				    u64 *link_modes, u8 *fec, u8 type)
    385{
    386	u64 adver_modes = 0;
    387	u32 fec_modes = 0;
    388	u32 mode;
    389
    390	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
    391		if (!test_bit(port_link_modes[mode].eth_mode, eth_modes))
    392			continue;
    393
    394		if (port_link_modes[mode].port_type != type)
    395			continue;
    396
    397		adver_modes |= port_link_modes[mode].pr_mask;
    398	}
    399
    400	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
    401		if (!test_bit(port_fec_caps[mode].eth_mode, eth_modes))
    402			continue;
    403
    404		fec_modes |= port_fec_caps[mode].pr_fec;
    405	}
    406
    407	*link_modes = adver_modes;
    408	*fec = fec_modes;
    409}
    410
    411static void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd,
    412					 struct prestera_port *port)
    413{
    414	u32 mode;
    415	u8 ptype;
    416
    417	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
    418		if ((port_link_modes[mode].pr_mask &
    419		    port->caps.supp_link_modes) == 0)
    420			continue;
    421
    422		ptype = port_link_modes[mode].port_type;
    423		__set_bit(port_types[ptype].eth_mode,
    424			  ecmd->link_modes.supported);
    425	}
    426}
    427
    428static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd,
    429					 struct prestera_port *port)
    430{
    431	struct prestera_port_phy_state *state = &port->state_phy;
    432	bool asym_pause;
    433	bool pause;
    434	u64 bitmap;
    435	int err;
    436
    437	err = prestera_hw_port_phy_mode_get(port, NULL, &state->lmode_bmap,
    438					    &state->remote_fc.pause,
    439					    &state->remote_fc.asym_pause);
    440	if (err)
    441		netdev_warn(port->dev, "Remote link caps get failed %d",
    442			    port->caps.transceiver);
    443
    444	bitmap = state->lmode_bmap;
    445
    446	prestera_modes_to_eth(ecmd->link_modes.lp_advertising,
    447			      bitmap, 0, PRESTERA_PORT_TYPE_NONE);
    448
    449	if (!bitmap_empty(ecmd->link_modes.lp_advertising,
    450			  __ETHTOOL_LINK_MODE_MASK_NBITS)) {
    451		ethtool_link_ksettings_add_link_mode(ecmd,
    452						     lp_advertising,
    453						     Autoneg);
    454	}
    455
    456	pause = state->remote_fc.pause;
    457	asym_pause = state->remote_fc.asym_pause;
    458
    459	if (pause)
    460		ethtool_link_ksettings_add_link_mode(ecmd,
    461						     lp_advertising,
    462						     Pause);
    463	if (asym_pause)
    464		ethtool_link_ksettings_add_link_mode(ecmd,
    465						     lp_advertising,
    466						     Asym_Pause);
    467}
    468
    469static void prestera_port_link_mode_get(struct ethtool_link_ksettings *ecmd,
    470					struct prestera_port *port)
    471{
    472	struct prestera_port_mac_state *state = &port->state_mac;
    473	u32 speed;
    474	u8 duplex;
    475	int err;
    476
    477	if (!port->state_mac.oper)
    478		return;
    479
    480	if (state->speed == SPEED_UNKNOWN || state->duplex == DUPLEX_UNKNOWN) {
    481		err = prestera_hw_port_mac_mode_get(port, NULL, &speed,
    482						    &duplex, NULL);
    483		if (err) {
    484			state->speed = SPEED_UNKNOWN;
    485			state->duplex = DUPLEX_UNKNOWN;
    486		} else {
    487			state->speed = speed;
    488			state->duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ?
    489					  DUPLEX_FULL : DUPLEX_HALF;
    490		}
    491	}
    492
    493	ecmd->base.speed = port->state_mac.speed;
    494	ecmd->base.duplex = port->state_mac.duplex;
    495}
    496
    497static void prestera_port_mdix_get(struct ethtool_link_ksettings *ecmd,
    498				   struct prestera_port *port)
    499{
    500	struct prestera_port_phy_state *state = &port->state_phy;
    501
    502	if (prestera_hw_port_phy_mode_get(port,
    503					  &state->mdix, NULL, NULL, NULL)) {
    504		netdev_warn(port->dev, "MDIX params get failed");
    505		state->mdix = ETH_TP_MDI_INVALID;
    506	}
    507
    508	ecmd->base.eth_tp_mdix = port->state_phy.mdix;
    509	ecmd->base.eth_tp_mdix_ctrl = port->cfg_phy.mdix;
    510}
    511
    512static int
    513prestera_ethtool_get_link_ksettings(struct net_device *dev,
    514				    struct ethtool_link_ksettings *ecmd)
    515{
    516	struct prestera_port *port = netdev_priv(dev);
    517
    518	ethtool_link_ksettings_zero_link_mode(ecmd, supported);
    519	ethtool_link_ksettings_zero_link_mode(ecmd, advertising);
    520	ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising);
    521	ecmd->base.speed = SPEED_UNKNOWN;
    522	ecmd->base.duplex = DUPLEX_UNKNOWN;
    523
    524	ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
    525
    526	if (port->caps.type == PRESTERA_PORT_TYPE_TP) {
    527		ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg);
    528
    529		if (netif_running(dev) &&
    530		    (port->autoneg ||
    531		     port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER))
    532			ethtool_link_ksettings_add_link_mode(ecmd, advertising,
    533							     Autoneg);
    534	}
    535
    536	prestera_modes_to_eth(ecmd->link_modes.supported,
    537			      port->caps.supp_link_modes,
    538			      port->caps.supp_fec,
    539			      port->caps.type);
    540
    541	prestera_port_supp_types_get(ecmd, port);
    542
    543	if (netif_carrier_ok(dev))
    544		prestera_port_link_mode_get(ecmd, port);
    545
    546	ecmd->base.port = prestera_port_type_get(port);
    547
    548	if (port->autoneg) {
    549		if (netif_running(dev))
    550			prestera_modes_to_eth(ecmd->link_modes.advertising,
    551					      port->adver_link_modes,
    552					      port->adver_fec,
    553					      port->caps.type);
    554
    555		if (netif_carrier_ok(dev) &&
    556		    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
    557			prestera_port_remote_cap_get(ecmd, port);
    558	}
    559
    560	if (port->caps.type == PRESTERA_PORT_TYPE_TP &&
    561	    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
    562		prestera_port_mdix_get(ecmd, port);
    563
    564	return 0;
    565}
    566
    567static int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd,
    568				  struct prestera_port *port)
    569{
    570	if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID &&
    571	    port->caps.transceiver ==  PRESTERA_PORT_TCVR_COPPER &&
    572	    port->caps.type == PRESTERA_PORT_TYPE_TP) {
    573		port->cfg_phy.mdix = ecmd->base.eth_tp_mdix_ctrl;
    574		return prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,
    575						     port->autoneg,
    576						     port->cfg_phy.mode,
    577						     port->adver_link_modes,
    578						     port->cfg_phy.mdix);
    579	}
    580	return 0;
    581
    582}
    583
    584static int prestera_port_link_mode_set(struct prestera_port *port,
    585				       u32 speed, u8 duplex, u8 type)
    586{
    587	u32 new_mode = PRESTERA_LINK_MODE_MAX;
    588	u32 mode;
    589	int err;
    590
    591	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
    592		if (speed != SPEED_UNKNOWN &&
    593		    speed != port_link_modes[mode].speed)
    594			continue;
    595
    596		if (duplex != DUPLEX_UNKNOWN &&
    597		    duplex != port_link_modes[mode].duplex)
    598			continue;
    599
    600		if (!(port_link_modes[mode].pr_mask &
    601		    port->caps.supp_link_modes))
    602			continue;
    603
    604		if (type != port_link_modes[mode].port_type)
    605			continue;
    606
    607		new_mode = mode;
    608		break;
    609	}
    610
    611	if (new_mode == PRESTERA_LINK_MODE_MAX)
    612		return -EOPNOTSUPP;
    613
    614	err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,
    615					    false, new_mode, 0,
    616					    port->cfg_phy.mdix);
    617	if (err)
    618		return err;
    619
    620	port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);
    621	port->adver_link_modes = 0;
    622	port->cfg_phy.mode = new_mode;
    623	port->autoneg = false;
    624
    625	return 0;
    626}
    627
    628static int
    629prestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd,
    630			       struct prestera_port *port)
    631{
    632	u8 duplex = DUPLEX_UNKNOWN;
    633
    634	if (ecmd->base.duplex != DUPLEX_UNKNOWN)
    635		duplex = ecmd->base.duplex == DUPLEX_FULL ?
    636			 PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF;
    637
    638	return prestera_port_link_mode_set(port, ecmd->base.speed, duplex,
    639					   port->caps.type);
    640}
    641
    642static int
    643prestera_ethtool_set_link_ksettings(struct net_device *dev,
    644				    const struct ethtool_link_ksettings *ecmd)
    645{
    646	struct prestera_port *port = netdev_priv(dev);
    647	u64 adver_modes;
    648	u8 adver_fec;
    649	int err;
    650
    651	err = prestera_port_type_set(ecmd, port);
    652	if (err)
    653		return err;
    654
    655	if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) {
    656		err = prestera_port_mdix_set(ecmd, port);
    657		if (err)
    658			return err;
    659	}
    660
    661	prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes,
    662				&adver_fec, port->caps.type);
    663
    664	if (ecmd->base.autoneg == AUTONEG_ENABLE)
    665		err = prestera_port_autoneg_set(port, adver_modes);
    666	else
    667		err = prestera_port_speed_duplex_set(ecmd, port);
    668
    669	return err;
    670}
    671
    672static int prestera_ethtool_get_fecparam(struct net_device *dev,
    673					 struct ethtool_fecparam *fecparam)
    674{
    675	struct prestera_port *port = netdev_priv(dev);
    676	u8 active;
    677	u32 mode;
    678	int err;
    679
    680	err = prestera_hw_port_mac_mode_get(port, NULL, NULL, NULL, &active);
    681	if (err)
    682		return err;
    683
    684	fecparam->fec = 0;
    685
    686	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
    687		if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0)
    688			continue;
    689
    690		fecparam->fec |= port_fec_caps[mode].eth_fec;
    691	}
    692
    693	if (active < PRESTERA_PORT_FEC_MAX)
    694		fecparam->active_fec = port_fec_caps[active].eth_fec;
    695	else
    696		fecparam->active_fec = ETHTOOL_FEC_AUTO;
    697
    698	return 0;
    699}
    700
    701static int prestera_ethtool_set_fecparam(struct net_device *dev,
    702					 struct ethtool_fecparam *fecparam)
    703{
    704	struct prestera_port *port = netdev_priv(dev);
    705	struct prestera_port_mac_config cfg_mac;
    706	u32 mode;
    707	u8 fec;
    708
    709	if (port->autoneg) {
    710		netdev_err(dev, "FEC set is not allowed while autoneg is on\n");
    711		return -EINVAL;
    712	}
    713
    714	if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) {
    715		netdev_err(dev, "FEC set is not allowed on non-SFP ports\n");
    716		return -EINVAL;
    717	}
    718
    719	fec = PRESTERA_PORT_FEC_MAX;
    720	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
    721		if ((port_fec_caps[mode].eth_fec & fecparam->fec) &&
    722		    (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) {
    723			fec = mode;
    724			break;
    725		}
    726	}
    727
    728	prestera_port_cfg_mac_read(port, &cfg_mac);
    729
    730	if (fec == cfg_mac.fec)
    731		return 0;
    732
    733	if (fec == PRESTERA_PORT_FEC_MAX) {
    734		netdev_err(dev, "Unsupported FEC requested");
    735		return -EINVAL;
    736	}
    737
    738	cfg_mac.fec = fec;
    739
    740	return prestera_port_cfg_mac_write(port, &cfg_mac);
    741}
    742
    743static int prestera_ethtool_get_sset_count(struct net_device *dev, int sset)
    744{
    745	switch (sset) {
    746	case ETH_SS_STATS:
    747		return PRESTERA_STATS_CNT;
    748	default:
    749		return -EOPNOTSUPP;
    750	}
    751}
    752
    753static void prestera_ethtool_get_strings(struct net_device *dev,
    754					 u32 stringset, u8 *data)
    755{
    756	if (stringset != ETH_SS_STATS)
    757		return;
    758
    759	memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name));
    760}
    761
    762static void prestera_ethtool_get_stats(struct net_device *dev,
    763				       struct ethtool_stats *stats, u64 *data)
    764{
    765	struct prestera_port *port = netdev_priv(dev);
    766	struct prestera_port_stats *port_stats;
    767
    768	port_stats = &port->cached_hw_stats.stats;
    769
    770	memcpy(data, port_stats, sizeof(*port_stats));
    771}
    772
    773static int prestera_ethtool_nway_reset(struct net_device *dev)
    774{
    775	struct prestera_port *port = netdev_priv(dev);
    776
    777	if (netif_running(dev) &&
    778	    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&
    779	    port->caps.type == PRESTERA_PORT_TYPE_TP)
    780		return prestera_hw_port_autoneg_restart(port);
    781
    782	return -EINVAL;
    783}
    784
    785void prestera_ethtool_port_state_changed(struct prestera_port *port,
    786					 struct prestera_port_event *evt)
    787{
    788	struct prestera_port_mac_state *smac = &port->state_mac;
    789
    790	smac->oper = evt->data.mac.oper;
    791
    792	if (smac->oper) {
    793		smac->mode = evt->data.mac.mode;
    794		smac->speed = evt->data.mac.speed;
    795		smac->duplex = evt->data.mac.duplex;
    796		smac->fc = evt->data.mac.fc;
    797		smac->fec = evt->data.mac.fec;
    798	} else {
    799		smac->mode = PRESTERA_MAC_MODE_MAX;
    800		smac->speed = SPEED_UNKNOWN;
    801		smac->duplex = DUPLEX_UNKNOWN;
    802		smac->fc = 0;
    803		smac->fec = 0;
    804	}
    805}
    806
    807const struct ethtool_ops prestera_ethtool_ops = {
    808	.get_drvinfo = prestera_ethtool_get_drvinfo,
    809	.get_link_ksettings = prestera_ethtool_get_link_ksettings,
    810	.set_link_ksettings = prestera_ethtool_set_link_ksettings,
    811	.get_fecparam = prestera_ethtool_get_fecparam,
    812	.set_fecparam = prestera_ethtool_set_fecparam,
    813	.get_sset_count = prestera_ethtool_get_sset_count,
    814	.get_strings = prestera_ethtool_get_strings,
    815	.get_ethtool_stats = prestera_ethtool_get_stats,
    816	.get_link = ethtool_op_get_link,
    817	.nway_reset = prestera_ethtool_nway_reset
    818};