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