Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
0003 
0004 #include <linux/ethtool.h>
0005 #include <linux/kernel.h>
0006 #include <linux/netdevice.h>
0007 
0008 #include "prestera_ethtool.h"
0009 #include "prestera.h"
0010 #include "prestera_hw.h"
0011 
0012 #define PRESTERA_STATS_CNT \
0013     (sizeof(struct prestera_port_stats) / sizeof(u64))
0014 #define PRESTERA_STATS_IDX(name) \
0015     (offsetof(struct prestera_port_stats, name) / sizeof(u64))
0016 #define PRESTERA_STATS_FIELD(name)  \
0017     [PRESTERA_STATS_IDX(name)] = __stringify(name)
0018 
0019 static const char driver_kind[] = "prestera";
0020 
0021 static const struct prestera_link_mode {
0022     enum ethtool_link_mode_bit_indices eth_mode;
0023     u32 speed;
0024     u64 pr_mask;
0025     u8 duplex;
0026     u8 port_type;
0027 } port_link_modes[PRESTERA_LINK_MODE_MAX] = {
0028     [PRESTERA_LINK_MODE_10baseT_Half] = {
0029         .eth_mode =  ETHTOOL_LINK_MODE_10baseT_Half_BIT,
0030         .speed = 10,
0031         .pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Half,
0032         .duplex = PRESTERA_PORT_DUPLEX_HALF,
0033         .port_type = PRESTERA_PORT_TYPE_TP,
0034     },
0035     [PRESTERA_LINK_MODE_10baseT_Full] = {
0036         .eth_mode =  ETHTOOL_LINK_MODE_10baseT_Full_BIT,
0037         .speed = 10,
0038         .pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Full,
0039         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0040         .port_type = PRESTERA_PORT_TYPE_TP,
0041     },
0042     [PRESTERA_LINK_MODE_100baseT_Half] = {
0043         .eth_mode =  ETHTOOL_LINK_MODE_100baseT_Half_BIT,
0044         .speed = 100,
0045         .pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Half,
0046         .duplex = PRESTERA_PORT_DUPLEX_HALF,
0047         .port_type = PRESTERA_PORT_TYPE_TP,
0048     },
0049     [PRESTERA_LINK_MODE_100baseT_Full] = {
0050         .eth_mode =  ETHTOOL_LINK_MODE_100baseT_Full_BIT,
0051         .speed = 100,
0052         .pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Full,
0053         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0054         .port_type = PRESTERA_PORT_TYPE_TP,
0055     },
0056     [PRESTERA_LINK_MODE_1000baseT_Half] = {
0057         .eth_mode =  ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
0058         .speed = 1000,
0059         .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Half,
0060         .duplex = PRESTERA_PORT_DUPLEX_HALF,
0061         .port_type = PRESTERA_PORT_TYPE_TP,
0062     },
0063     [PRESTERA_LINK_MODE_1000baseT_Full] = {
0064         .eth_mode =  ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
0065         .speed = 1000,
0066         .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Full,
0067         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0068         .port_type = PRESTERA_PORT_TYPE_TP,
0069     },
0070     [PRESTERA_LINK_MODE_1000baseX_Full] = {
0071         .eth_mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
0072         .speed = 1000,
0073         .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseX_Full,
0074         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0075         .port_type = PRESTERA_PORT_TYPE_FIBRE,
0076     },
0077     [PRESTERA_LINK_MODE_1000baseKX_Full] = {
0078         .eth_mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
0079         .speed = 1000,
0080         .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseKX_Full,
0081         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0082         .port_type = PRESTERA_PORT_TYPE_TP,
0083     },
0084     [PRESTERA_LINK_MODE_2500baseX_Full] = {
0085         .eth_mode =  ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
0086         .speed = 2500,
0087         .pr_mask = 1 << PRESTERA_LINK_MODE_2500baseX_Full,
0088         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0089     },
0090     [PRESTERA_LINK_MODE_10GbaseKR_Full] = {
0091         .eth_mode = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
0092         .speed = 10000,
0093         .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseKR_Full,
0094         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0095         .port_type = PRESTERA_PORT_TYPE_TP,
0096     },
0097     [PRESTERA_LINK_MODE_10GbaseSR_Full] = {
0098         .eth_mode = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
0099         .speed = 10000,
0100         .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseSR_Full,
0101         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0102         .port_type = PRESTERA_PORT_TYPE_FIBRE,
0103     },
0104     [PRESTERA_LINK_MODE_10GbaseLR_Full] = {
0105         .eth_mode = ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
0106         .speed = 10000,
0107         .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseLR_Full,
0108         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0109         .port_type = PRESTERA_PORT_TYPE_FIBRE,
0110     },
0111     [PRESTERA_LINK_MODE_20GbaseKR2_Full] = {
0112         .eth_mode = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
0113         .speed = 20000,
0114         .pr_mask = 1 << PRESTERA_LINK_MODE_20GbaseKR2_Full,
0115         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0116         .port_type = PRESTERA_PORT_TYPE_TP,
0117     },
0118     [PRESTERA_LINK_MODE_25GbaseCR_Full] = {
0119         .eth_mode = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
0120         .speed = 25000,
0121         .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseCR_Full,
0122         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0123         .port_type = PRESTERA_PORT_TYPE_DA,
0124     },
0125     [PRESTERA_LINK_MODE_25GbaseKR_Full] = {
0126         .eth_mode = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
0127         .speed = 25000,
0128         .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseKR_Full,
0129         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0130         .port_type = PRESTERA_PORT_TYPE_TP,
0131     },
0132     [PRESTERA_LINK_MODE_25GbaseSR_Full] = {
0133         .eth_mode = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
0134         .speed = 25000,
0135         .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseSR_Full,
0136         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0137         .port_type = PRESTERA_PORT_TYPE_FIBRE,
0138     },
0139     [PRESTERA_LINK_MODE_40GbaseKR4_Full] = {
0140         .eth_mode = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
0141         .speed = 40000,
0142         .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseKR4_Full,
0143         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0144         .port_type = PRESTERA_PORT_TYPE_TP,
0145     },
0146     [PRESTERA_LINK_MODE_40GbaseCR4_Full] = {
0147         .eth_mode = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
0148         .speed = 40000,
0149         .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseCR4_Full,
0150         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0151         .port_type = PRESTERA_PORT_TYPE_DA,
0152     },
0153     [PRESTERA_LINK_MODE_40GbaseSR4_Full] = {
0154         .eth_mode = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
0155         .speed = 40000,
0156         .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseSR4_Full,
0157         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0158         .port_type = PRESTERA_PORT_TYPE_FIBRE,
0159     },
0160     [PRESTERA_LINK_MODE_50GbaseCR2_Full] = {
0161         .eth_mode = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
0162         .speed = 50000,
0163         .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseCR2_Full,
0164         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0165         .port_type = PRESTERA_PORT_TYPE_DA,
0166     },
0167     [PRESTERA_LINK_MODE_50GbaseKR2_Full] = {
0168         .eth_mode = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
0169         .speed = 50000,
0170         .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseKR2_Full,
0171         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0172         .port_type = PRESTERA_PORT_TYPE_TP,
0173     },
0174     [PRESTERA_LINK_MODE_50GbaseSR2_Full] = {
0175         .eth_mode = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
0176         .speed = 50000,
0177         .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseSR2_Full,
0178         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0179         .port_type = PRESTERA_PORT_TYPE_FIBRE,
0180     },
0181     [PRESTERA_LINK_MODE_100GbaseKR4_Full] = {
0182         .eth_mode = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
0183         .speed = 100000,
0184         .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseKR4_Full,
0185         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0186         .port_type = PRESTERA_PORT_TYPE_TP,
0187     },
0188     [PRESTERA_LINK_MODE_100GbaseSR4_Full] = {
0189         .eth_mode = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
0190         .speed = 100000,
0191         .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseSR4_Full,
0192         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0193         .port_type = PRESTERA_PORT_TYPE_FIBRE,
0194     },
0195     [PRESTERA_LINK_MODE_100GbaseCR4_Full] = {
0196         .eth_mode = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
0197         .speed = 100000,
0198         .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseCR4_Full,
0199         .duplex = PRESTERA_PORT_DUPLEX_FULL,
0200         .port_type = PRESTERA_PORT_TYPE_DA,
0201     }
0202 };
0203 
0204 static const struct prestera_fec {
0205     u32 eth_fec;
0206     enum ethtool_link_mode_bit_indices eth_mode;
0207     u8 pr_fec;
0208 } port_fec_caps[PRESTERA_PORT_FEC_MAX] = {
0209     [PRESTERA_PORT_FEC_OFF] = {
0210         .eth_fec = ETHTOOL_FEC_OFF,
0211         .eth_mode = ETHTOOL_LINK_MODE_FEC_NONE_BIT,
0212         .pr_fec = 1 << PRESTERA_PORT_FEC_OFF,
0213     },
0214     [PRESTERA_PORT_FEC_BASER] = {
0215         .eth_fec = ETHTOOL_FEC_BASER,
0216         .eth_mode = ETHTOOL_LINK_MODE_FEC_BASER_BIT,
0217         .pr_fec = 1 << PRESTERA_PORT_FEC_BASER,
0218     },
0219     [PRESTERA_PORT_FEC_RS] = {
0220         .eth_fec = ETHTOOL_FEC_RS,
0221         .eth_mode = ETHTOOL_LINK_MODE_FEC_RS_BIT,
0222         .pr_fec = 1 << PRESTERA_PORT_FEC_RS,
0223     }
0224 };
0225 
0226 static const struct prestera_port_type {
0227     enum ethtool_link_mode_bit_indices eth_mode;
0228     u8 eth_type;
0229 } port_types[PRESTERA_PORT_TYPE_MAX] = {
0230     [PRESTERA_PORT_TYPE_NONE] = {
0231         .eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS,
0232         .eth_type = PORT_NONE,
0233     },
0234     [PRESTERA_PORT_TYPE_TP] = {
0235         .eth_mode = ETHTOOL_LINK_MODE_TP_BIT,
0236         .eth_type = PORT_TP,
0237     },
0238     [PRESTERA_PORT_TYPE_AUI] = {
0239         .eth_mode = ETHTOOL_LINK_MODE_AUI_BIT,
0240         .eth_type = PORT_AUI,
0241     },
0242     [PRESTERA_PORT_TYPE_MII] = {
0243         .eth_mode = ETHTOOL_LINK_MODE_MII_BIT,
0244         .eth_type = PORT_MII,
0245     },
0246     [PRESTERA_PORT_TYPE_FIBRE] = {
0247         .eth_mode = ETHTOOL_LINK_MODE_FIBRE_BIT,
0248         .eth_type = PORT_FIBRE,
0249     },
0250     [PRESTERA_PORT_TYPE_BNC] = {
0251         .eth_mode = ETHTOOL_LINK_MODE_BNC_BIT,
0252         .eth_type = PORT_BNC,
0253     },
0254     [PRESTERA_PORT_TYPE_DA] = {
0255         .eth_mode = ETHTOOL_LINK_MODE_TP_BIT,
0256         .eth_type = PORT_TP,
0257     },
0258     [PRESTERA_PORT_TYPE_OTHER] = {
0259         .eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS,
0260         .eth_type = PORT_OTHER,
0261     }
0262 };
0263 
0264 static const char prestera_cnt_name[PRESTERA_STATS_CNT][ETH_GSTRING_LEN] = {
0265     PRESTERA_STATS_FIELD(good_octets_received),
0266     PRESTERA_STATS_FIELD(bad_octets_received),
0267     PRESTERA_STATS_FIELD(mac_trans_error),
0268     PRESTERA_STATS_FIELD(broadcast_frames_received),
0269     PRESTERA_STATS_FIELD(multicast_frames_received),
0270     PRESTERA_STATS_FIELD(frames_64_octets),
0271     PRESTERA_STATS_FIELD(frames_65_to_127_octets),
0272     PRESTERA_STATS_FIELD(frames_128_to_255_octets),
0273     PRESTERA_STATS_FIELD(frames_256_to_511_octets),
0274     PRESTERA_STATS_FIELD(frames_512_to_1023_octets),
0275     PRESTERA_STATS_FIELD(frames_1024_to_max_octets),
0276     PRESTERA_STATS_FIELD(excessive_collision),
0277     PRESTERA_STATS_FIELD(multicast_frames_sent),
0278     PRESTERA_STATS_FIELD(broadcast_frames_sent),
0279     PRESTERA_STATS_FIELD(fc_sent),
0280     PRESTERA_STATS_FIELD(fc_received),
0281     PRESTERA_STATS_FIELD(buffer_overrun),
0282     PRESTERA_STATS_FIELD(undersize),
0283     PRESTERA_STATS_FIELD(fragments),
0284     PRESTERA_STATS_FIELD(oversize),
0285     PRESTERA_STATS_FIELD(jabber),
0286     PRESTERA_STATS_FIELD(rx_error_frame_received),
0287     PRESTERA_STATS_FIELD(bad_crc),
0288     PRESTERA_STATS_FIELD(collisions),
0289     PRESTERA_STATS_FIELD(late_collision),
0290     PRESTERA_STATS_FIELD(unicast_frames_received),
0291     PRESTERA_STATS_FIELD(unicast_frames_sent),
0292     PRESTERA_STATS_FIELD(sent_multiple),
0293     PRESTERA_STATS_FIELD(sent_deferred),
0294     PRESTERA_STATS_FIELD(good_octets_sent),
0295 };
0296 
0297 static void prestera_ethtool_get_drvinfo(struct net_device *dev,
0298                      struct ethtool_drvinfo *drvinfo)
0299 {
0300     struct prestera_port *port = netdev_priv(dev);
0301     struct prestera_switch *sw = port->sw;
0302 
0303     strlcpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver));
0304     strlcpy(drvinfo->bus_info, dev_name(prestera_dev(sw)),
0305         sizeof(drvinfo->bus_info));
0306     snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
0307          "%d.%d.%d",
0308          sw->dev->fw_rev.maj,
0309          sw->dev->fw_rev.min,
0310          sw->dev->fw_rev.sub);
0311 }
0312 
0313 static u8 prestera_port_type_get(struct prestera_port *port)
0314 {
0315     if (port->caps.type < PRESTERA_PORT_TYPE_MAX)
0316         return port_types[port->caps.type].eth_type;
0317 
0318     return PORT_OTHER;
0319 }
0320 
0321 static int prestera_port_type_set(const struct ethtool_link_ksettings *ecmd,
0322                   struct prestera_port *port)
0323 {
0324     u32 new_mode = PRESTERA_LINK_MODE_MAX;
0325     u32 type, mode;
0326 
0327     for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) {
0328         if (port_types[type].eth_type == ecmd->base.port &&
0329             test_bit(port_types[type].eth_mode,
0330                  ecmd->link_modes.supported)) {
0331             break;
0332         }
0333     }
0334 
0335     if (type == port->caps.type)
0336         return 0;
0337     if (type != port->caps.type && ecmd->base.autoneg == AUTONEG_ENABLE)
0338         return -EINVAL;
0339     if (type == PRESTERA_PORT_TYPE_MAX)
0340         return -EOPNOTSUPP;
0341 
0342     for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
0343         if ((port_link_modes[mode].pr_mask &
0344             port->caps.supp_link_modes) &&
0345             type == port_link_modes[mode].port_type) {
0346             new_mode = mode;
0347         }
0348     }
0349 
0350     if (new_mode >= PRESTERA_LINK_MODE_MAX)
0351         return -EINVAL;
0352 
0353     port->caps.type = type;
0354     port->autoneg = false;
0355 
0356     return 0;
0357 }
0358 
0359 static void prestera_modes_to_eth(unsigned long *eth_modes, u64 link_modes,
0360                   u8 fec, u8 type)
0361 {
0362     u32 mode;
0363 
0364     for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
0365         if ((port_link_modes[mode].pr_mask & link_modes) == 0)
0366             continue;
0367 
0368         if (type != PRESTERA_PORT_TYPE_NONE &&
0369             port_link_modes[mode].port_type != type)
0370             continue;
0371 
0372         __set_bit(port_link_modes[mode].eth_mode, eth_modes);
0373     }
0374 
0375     for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
0376         if ((port_fec_caps[mode].pr_fec & fec) == 0)
0377             continue;
0378 
0379         __set_bit(port_fec_caps[mode].eth_mode, eth_modes);
0380     }
0381 }
0382 
0383 static void prestera_modes_from_eth(const unsigned long *eth_modes,
0384                     u64 *link_modes, u8 *fec, u8 type)
0385 {
0386     u64 adver_modes = 0;
0387     u32 fec_modes = 0;
0388     u32 mode;
0389 
0390     for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
0391         if (!test_bit(port_link_modes[mode].eth_mode, eth_modes))
0392             continue;
0393 
0394         if (port_link_modes[mode].port_type != type)
0395             continue;
0396 
0397         adver_modes |= port_link_modes[mode].pr_mask;
0398     }
0399 
0400     for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
0401         if (!test_bit(port_fec_caps[mode].eth_mode, eth_modes))
0402             continue;
0403 
0404         fec_modes |= port_fec_caps[mode].pr_fec;
0405     }
0406 
0407     *link_modes = adver_modes;
0408     *fec = fec_modes;
0409 }
0410 
0411 static void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd,
0412                      struct prestera_port *port)
0413 {
0414     u32 mode;
0415     u8 ptype;
0416 
0417     for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
0418         if ((port_link_modes[mode].pr_mask &
0419             port->caps.supp_link_modes) == 0)
0420             continue;
0421 
0422         ptype = port_link_modes[mode].port_type;
0423         __set_bit(port_types[ptype].eth_mode,
0424               ecmd->link_modes.supported);
0425     }
0426 }
0427 
0428 static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd,
0429                      struct prestera_port *port)
0430 {
0431     struct prestera_port_phy_state *state = &port->state_phy;
0432     bool asym_pause;
0433     bool pause;
0434     u64 bitmap;
0435     int err;
0436 
0437     err = prestera_hw_port_phy_mode_get(port, NULL, &state->lmode_bmap,
0438                         &state->remote_fc.pause,
0439                         &state->remote_fc.asym_pause);
0440     if (err)
0441         netdev_warn(port->dev, "Remote link caps get failed %d",
0442                 port->caps.transceiver);
0443 
0444     bitmap = state->lmode_bmap;
0445 
0446     prestera_modes_to_eth(ecmd->link_modes.lp_advertising,
0447                   bitmap, 0, PRESTERA_PORT_TYPE_NONE);
0448 
0449     if (!bitmap_empty(ecmd->link_modes.lp_advertising,
0450               __ETHTOOL_LINK_MODE_MASK_NBITS)) {
0451         ethtool_link_ksettings_add_link_mode(ecmd,
0452                              lp_advertising,
0453                              Autoneg);
0454     }
0455 
0456     pause = state->remote_fc.pause;
0457     asym_pause = state->remote_fc.asym_pause;
0458 
0459     if (pause)
0460         ethtool_link_ksettings_add_link_mode(ecmd,
0461                              lp_advertising,
0462                              Pause);
0463     if (asym_pause)
0464         ethtool_link_ksettings_add_link_mode(ecmd,
0465                              lp_advertising,
0466                              Asym_Pause);
0467 }
0468 
0469 static void prestera_port_link_mode_get(struct ethtool_link_ksettings *ecmd,
0470                     struct prestera_port *port)
0471 {
0472     struct prestera_port_mac_state *state = &port->state_mac;
0473     u32 speed;
0474     u8 duplex;
0475     int err;
0476 
0477     if (!port->state_mac.oper)
0478         return;
0479 
0480     if (state->speed == SPEED_UNKNOWN || state->duplex == DUPLEX_UNKNOWN) {
0481         err = prestera_hw_port_mac_mode_get(port, NULL, &speed,
0482                             &duplex, NULL);
0483         if (err) {
0484             state->speed = SPEED_UNKNOWN;
0485             state->duplex = DUPLEX_UNKNOWN;
0486         } else {
0487             state->speed = speed;
0488             state->duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ?
0489                       DUPLEX_FULL : DUPLEX_HALF;
0490         }
0491     }
0492 
0493     ecmd->base.speed = port->state_mac.speed;
0494     ecmd->base.duplex = port->state_mac.duplex;
0495 }
0496 
0497 static void prestera_port_mdix_get(struct ethtool_link_ksettings *ecmd,
0498                    struct prestera_port *port)
0499 {
0500     struct prestera_port_phy_state *state = &port->state_phy;
0501 
0502     if (prestera_hw_port_phy_mode_get(port,
0503                       &state->mdix, NULL, NULL, NULL)) {
0504         netdev_warn(port->dev, "MDIX params get failed");
0505         state->mdix = ETH_TP_MDI_INVALID;
0506     }
0507 
0508     ecmd->base.eth_tp_mdix = port->state_phy.mdix;
0509     ecmd->base.eth_tp_mdix_ctrl = port->cfg_phy.mdix;
0510 }
0511 
0512 static int
0513 prestera_ethtool_get_link_ksettings(struct net_device *dev,
0514                     struct ethtool_link_ksettings *ecmd)
0515 {
0516     struct prestera_port *port = netdev_priv(dev);
0517 
0518     ethtool_link_ksettings_zero_link_mode(ecmd, supported);
0519     ethtool_link_ksettings_zero_link_mode(ecmd, advertising);
0520     ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising);
0521     ecmd->base.speed = SPEED_UNKNOWN;
0522     ecmd->base.duplex = DUPLEX_UNKNOWN;
0523 
0524     if (port->phy_link)
0525         return phylink_ethtool_ksettings_get(port->phy_link, ecmd);
0526 
0527     ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
0528 
0529     if (port->caps.type == PRESTERA_PORT_TYPE_TP) {
0530         ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg);
0531 
0532         if (netif_running(dev) &&
0533             (port->autoneg ||
0534              port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER))
0535             ethtool_link_ksettings_add_link_mode(ecmd, advertising,
0536                                  Autoneg);
0537     }
0538 
0539     prestera_modes_to_eth(ecmd->link_modes.supported,
0540                   port->caps.supp_link_modes,
0541                   port->caps.supp_fec,
0542                   port->caps.type);
0543 
0544     prestera_port_supp_types_get(ecmd, port);
0545 
0546     if (netif_carrier_ok(dev))
0547         prestera_port_link_mode_get(ecmd, port);
0548 
0549     ecmd->base.port = prestera_port_type_get(port);
0550 
0551     if (port->autoneg) {
0552         if (netif_running(dev))
0553             prestera_modes_to_eth(ecmd->link_modes.advertising,
0554                           port->adver_link_modes,
0555                           port->adver_fec,
0556                           port->caps.type);
0557 
0558         if (netif_carrier_ok(dev) &&
0559             port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
0560             prestera_port_remote_cap_get(ecmd, port);
0561     }
0562 
0563     if (port->caps.type == PRESTERA_PORT_TYPE_TP &&
0564         port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
0565         prestera_port_mdix_get(ecmd, port);
0566 
0567     return 0;
0568 }
0569 
0570 static int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd,
0571                   struct prestera_port *port)
0572 {
0573     if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID &&
0574         port->caps.transceiver ==  PRESTERA_PORT_TCVR_COPPER &&
0575         port->caps.type == PRESTERA_PORT_TYPE_TP) {
0576         port->cfg_phy.mdix = ecmd->base.eth_tp_mdix_ctrl;
0577         return prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,
0578                              port->autoneg,
0579                              port->cfg_phy.mode,
0580                              port->adver_link_modes,
0581                              port->cfg_phy.mdix);
0582     }
0583     return 0;
0584 
0585 }
0586 
0587 static int prestera_port_link_mode_set(struct prestera_port *port,
0588                        u32 speed, u8 duplex, u8 type)
0589 {
0590     u32 new_mode = PRESTERA_LINK_MODE_MAX;
0591     u32 mode;
0592     int err;
0593 
0594     for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
0595         if (speed != SPEED_UNKNOWN &&
0596             speed != port_link_modes[mode].speed)
0597             continue;
0598 
0599         if (duplex != DUPLEX_UNKNOWN &&
0600             duplex != port_link_modes[mode].duplex)
0601             continue;
0602 
0603         if (!(port_link_modes[mode].pr_mask &
0604             port->caps.supp_link_modes))
0605             continue;
0606 
0607         if (type != port_link_modes[mode].port_type)
0608             continue;
0609 
0610         new_mode = mode;
0611         break;
0612     }
0613 
0614     if (new_mode == PRESTERA_LINK_MODE_MAX)
0615         return -EOPNOTSUPP;
0616 
0617     err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,
0618                         false, new_mode, 0,
0619                         port->cfg_phy.mdix);
0620     if (err)
0621         return err;
0622 
0623     port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);
0624     port->adver_link_modes = 0;
0625     port->cfg_phy.mode = new_mode;
0626     port->autoneg = false;
0627 
0628     return 0;
0629 }
0630 
0631 static int
0632 prestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd,
0633                    struct prestera_port *port)
0634 {
0635     u8 duplex = DUPLEX_UNKNOWN;
0636 
0637     if (ecmd->base.duplex != DUPLEX_UNKNOWN)
0638         duplex = ecmd->base.duplex == DUPLEX_FULL ?
0639              PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF;
0640 
0641     return prestera_port_link_mode_set(port, ecmd->base.speed, duplex,
0642                        port->caps.type);
0643 }
0644 
0645 static int
0646 prestera_ethtool_set_link_ksettings(struct net_device *dev,
0647                     const struct ethtool_link_ksettings *ecmd)
0648 {
0649     struct prestera_port *port = netdev_priv(dev);
0650     u64 adver_modes;
0651     u8 adver_fec;
0652     int err;
0653 
0654     if (port->phy_link)
0655         return phylink_ethtool_ksettings_set(port->phy_link, ecmd);
0656 
0657     err = prestera_port_type_set(ecmd, port);
0658     if (err)
0659         return err;
0660 
0661     if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) {
0662         err = prestera_port_mdix_set(ecmd, port);
0663         if (err)
0664             return err;
0665     }
0666 
0667     prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes,
0668                 &adver_fec, port->caps.type);
0669 
0670     if (ecmd->base.autoneg == AUTONEG_ENABLE)
0671         err = prestera_port_autoneg_set(port, adver_modes);
0672     else
0673         err = prestera_port_speed_duplex_set(ecmd, port);
0674 
0675     return err;
0676 }
0677 
0678 static int prestera_ethtool_get_fecparam(struct net_device *dev,
0679                      struct ethtool_fecparam *fecparam)
0680 {
0681     struct prestera_port *port = netdev_priv(dev);
0682     u8 active;
0683     u32 mode;
0684     int err;
0685 
0686     err = prestera_hw_port_mac_mode_get(port, NULL, NULL, NULL, &active);
0687     if (err)
0688         return err;
0689 
0690     fecparam->fec = 0;
0691 
0692     for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
0693         if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0)
0694             continue;
0695 
0696         fecparam->fec |= port_fec_caps[mode].eth_fec;
0697     }
0698 
0699     if (active < PRESTERA_PORT_FEC_MAX)
0700         fecparam->active_fec = port_fec_caps[active].eth_fec;
0701     else
0702         fecparam->active_fec = ETHTOOL_FEC_AUTO;
0703 
0704     return 0;
0705 }
0706 
0707 static int prestera_ethtool_set_fecparam(struct net_device *dev,
0708                      struct ethtool_fecparam *fecparam)
0709 {
0710     struct prestera_port *port = netdev_priv(dev);
0711     struct prestera_port_mac_config cfg_mac;
0712     u32 mode;
0713     u8 fec;
0714 
0715     if (port->autoneg) {
0716         netdev_err(dev, "FEC set is not allowed while autoneg is on\n");
0717         return -EINVAL;
0718     }
0719 
0720     if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) {
0721         netdev_err(dev, "FEC set is not allowed on non-SFP ports\n");
0722         return -EINVAL;
0723     }
0724 
0725     fec = PRESTERA_PORT_FEC_MAX;
0726     for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
0727         if ((port_fec_caps[mode].eth_fec & fecparam->fec) &&
0728             (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) {
0729             fec = mode;
0730             break;
0731         }
0732     }
0733 
0734     prestera_port_cfg_mac_read(port, &cfg_mac);
0735 
0736     if (fec == cfg_mac.fec)
0737         return 0;
0738 
0739     if (fec == PRESTERA_PORT_FEC_MAX) {
0740         netdev_err(dev, "Unsupported FEC requested");
0741         return -EINVAL;
0742     }
0743 
0744     cfg_mac.fec = fec;
0745 
0746     return prestera_port_cfg_mac_write(port, &cfg_mac);
0747 }
0748 
0749 static int prestera_ethtool_get_sset_count(struct net_device *dev, int sset)
0750 {
0751     switch (sset) {
0752     case ETH_SS_STATS:
0753         return PRESTERA_STATS_CNT;
0754     default:
0755         return -EOPNOTSUPP;
0756     }
0757 }
0758 
0759 static void prestera_ethtool_get_strings(struct net_device *dev,
0760                      u32 stringset, u8 *data)
0761 {
0762     if (stringset != ETH_SS_STATS)
0763         return;
0764 
0765     memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name));
0766 }
0767 
0768 static void prestera_ethtool_get_stats(struct net_device *dev,
0769                        struct ethtool_stats *stats, u64 *data)
0770 {
0771     struct prestera_port *port = netdev_priv(dev);
0772     struct prestera_port_stats *port_stats;
0773 
0774     port_stats = &port->cached_hw_stats.stats;
0775 
0776     memcpy(data, port_stats, sizeof(*port_stats));
0777 }
0778 
0779 static int prestera_ethtool_nway_reset(struct net_device *dev)
0780 {
0781     struct prestera_port *port = netdev_priv(dev);
0782 
0783     if (netif_running(dev) &&
0784         port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&
0785         port->caps.type == PRESTERA_PORT_TYPE_TP)
0786         return prestera_hw_port_autoneg_restart(port);
0787 
0788     return -EINVAL;
0789 }
0790 
0791 const struct ethtool_ops prestera_ethtool_ops = {
0792     .get_drvinfo = prestera_ethtool_get_drvinfo,
0793     .get_link_ksettings = prestera_ethtool_get_link_ksettings,
0794     .set_link_ksettings = prestera_ethtool_set_link_ksettings,
0795     .get_fecparam = prestera_ethtool_get_fecparam,
0796     .set_fecparam = prestera_ethtool_set_fecparam,
0797     .get_sset_count = prestera_ethtool_get_sset_count,
0798     .get_strings = prestera_ethtool_get_strings,
0799     .get_ethtool_stats = prestera_ethtool_get_stats,
0800     .get_link = ethtool_op_get_link,
0801     .nway_reset = prestera_ethtool_nway_reset
0802 };