brintos

brintos / linux-shallow public Read only

0
0
Text · 22.3 KiB · 2f52dab Raw
803 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* 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	strscpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver));304	strscpy(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 int513prestera_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	if (port->phy_link)525		return phylink_ethtool_ksettings_get(port->phy_link, ecmd);526 527	ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;528 529	if (port->caps.type == PRESTERA_PORT_TYPE_TP) {530		ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg);531 532		if (netif_running(dev) &&533		    (port->autoneg ||534		     port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER))535			ethtool_link_ksettings_add_link_mode(ecmd, advertising,536							     Autoneg);537	}538 539	prestera_modes_to_eth(ecmd->link_modes.supported,540			      port->caps.supp_link_modes,541			      port->caps.supp_fec,542			      port->caps.type);543 544	prestera_port_supp_types_get(ecmd, port);545 546	if (netif_carrier_ok(dev))547		prestera_port_link_mode_get(ecmd, port);548 549	ecmd->base.port = prestera_port_type_get(port);550 551	if (port->autoneg) {552		if (netif_running(dev))553			prestera_modes_to_eth(ecmd->link_modes.advertising,554					      port->adver_link_modes,555					      port->adver_fec,556					      port->caps.type);557 558		if (netif_carrier_ok(dev) &&559		    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)560			prestera_port_remote_cap_get(ecmd, port);561	}562 563	if (port->caps.type == PRESTERA_PORT_TYPE_TP &&564	    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)565		prestera_port_mdix_get(ecmd, port);566 567	return 0;568}569 570static int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd,571				  struct prestera_port *port)572{573	if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID &&574	    port->caps.transceiver ==  PRESTERA_PORT_TCVR_COPPER &&575	    port->caps.type == PRESTERA_PORT_TYPE_TP) {576		port->cfg_phy.mdix = ecmd->base.eth_tp_mdix_ctrl;577		return prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,578						     port->autoneg,579						     port->cfg_phy.mode,580						     port->adver_link_modes,581						     port->cfg_phy.mdix);582	}583	return 0;584 585}586 587static int prestera_port_link_mode_set(struct prestera_port *port,588				       u32 speed, u8 duplex, u8 type)589{590	u32 new_mode = PRESTERA_LINK_MODE_MAX;591	u32 mode;592	int err;593 594	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {595		if (speed != SPEED_UNKNOWN &&596		    speed != port_link_modes[mode].speed)597			continue;598 599		if (duplex != DUPLEX_UNKNOWN &&600		    duplex != port_link_modes[mode].duplex)601			continue;602 603		if (!(port_link_modes[mode].pr_mask &604		    port->caps.supp_link_modes))605			continue;606 607		if (type != port_link_modes[mode].port_type)608			continue;609 610		new_mode = mode;611		break;612	}613 614	if (new_mode == PRESTERA_LINK_MODE_MAX)615		return -EOPNOTSUPP;616 617	err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,618					    false, new_mode, 0,619					    port->cfg_phy.mdix);620	if (err)621		return err;622 623	port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);624	port->adver_link_modes = 0;625	port->cfg_phy.mode = new_mode;626	port->autoneg = false;627 628	return 0;629}630 631static int632prestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd,633			       struct prestera_port *port)634{635	u8 duplex = DUPLEX_UNKNOWN;636 637	if (ecmd->base.duplex != DUPLEX_UNKNOWN)638		duplex = ecmd->base.duplex == DUPLEX_FULL ?639			 PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF;640 641	return prestera_port_link_mode_set(port, ecmd->base.speed, duplex,642					   port->caps.type);643}644 645static int646prestera_ethtool_set_link_ksettings(struct net_device *dev,647				    const struct ethtool_link_ksettings *ecmd)648{649	struct prestera_port *port = netdev_priv(dev);650	u64 adver_modes;651	u8 adver_fec;652	int err;653 654	if (port->phy_link)655		return phylink_ethtool_ksettings_set(port->phy_link, ecmd);656 657	err = prestera_port_type_set(ecmd, port);658	if (err)659		return err;660 661	if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) {662		err = prestera_port_mdix_set(ecmd, port);663		if (err)664			return err;665	}666 667	prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes,668				&adver_fec, port->caps.type);669 670	if (ecmd->base.autoneg == AUTONEG_ENABLE)671		err = prestera_port_autoneg_set(port, adver_modes);672	else673		err = prestera_port_speed_duplex_set(ecmd, port);674 675	return err;676}677 678static int prestera_ethtool_get_fecparam(struct net_device *dev,679					 struct ethtool_fecparam *fecparam)680{681	struct prestera_port *port = netdev_priv(dev);682	u8 active;683	u32 mode;684	int err;685 686	err = prestera_hw_port_mac_mode_get(port, NULL, NULL, NULL, &active);687	if (err)688		return err;689 690	fecparam->fec = 0;691 692	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {693		if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0)694			continue;695 696		fecparam->fec |= port_fec_caps[mode].eth_fec;697	}698 699	if (active < PRESTERA_PORT_FEC_MAX)700		fecparam->active_fec = port_fec_caps[active].eth_fec;701	else702		fecparam->active_fec = ETHTOOL_FEC_AUTO;703 704	return 0;705}706 707static int prestera_ethtool_set_fecparam(struct net_device *dev,708					 struct ethtool_fecparam *fecparam)709{710	struct prestera_port *port = netdev_priv(dev);711	struct prestera_port_mac_config cfg_mac;712	u32 mode;713	u8 fec;714 715	if (port->autoneg) {716		netdev_err(dev, "FEC set is not allowed while autoneg is on\n");717		return -EINVAL;718	}719 720	if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) {721		netdev_err(dev, "FEC set is not allowed on non-SFP ports\n");722		return -EINVAL;723	}724 725	fec = PRESTERA_PORT_FEC_MAX;726	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {727		if ((port_fec_caps[mode].eth_fec & fecparam->fec) &&728		    (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) {729			fec = mode;730			break;731		}732	}733 734	prestera_port_cfg_mac_read(port, &cfg_mac);735 736	if (fec == cfg_mac.fec)737		return 0;738 739	if (fec == PRESTERA_PORT_FEC_MAX) {740		netdev_err(dev, "Unsupported FEC requested");741		return -EINVAL;742	}743 744	cfg_mac.fec = fec;745 746	return prestera_port_cfg_mac_write(port, &cfg_mac);747}748 749static int prestera_ethtool_get_sset_count(struct net_device *dev, int sset)750{751	switch (sset) {752	case ETH_SS_STATS:753		return PRESTERA_STATS_CNT;754	default:755		return -EOPNOTSUPP;756	}757}758 759static void prestera_ethtool_get_strings(struct net_device *dev,760					 u32 stringset, u8 *data)761{762	if (stringset != ETH_SS_STATS)763		return;764 765	memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name));766}767 768static void prestera_ethtool_get_stats(struct net_device *dev,769				       struct ethtool_stats *stats, u64 *data)770{771	struct prestera_port *port = netdev_priv(dev);772	struct prestera_port_stats *port_stats;773 774	port_stats = &port->cached_hw_stats.stats;775 776	memcpy(data, port_stats, sizeof(*port_stats));777}778 779static int prestera_ethtool_nway_reset(struct net_device *dev)780{781	struct prestera_port *port = netdev_priv(dev);782 783	if (netif_running(dev) &&784	    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&785	    port->caps.type == PRESTERA_PORT_TYPE_TP)786		return prestera_hw_port_autoneg_restart(port);787 788	return -EINVAL;789}790 791const struct ethtool_ops prestera_ethtool_ops = {792	.get_drvinfo = prestera_ethtool_get_drvinfo,793	.get_link_ksettings = prestera_ethtool_get_link_ksettings,794	.set_link_ksettings = prestera_ethtool_set_link_ksettings,795	.get_fecparam = prestera_ethtool_get_fecparam,796	.set_fecparam = prestera_ethtool_set_fecparam,797	.get_sset_count = prestera_ethtool_get_sset_count,798	.get_strings = prestera_ethtool_get_strings,799	.get_ethtool_stats = prestera_ethtool_get_stats,800	.get_link = ethtool_op_get_link,801	.nway_reset = prestera_ethtool_nway_reset802};803