linux/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c
<<
>>
Prefs
   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        int err;
 327
 328        for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) {
 329                if (port_types[type].eth_type == ecmd->base.port &&
 330                    test_bit(port_types[type].eth_mode,
 331                             ecmd->link_modes.supported)) {
 332                        break;
 333                }
 334        }
 335
 336        if (type == port->caps.type)
 337                return 0;
 338        if (type != port->caps.type && ecmd->base.autoneg == AUTONEG_ENABLE)
 339                return -EINVAL;
 340        if (type == PRESTERA_PORT_TYPE_MAX)
 341                return -EOPNOTSUPP;
 342
 343        for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
 344                if ((port_link_modes[mode].pr_mask &
 345                    port->caps.supp_link_modes) &&
 346                    type == port_link_modes[mode].port_type) {
 347                        new_mode = mode;
 348                }
 349        }
 350
 351        if (new_mode < PRESTERA_LINK_MODE_MAX)
 352                err = prestera_hw_port_link_mode_set(port, new_mode);
 353        else
 354                err = -EINVAL;
 355
 356        if (err)
 357                return err;
 358
 359        port->caps.type = type;
 360        port->autoneg = false;
 361
 362        return 0;
 363}
 364
 365static void prestera_modes_to_eth(unsigned long *eth_modes, u64 link_modes,
 366                                  u8 fec, u8 type)
 367{
 368        u32 mode;
 369
 370        for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
 371                if ((port_link_modes[mode].pr_mask & link_modes) == 0)
 372                        continue;
 373
 374                if (type != PRESTERA_PORT_TYPE_NONE &&
 375                    port_link_modes[mode].port_type != type)
 376                        continue;
 377
 378                __set_bit(port_link_modes[mode].eth_mode, eth_modes);
 379        }
 380
 381        for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
 382                if ((port_fec_caps[mode].pr_fec & fec) == 0)
 383                        continue;
 384
 385                __set_bit(port_fec_caps[mode].eth_mode, eth_modes);
 386        }
 387}
 388
 389static void prestera_modes_from_eth(const unsigned long *eth_modes,
 390                                    u64 *link_modes, u8 *fec, u8 type)
 391{
 392        u64 adver_modes = 0;
 393        u32 fec_modes = 0;
 394        u32 mode;
 395
 396        for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
 397                if (!test_bit(port_link_modes[mode].eth_mode, eth_modes))
 398                        continue;
 399
 400                if (port_link_modes[mode].port_type != type)
 401                        continue;
 402
 403                adver_modes |= port_link_modes[mode].pr_mask;
 404        }
 405
 406        for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
 407                if (!test_bit(port_fec_caps[mode].eth_mode, eth_modes))
 408                        continue;
 409
 410                fec_modes |= port_fec_caps[mode].pr_fec;
 411        }
 412
 413        *link_modes = adver_modes;
 414        *fec = fec_modes;
 415}
 416
 417static void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd,
 418                                         struct prestera_port *port)
 419{
 420        u32 mode;
 421        u8 ptype;
 422
 423        for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
 424                if ((port_link_modes[mode].pr_mask &
 425                    port->caps.supp_link_modes) == 0)
 426                        continue;
 427
 428                ptype = port_link_modes[mode].port_type;
 429                __set_bit(port_types[ptype].eth_mode,
 430                          ecmd->link_modes.supported);
 431        }
 432}
 433
 434static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd,
 435                                         struct prestera_port *port)
 436{
 437        bool asym_pause;
 438        bool pause;
 439        u64 bitmap;
 440        int err;
 441
 442        err = prestera_hw_port_remote_cap_get(port, &bitmap);
 443        if (!err) {
 444                prestera_modes_to_eth(ecmd->link_modes.lp_advertising,
 445                                      bitmap, 0, PRESTERA_PORT_TYPE_NONE);
 446
 447                if (!bitmap_empty(ecmd->link_modes.lp_advertising,
 448                                  __ETHTOOL_LINK_MODE_MASK_NBITS)) {
 449                        ethtool_link_ksettings_add_link_mode(ecmd,
 450                                                             lp_advertising,
 451                                                             Autoneg);
 452                }
 453        }
 454
 455        err = prestera_hw_port_remote_fc_get(port, &pause, &asym_pause);
 456        if (err)
 457                return;
 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_speed_get(struct ethtool_link_ksettings *ecmd,
 470                                    struct prestera_port *port)
 471{
 472        u32 speed;
 473        int err;
 474
 475        err = prestera_hw_port_speed_get(port, &speed);
 476        ecmd->base.speed = err ? SPEED_UNKNOWN : speed;
 477}
 478
 479static void prestera_port_duplex_get(struct ethtool_link_ksettings *ecmd,
 480                                     struct prestera_port *port)
 481{
 482        u8 duplex;
 483        int err;
 484
 485        err = prestera_hw_port_duplex_get(port, &duplex);
 486        if (err) {
 487                ecmd->base.duplex = DUPLEX_UNKNOWN;
 488                return;
 489        }
 490
 491        ecmd->base.duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ?
 492                            DUPLEX_FULL : DUPLEX_HALF;
 493}
 494
 495static int
 496prestera_ethtool_get_link_ksettings(struct net_device *dev,
 497                                    struct ethtool_link_ksettings *ecmd)
 498{
 499        struct prestera_port *port = netdev_priv(dev);
 500
 501        ethtool_link_ksettings_zero_link_mode(ecmd, supported);
 502        ethtool_link_ksettings_zero_link_mode(ecmd, advertising);
 503        ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising);
 504
 505        ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
 506
 507        if (port->caps.type == PRESTERA_PORT_TYPE_TP) {
 508                ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg);
 509
 510                if (netif_running(dev) &&
 511                    (port->autoneg ||
 512                     port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER))
 513                        ethtool_link_ksettings_add_link_mode(ecmd, advertising,
 514                                                             Autoneg);
 515        }
 516
 517        prestera_modes_to_eth(ecmd->link_modes.supported,
 518                              port->caps.supp_link_modes,
 519                              port->caps.supp_fec,
 520                              port->caps.type);
 521
 522        prestera_port_supp_types_get(ecmd, port);
 523
 524        if (netif_carrier_ok(dev)) {
 525                prestera_port_speed_get(ecmd, port);
 526                prestera_port_duplex_get(ecmd, port);
 527        } else {
 528                ecmd->base.speed = SPEED_UNKNOWN;
 529                ecmd->base.duplex = DUPLEX_UNKNOWN;
 530        }
 531
 532        ecmd->base.port = prestera_port_type_get(port);
 533
 534        if (port->autoneg) {
 535                if (netif_running(dev))
 536                        prestera_modes_to_eth(ecmd->link_modes.advertising,
 537                                              port->adver_link_modes,
 538                                              port->adver_fec,
 539                                              port->caps.type);
 540
 541                if (netif_carrier_ok(dev) &&
 542                    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
 543                        prestera_port_remote_cap_get(ecmd, port);
 544        }
 545
 546        if (port->caps.type == PRESTERA_PORT_TYPE_TP &&
 547            port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
 548                prestera_hw_port_mdix_get(port, &ecmd->base.eth_tp_mdix,
 549                                          &ecmd->base.eth_tp_mdix_ctrl);
 550
 551        return 0;
 552}
 553
 554static int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd,
 555                                  struct prestera_port *port)
 556{
 557        if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID &&
 558            port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&
 559            port->caps.type == PRESTERA_PORT_TYPE_TP)
 560                return prestera_hw_port_mdix_set(port,
 561                                                 ecmd->base.eth_tp_mdix_ctrl);
 562
 563        return 0;
 564}
 565
 566static int prestera_port_link_mode_set(struct prestera_port *port,
 567                                       u32 speed, u8 duplex, u8 type)
 568{
 569        u32 new_mode = PRESTERA_LINK_MODE_MAX;
 570        u32 mode;
 571
 572        for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
 573                if (speed != port_link_modes[mode].speed)
 574                        continue;
 575
 576                if (duplex != port_link_modes[mode].duplex)
 577                        continue;
 578
 579                if (!(port_link_modes[mode].pr_mask &
 580                    port->caps.supp_link_modes))
 581                        continue;
 582
 583                if (type != port_link_modes[mode].port_type)
 584                        continue;
 585
 586                new_mode = mode;
 587                break;
 588        }
 589
 590        if (new_mode == PRESTERA_LINK_MODE_MAX)
 591                return -EOPNOTSUPP;
 592
 593        return prestera_hw_port_link_mode_set(port, new_mode);
 594}
 595
 596static int
 597prestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd,
 598                               struct prestera_port *port)
 599{
 600        u32 curr_mode;
 601        u8 duplex;
 602        u32 speed;
 603        int err;
 604
 605        err = prestera_hw_port_link_mode_get(port, &curr_mode);
 606        if (err)
 607                return err;
 608        if (curr_mode >= PRESTERA_LINK_MODE_MAX)
 609                return -EINVAL;
 610
 611        if (ecmd->base.duplex != DUPLEX_UNKNOWN)
 612                duplex = ecmd->base.duplex == DUPLEX_FULL ?
 613                         PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF;
 614        else
 615                duplex = port_link_modes[curr_mode].duplex;
 616
 617        if (ecmd->base.speed != SPEED_UNKNOWN)
 618                speed = ecmd->base.speed;
 619        else
 620                speed = port_link_modes[curr_mode].speed;
 621
 622        return prestera_port_link_mode_set(port, speed, duplex,
 623                                           port->caps.type);
 624}
 625
 626static int
 627prestera_ethtool_set_link_ksettings(struct net_device *dev,
 628                                    const struct ethtool_link_ksettings *ecmd)
 629{
 630        struct prestera_port *port = netdev_priv(dev);
 631        u64 adver_modes;
 632        u8 adver_fec;
 633        int err;
 634
 635        err = prestera_port_type_set(ecmd, port);
 636        if (err)
 637                return err;
 638
 639        if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) {
 640                err = prestera_port_mdix_set(ecmd, port);
 641                if (err)
 642                        return err;
 643        }
 644
 645        prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes,
 646                                &adver_fec, port->caps.type);
 647
 648        err = prestera_port_autoneg_set(port,
 649                                        ecmd->base.autoneg == AUTONEG_ENABLE,
 650                                        adver_modes, adver_fec);
 651        if (err)
 652                return err;
 653
 654        if (ecmd->base.autoneg == AUTONEG_DISABLE) {
 655                err = prestera_port_speed_duplex_set(ecmd, port);
 656                if (err)
 657                        return err;
 658        }
 659
 660        return 0;
 661}
 662
 663static int prestera_ethtool_get_fecparam(struct net_device *dev,
 664                                         struct ethtool_fecparam *fecparam)
 665{
 666        struct prestera_port *port = netdev_priv(dev);
 667        u8 active;
 668        u32 mode;
 669        int err;
 670
 671        err = prestera_hw_port_fec_get(port, &active);
 672        if (err)
 673                return err;
 674
 675        fecparam->fec = 0;
 676
 677        for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
 678                if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0)
 679                        continue;
 680
 681                fecparam->fec |= port_fec_caps[mode].eth_fec;
 682        }
 683
 684        if (active < PRESTERA_PORT_FEC_MAX)
 685                fecparam->active_fec = port_fec_caps[active].eth_fec;
 686        else
 687                fecparam->active_fec = ETHTOOL_FEC_AUTO;
 688
 689        return 0;
 690}
 691
 692static int prestera_ethtool_set_fecparam(struct net_device *dev,
 693                                         struct ethtool_fecparam *fecparam)
 694{
 695        struct prestera_port *port = netdev_priv(dev);
 696        u8 fec, active;
 697        u32 mode;
 698        int err;
 699
 700        if (port->autoneg) {
 701                netdev_err(dev, "FEC set is not allowed while autoneg is on\n");
 702                return -EINVAL;
 703        }
 704
 705        err = prestera_hw_port_fec_get(port, &active);
 706        if (err)
 707                return err;
 708
 709        fec = PRESTERA_PORT_FEC_MAX;
 710        for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
 711                if ((port_fec_caps[mode].eth_fec & fecparam->fec) &&
 712                    (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) {
 713                        fec = mode;
 714                        break;
 715                }
 716        }
 717
 718        if (fec == active)
 719                return 0;
 720
 721        if (fec == PRESTERA_PORT_FEC_MAX)
 722                return -EOPNOTSUPP;
 723
 724        return prestera_hw_port_fec_set(port, fec);
 725}
 726
 727static int prestera_ethtool_get_sset_count(struct net_device *dev, int sset)
 728{
 729        switch (sset) {
 730        case ETH_SS_STATS:
 731                return PRESTERA_STATS_CNT;
 732        default:
 733                return -EOPNOTSUPP;
 734        }
 735}
 736
 737static void prestera_ethtool_get_strings(struct net_device *dev,
 738                                         u32 stringset, u8 *data)
 739{
 740        if (stringset != ETH_SS_STATS)
 741                return;
 742
 743        memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name));
 744}
 745
 746static void prestera_ethtool_get_stats(struct net_device *dev,
 747                                       struct ethtool_stats *stats, u64 *data)
 748{
 749        struct prestera_port *port = netdev_priv(dev);
 750        struct prestera_port_stats *port_stats;
 751
 752        port_stats = &port->cached_hw_stats.stats;
 753
 754        memcpy(data, port_stats, sizeof(*port_stats));
 755}
 756
 757static int prestera_ethtool_nway_reset(struct net_device *dev)
 758{
 759        struct prestera_port *port = netdev_priv(dev);
 760
 761        if (netif_running(dev) &&
 762            port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&
 763            port->caps.type == PRESTERA_PORT_TYPE_TP)
 764                return prestera_hw_port_autoneg_restart(port);
 765
 766        return -EINVAL;
 767}
 768
 769const struct ethtool_ops prestera_ethtool_ops = {
 770        .get_drvinfo = prestera_ethtool_get_drvinfo,
 771        .get_link_ksettings = prestera_ethtool_get_link_ksettings,
 772        .set_link_ksettings = prestera_ethtool_set_link_ksettings,
 773        .get_fecparam = prestera_ethtool_get_fecparam,
 774        .set_fecparam = prestera_ethtool_set_fecparam,
 775        .get_sset_count = prestera_ethtool_get_sset_count,
 776        .get_strings = prestera_ethtool_get_strings,
 777        .get_ethtool_stats = prestera_ethtool_get_stats,
 778        .get_link = ethtool_op_get_link,
 779        .nway_reset = prestera_ethtool_nway_reset
 780};
 781