162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/ethtool.h> 562306a36Sopenharmony_ci#include <linux/kernel.h> 662306a36Sopenharmony_ci#include <linux/netdevice.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "prestera_ethtool.h" 962306a36Sopenharmony_ci#include "prestera.h" 1062306a36Sopenharmony_ci#include "prestera_hw.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define PRESTERA_STATS_CNT \ 1362306a36Sopenharmony_ci (sizeof(struct prestera_port_stats) / sizeof(u64)) 1462306a36Sopenharmony_ci#define PRESTERA_STATS_IDX(name) \ 1562306a36Sopenharmony_ci (offsetof(struct prestera_port_stats, name) / sizeof(u64)) 1662306a36Sopenharmony_ci#define PRESTERA_STATS_FIELD(name) \ 1762306a36Sopenharmony_ci [PRESTERA_STATS_IDX(name)] = __stringify(name) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const char driver_kind[] = "prestera"; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic const struct prestera_link_mode { 2262306a36Sopenharmony_ci enum ethtool_link_mode_bit_indices eth_mode; 2362306a36Sopenharmony_ci u32 speed; 2462306a36Sopenharmony_ci u64 pr_mask; 2562306a36Sopenharmony_ci u8 duplex; 2662306a36Sopenharmony_ci u8 port_type; 2762306a36Sopenharmony_ci} port_link_modes[PRESTERA_LINK_MODE_MAX] = { 2862306a36Sopenharmony_ci [PRESTERA_LINK_MODE_10baseT_Half] = { 2962306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_10baseT_Half_BIT, 3062306a36Sopenharmony_ci .speed = 10, 3162306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Half, 3262306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_HALF, 3362306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 3462306a36Sopenharmony_ci }, 3562306a36Sopenharmony_ci [PRESTERA_LINK_MODE_10baseT_Full] = { 3662306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_10baseT_Full_BIT, 3762306a36Sopenharmony_ci .speed = 10, 3862306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Full, 3962306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 4062306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 4162306a36Sopenharmony_ci }, 4262306a36Sopenharmony_ci [PRESTERA_LINK_MODE_100baseT_Half] = { 4362306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_100baseT_Half_BIT, 4462306a36Sopenharmony_ci .speed = 100, 4562306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Half, 4662306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_HALF, 4762306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 4862306a36Sopenharmony_ci }, 4962306a36Sopenharmony_ci [PRESTERA_LINK_MODE_100baseT_Full] = { 5062306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_100baseT_Full_BIT, 5162306a36Sopenharmony_ci .speed = 100, 5262306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Full, 5362306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 5462306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci [PRESTERA_LINK_MODE_1000baseT_Half] = { 5762306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 5862306a36Sopenharmony_ci .speed = 1000, 5962306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Half, 6062306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_HALF, 6162306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 6262306a36Sopenharmony_ci }, 6362306a36Sopenharmony_ci [PRESTERA_LINK_MODE_1000baseT_Full] = { 6462306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 6562306a36Sopenharmony_ci .speed = 1000, 6662306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Full, 6762306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 6862306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 6962306a36Sopenharmony_ci }, 7062306a36Sopenharmony_ci [PRESTERA_LINK_MODE_1000baseX_Full] = { 7162306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 7262306a36Sopenharmony_ci .speed = 1000, 7362306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseX_Full, 7462306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 7562306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_FIBRE, 7662306a36Sopenharmony_ci }, 7762306a36Sopenharmony_ci [PRESTERA_LINK_MODE_1000baseKX_Full] = { 7862306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, 7962306a36Sopenharmony_ci .speed = 1000, 8062306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseKX_Full, 8162306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 8262306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 8362306a36Sopenharmony_ci }, 8462306a36Sopenharmony_ci [PRESTERA_LINK_MODE_2500baseX_Full] = { 8562306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, 8662306a36Sopenharmony_ci .speed = 2500, 8762306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_2500baseX_Full, 8862306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 8962306a36Sopenharmony_ci }, 9062306a36Sopenharmony_ci [PRESTERA_LINK_MODE_10GbaseKR_Full] = { 9162306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, 9262306a36Sopenharmony_ci .speed = 10000, 9362306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseKR_Full, 9462306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 9562306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 9662306a36Sopenharmony_ci }, 9762306a36Sopenharmony_ci [PRESTERA_LINK_MODE_10GbaseSR_Full] = { 9862306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, 9962306a36Sopenharmony_ci .speed = 10000, 10062306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseSR_Full, 10162306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 10262306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_FIBRE, 10362306a36Sopenharmony_ci }, 10462306a36Sopenharmony_ci [PRESTERA_LINK_MODE_10GbaseLR_Full] = { 10562306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, 10662306a36Sopenharmony_ci .speed = 10000, 10762306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseLR_Full, 10862306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 10962306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_FIBRE, 11062306a36Sopenharmony_ci }, 11162306a36Sopenharmony_ci [PRESTERA_LINK_MODE_20GbaseKR2_Full] = { 11262306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT, 11362306a36Sopenharmony_ci .speed = 20000, 11462306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_20GbaseKR2_Full, 11562306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 11662306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 11762306a36Sopenharmony_ci }, 11862306a36Sopenharmony_ci [PRESTERA_LINK_MODE_25GbaseCR_Full] = { 11962306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, 12062306a36Sopenharmony_ci .speed = 25000, 12162306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseCR_Full, 12262306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 12362306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_DA, 12462306a36Sopenharmony_ci }, 12562306a36Sopenharmony_ci [PRESTERA_LINK_MODE_25GbaseKR_Full] = { 12662306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, 12762306a36Sopenharmony_ci .speed = 25000, 12862306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseKR_Full, 12962306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 13062306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 13162306a36Sopenharmony_ci }, 13262306a36Sopenharmony_ci [PRESTERA_LINK_MODE_25GbaseSR_Full] = { 13362306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, 13462306a36Sopenharmony_ci .speed = 25000, 13562306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseSR_Full, 13662306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 13762306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_FIBRE, 13862306a36Sopenharmony_ci }, 13962306a36Sopenharmony_ci [PRESTERA_LINK_MODE_40GbaseKR4_Full] = { 14062306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, 14162306a36Sopenharmony_ci .speed = 40000, 14262306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseKR4_Full, 14362306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 14462306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 14562306a36Sopenharmony_ci }, 14662306a36Sopenharmony_ci [PRESTERA_LINK_MODE_40GbaseCR4_Full] = { 14762306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, 14862306a36Sopenharmony_ci .speed = 40000, 14962306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseCR4_Full, 15062306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 15162306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_DA, 15262306a36Sopenharmony_ci }, 15362306a36Sopenharmony_ci [PRESTERA_LINK_MODE_40GbaseSR4_Full] = { 15462306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, 15562306a36Sopenharmony_ci .speed = 40000, 15662306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseSR4_Full, 15762306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 15862306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_FIBRE, 15962306a36Sopenharmony_ci }, 16062306a36Sopenharmony_ci [PRESTERA_LINK_MODE_50GbaseCR2_Full] = { 16162306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, 16262306a36Sopenharmony_ci .speed = 50000, 16362306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseCR2_Full, 16462306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 16562306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_DA, 16662306a36Sopenharmony_ci }, 16762306a36Sopenharmony_ci [PRESTERA_LINK_MODE_50GbaseKR2_Full] = { 16862306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, 16962306a36Sopenharmony_ci .speed = 50000, 17062306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseKR2_Full, 17162306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 17262306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 17362306a36Sopenharmony_ci }, 17462306a36Sopenharmony_ci [PRESTERA_LINK_MODE_50GbaseSR2_Full] = { 17562306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, 17662306a36Sopenharmony_ci .speed = 50000, 17762306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseSR2_Full, 17862306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 17962306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_FIBRE, 18062306a36Sopenharmony_ci }, 18162306a36Sopenharmony_ci [PRESTERA_LINK_MODE_100GbaseKR4_Full] = { 18262306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, 18362306a36Sopenharmony_ci .speed = 100000, 18462306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseKR4_Full, 18562306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 18662306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_TP, 18762306a36Sopenharmony_ci }, 18862306a36Sopenharmony_ci [PRESTERA_LINK_MODE_100GbaseSR4_Full] = { 18962306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, 19062306a36Sopenharmony_ci .speed = 100000, 19162306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseSR4_Full, 19262306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 19362306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_FIBRE, 19462306a36Sopenharmony_ci }, 19562306a36Sopenharmony_ci [PRESTERA_LINK_MODE_100GbaseCR4_Full] = { 19662306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, 19762306a36Sopenharmony_ci .speed = 100000, 19862306a36Sopenharmony_ci .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseCR4_Full, 19962306a36Sopenharmony_ci .duplex = PRESTERA_PORT_DUPLEX_FULL, 20062306a36Sopenharmony_ci .port_type = PRESTERA_PORT_TYPE_DA, 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic const struct prestera_fec { 20562306a36Sopenharmony_ci u32 eth_fec; 20662306a36Sopenharmony_ci enum ethtool_link_mode_bit_indices eth_mode; 20762306a36Sopenharmony_ci u8 pr_fec; 20862306a36Sopenharmony_ci} port_fec_caps[PRESTERA_PORT_FEC_MAX] = { 20962306a36Sopenharmony_ci [PRESTERA_PORT_FEC_OFF] = { 21062306a36Sopenharmony_ci .eth_fec = ETHTOOL_FEC_OFF, 21162306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_FEC_NONE_BIT, 21262306a36Sopenharmony_ci .pr_fec = 1 << PRESTERA_PORT_FEC_OFF, 21362306a36Sopenharmony_ci }, 21462306a36Sopenharmony_ci [PRESTERA_PORT_FEC_BASER] = { 21562306a36Sopenharmony_ci .eth_fec = ETHTOOL_FEC_BASER, 21662306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_FEC_BASER_BIT, 21762306a36Sopenharmony_ci .pr_fec = 1 << PRESTERA_PORT_FEC_BASER, 21862306a36Sopenharmony_ci }, 21962306a36Sopenharmony_ci [PRESTERA_PORT_FEC_RS] = { 22062306a36Sopenharmony_ci .eth_fec = ETHTOOL_FEC_RS, 22162306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_FEC_RS_BIT, 22262306a36Sopenharmony_ci .pr_fec = 1 << PRESTERA_PORT_FEC_RS, 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const struct prestera_port_type { 22762306a36Sopenharmony_ci enum ethtool_link_mode_bit_indices eth_mode; 22862306a36Sopenharmony_ci u8 eth_type; 22962306a36Sopenharmony_ci} port_types[PRESTERA_PORT_TYPE_MAX] = { 23062306a36Sopenharmony_ci [PRESTERA_PORT_TYPE_NONE] = { 23162306a36Sopenharmony_ci .eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS, 23262306a36Sopenharmony_ci .eth_type = PORT_NONE, 23362306a36Sopenharmony_ci }, 23462306a36Sopenharmony_ci [PRESTERA_PORT_TYPE_TP] = { 23562306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_TP_BIT, 23662306a36Sopenharmony_ci .eth_type = PORT_TP, 23762306a36Sopenharmony_ci }, 23862306a36Sopenharmony_ci [PRESTERA_PORT_TYPE_AUI] = { 23962306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_AUI_BIT, 24062306a36Sopenharmony_ci .eth_type = PORT_AUI, 24162306a36Sopenharmony_ci }, 24262306a36Sopenharmony_ci [PRESTERA_PORT_TYPE_MII] = { 24362306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_MII_BIT, 24462306a36Sopenharmony_ci .eth_type = PORT_MII, 24562306a36Sopenharmony_ci }, 24662306a36Sopenharmony_ci [PRESTERA_PORT_TYPE_FIBRE] = { 24762306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_FIBRE_BIT, 24862306a36Sopenharmony_ci .eth_type = PORT_FIBRE, 24962306a36Sopenharmony_ci }, 25062306a36Sopenharmony_ci [PRESTERA_PORT_TYPE_BNC] = { 25162306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_BNC_BIT, 25262306a36Sopenharmony_ci .eth_type = PORT_BNC, 25362306a36Sopenharmony_ci }, 25462306a36Sopenharmony_ci [PRESTERA_PORT_TYPE_DA] = { 25562306a36Sopenharmony_ci .eth_mode = ETHTOOL_LINK_MODE_TP_BIT, 25662306a36Sopenharmony_ci .eth_type = PORT_TP, 25762306a36Sopenharmony_ci }, 25862306a36Sopenharmony_ci [PRESTERA_PORT_TYPE_OTHER] = { 25962306a36Sopenharmony_ci .eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS, 26062306a36Sopenharmony_ci .eth_type = PORT_OTHER, 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci}; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic const char prestera_cnt_name[PRESTERA_STATS_CNT][ETH_GSTRING_LEN] = { 26562306a36Sopenharmony_ci PRESTERA_STATS_FIELD(good_octets_received), 26662306a36Sopenharmony_ci PRESTERA_STATS_FIELD(bad_octets_received), 26762306a36Sopenharmony_ci PRESTERA_STATS_FIELD(mac_trans_error), 26862306a36Sopenharmony_ci PRESTERA_STATS_FIELD(broadcast_frames_received), 26962306a36Sopenharmony_ci PRESTERA_STATS_FIELD(multicast_frames_received), 27062306a36Sopenharmony_ci PRESTERA_STATS_FIELD(frames_64_octets), 27162306a36Sopenharmony_ci PRESTERA_STATS_FIELD(frames_65_to_127_octets), 27262306a36Sopenharmony_ci PRESTERA_STATS_FIELD(frames_128_to_255_octets), 27362306a36Sopenharmony_ci PRESTERA_STATS_FIELD(frames_256_to_511_octets), 27462306a36Sopenharmony_ci PRESTERA_STATS_FIELD(frames_512_to_1023_octets), 27562306a36Sopenharmony_ci PRESTERA_STATS_FIELD(frames_1024_to_max_octets), 27662306a36Sopenharmony_ci PRESTERA_STATS_FIELD(excessive_collision), 27762306a36Sopenharmony_ci PRESTERA_STATS_FIELD(multicast_frames_sent), 27862306a36Sopenharmony_ci PRESTERA_STATS_FIELD(broadcast_frames_sent), 27962306a36Sopenharmony_ci PRESTERA_STATS_FIELD(fc_sent), 28062306a36Sopenharmony_ci PRESTERA_STATS_FIELD(fc_received), 28162306a36Sopenharmony_ci PRESTERA_STATS_FIELD(buffer_overrun), 28262306a36Sopenharmony_ci PRESTERA_STATS_FIELD(undersize), 28362306a36Sopenharmony_ci PRESTERA_STATS_FIELD(fragments), 28462306a36Sopenharmony_ci PRESTERA_STATS_FIELD(oversize), 28562306a36Sopenharmony_ci PRESTERA_STATS_FIELD(jabber), 28662306a36Sopenharmony_ci PRESTERA_STATS_FIELD(rx_error_frame_received), 28762306a36Sopenharmony_ci PRESTERA_STATS_FIELD(bad_crc), 28862306a36Sopenharmony_ci PRESTERA_STATS_FIELD(collisions), 28962306a36Sopenharmony_ci PRESTERA_STATS_FIELD(late_collision), 29062306a36Sopenharmony_ci PRESTERA_STATS_FIELD(unicast_frames_received), 29162306a36Sopenharmony_ci PRESTERA_STATS_FIELD(unicast_frames_sent), 29262306a36Sopenharmony_ci PRESTERA_STATS_FIELD(sent_multiple), 29362306a36Sopenharmony_ci PRESTERA_STATS_FIELD(sent_deferred), 29462306a36Sopenharmony_ci PRESTERA_STATS_FIELD(good_octets_sent), 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void prestera_ethtool_get_drvinfo(struct net_device *dev, 29862306a36Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 30162306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci strscpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver)); 30462306a36Sopenharmony_ci strscpy(drvinfo->bus_info, dev_name(prestera_dev(sw)), 30562306a36Sopenharmony_ci sizeof(drvinfo->bus_info)); 30662306a36Sopenharmony_ci snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), 30762306a36Sopenharmony_ci "%d.%d.%d", 30862306a36Sopenharmony_ci sw->dev->fw_rev.maj, 30962306a36Sopenharmony_ci sw->dev->fw_rev.min, 31062306a36Sopenharmony_ci sw->dev->fw_rev.sub); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic u8 prestera_port_type_get(struct prestera_port *port) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci if (port->caps.type < PRESTERA_PORT_TYPE_MAX) 31662306a36Sopenharmony_ci return port_types[port->caps.type].eth_type; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return PORT_OTHER; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int prestera_port_type_set(const struct ethtool_link_ksettings *ecmd, 32262306a36Sopenharmony_ci struct prestera_port *port) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci u32 new_mode = PRESTERA_LINK_MODE_MAX; 32562306a36Sopenharmony_ci u32 type, mode; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) { 32862306a36Sopenharmony_ci if (port_types[type].eth_type == ecmd->base.port && 32962306a36Sopenharmony_ci test_bit(port_types[type].eth_mode, 33062306a36Sopenharmony_ci ecmd->link_modes.supported)) { 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (type == port->caps.type) 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci if (type != port->caps.type && ecmd->base.autoneg == AUTONEG_ENABLE) 33862306a36Sopenharmony_ci return -EINVAL; 33962306a36Sopenharmony_ci if (type == PRESTERA_PORT_TYPE_MAX) 34062306a36Sopenharmony_ci return -EOPNOTSUPP; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 34362306a36Sopenharmony_ci if ((port_link_modes[mode].pr_mask & 34462306a36Sopenharmony_ci port->caps.supp_link_modes) && 34562306a36Sopenharmony_ci type == port_link_modes[mode].port_type) { 34662306a36Sopenharmony_ci new_mode = mode; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (new_mode >= PRESTERA_LINK_MODE_MAX) 35162306a36Sopenharmony_ci return -EINVAL; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci port->caps.type = type; 35462306a36Sopenharmony_ci port->autoneg = false; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void prestera_modes_to_eth(unsigned long *eth_modes, u64 link_modes, 36062306a36Sopenharmony_ci u8 fec, u8 type) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci u32 mode; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 36562306a36Sopenharmony_ci if ((port_link_modes[mode].pr_mask & link_modes) == 0) 36662306a36Sopenharmony_ci continue; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (type != PRESTERA_PORT_TYPE_NONE && 36962306a36Sopenharmony_ci port_link_modes[mode].port_type != type) 37062306a36Sopenharmony_ci continue; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci __set_bit(port_link_modes[mode].eth_mode, eth_modes); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { 37662306a36Sopenharmony_ci if ((port_fec_caps[mode].pr_fec & fec) == 0) 37762306a36Sopenharmony_ci continue; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci __set_bit(port_fec_caps[mode].eth_mode, eth_modes); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void prestera_modes_from_eth(const unsigned long *eth_modes, 38462306a36Sopenharmony_ci u64 *link_modes, u8 *fec, u8 type) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci u64 adver_modes = 0; 38762306a36Sopenharmony_ci u32 fec_modes = 0; 38862306a36Sopenharmony_ci u32 mode; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 39162306a36Sopenharmony_ci if (!test_bit(port_link_modes[mode].eth_mode, eth_modes)) 39262306a36Sopenharmony_ci continue; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (port_link_modes[mode].port_type != type) 39562306a36Sopenharmony_ci continue; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci adver_modes |= port_link_modes[mode].pr_mask; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { 40162306a36Sopenharmony_ci if (!test_bit(port_fec_caps[mode].eth_mode, eth_modes)) 40262306a36Sopenharmony_ci continue; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci fec_modes |= port_fec_caps[mode].pr_fec; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci *link_modes = adver_modes; 40862306a36Sopenharmony_ci *fec = fec_modes; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd, 41262306a36Sopenharmony_ci struct prestera_port *port) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci u32 mode; 41562306a36Sopenharmony_ci u8 ptype; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 41862306a36Sopenharmony_ci if ((port_link_modes[mode].pr_mask & 41962306a36Sopenharmony_ci port->caps.supp_link_modes) == 0) 42062306a36Sopenharmony_ci continue; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ptype = port_link_modes[mode].port_type; 42362306a36Sopenharmony_ci __set_bit(port_types[ptype].eth_mode, 42462306a36Sopenharmony_ci ecmd->link_modes.supported); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd, 42962306a36Sopenharmony_ci struct prestera_port *port) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct prestera_port_phy_state *state = &port->state_phy; 43262306a36Sopenharmony_ci bool asym_pause; 43362306a36Sopenharmony_ci bool pause; 43462306a36Sopenharmony_ci u64 bitmap; 43562306a36Sopenharmony_ci int err; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci err = prestera_hw_port_phy_mode_get(port, NULL, &state->lmode_bmap, 43862306a36Sopenharmony_ci &state->remote_fc.pause, 43962306a36Sopenharmony_ci &state->remote_fc.asym_pause); 44062306a36Sopenharmony_ci if (err) 44162306a36Sopenharmony_ci netdev_warn(port->dev, "Remote link caps get failed %d", 44262306a36Sopenharmony_ci port->caps.transceiver); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci bitmap = state->lmode_bmap; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci prestera_modes_to_eth(ecmd->link_modes.lp_advertising, 44762306a36Sopenharmony_ci bitmap, 0, PRESTERA_PORT_TYPE_NONE); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!bitmap_empty(ecmd->link_modes.lp_advertising, 45062306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS)) { 45162306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, 45262306a36Sopenharmony_ci lp_advertising, 45362306a36Sopenharmony_ci Autoneg); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci pause = state->remote_fc.pause; 45762306a36Sopenharmony_ci asym_pause = state->remote_fc.asym_pause; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (pause) 46062306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, 46162306a36Sopenharmony_ci lp_advertising, 46262306a36Sopenharmony_ci Pause); 46362306a36Sopenharmony_ci if (asym_pause) 46462306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, 46562306a36Sopenharmony_ci lp_advertising, 46662306a36Sopenharmony_ci Asym_Pause); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic void prestera_port_link_mode_get(struct ethtool_link_ksettings *ecmd, 47062306a36Sopenharmony_ci struct prestera_port *port) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct prestera_port_mac_state *state = &port->state_mac; 47362306a36Sopenharmony_ci u32 speed; 47462306a36Sopenharmony_ci u8 duplex; 47562306a36Sopenharmony_ci int err; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!port->state_mac.oper) 47862306a36Sopenharmony_ci return; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (state->speed == SPEED_UNKNOWN || state->duplex == DUPLEX_UNKNOWN) { 48162306a36Sopenharmony_ci err = prestera_hw_port_mac_mode_get(port, NULL, &speed, 48262306a36Sopenharmony_ci &duplex, NULL); 48362306a36Sopenharmony_ci if (err) { 48462306a36Sopenharmony_ci state->speed = SPEED_UNKNOWN; 48562306a36Sopenharmony_ci state->duplex = DUPLEX_UNKNOWN; 48662306a36Sopenharmony_ci } else { 48762306a36Sopenharmony_ci state->speed = speed; 48862306a36Sopenharmony_ci state->duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ? 48962306a36Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci ecmd->base.speed = port->state_mac.speed; 49462306a36Sopenharmony_ci ecmd->base.duplex = port->state_mac.duplex; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void prestera_port_mdix_get(struct ethtool_link_ksettings *ecmd, 49862306a36Sopenharmony_ci struct prestera_port *port) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct prestera_port_phy_state *state = &port->state_phy; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (prestera_hw_port_phy_mode_get(port, 50362306a36Sopenharmony_ci &state->mdix, NULL, NULL, NULL)) { 50462306a36Sopenharmony_ci netdev_warn(port->dev, "MDIX params get failed"); 50562306a36Sopenharmony_ci state->mdix = ETH_TP_MDI_INVALID; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ecmd->base.eth_tp_mdix = port->state_phy.mdix; 50962306a36Sopenharmony_ci ecmd->base.eth_tp_mdix_ctrl = port->cfg_phy.mdix; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic int 51362306a36Sopenharmony_ciprestera_ethtool_get_link_ksettings(struct net_device *dev, 51462306a36Sopenharmony_ci struct ethtool_link_ksettings *ecmd) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, supported); 51962306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, advertising); 52062306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising); 52162306a36Sopenharmony_ci ecmd->base.speed = SPEED_UNKNOWN; 52262306a36Sopenharmony_ci ecmd->base.duplex = DUPLEX_UNKNOWN; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (port->phy_link) 52562306a36Sopenharmony_ci return phylink_ethtool_ksettings_get(port->phy_link, ecmd); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (port->caps.type == PRESTERA_PORT_TYPE_TP) { 53062306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (netif_running(dev) && 53362306a36Sopenharmony_ci (port->autoneg || 53462306a36Sopenharmony_ci port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)) 53562306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, advertising, 53662306a36Sopenharmony_ci Autoneg); 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci prestera_modes_to_eth(ecmd->link_modes.supported, 54062306a36Sopenharmony_ci port->caps.supp_link_modes, 54162306a36Sopenharmony_ci port->caps.supp_fec, 54262306a36Sopenharmony_ci port->caps.type); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci prestera_port_supp_types_get(ecmd, port); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (netif_carrier_ok(dev)) 54762306a36Sopenharmony_ci prestera_port_link_mode_get(ecmd, port); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci ecmd->base.port = prestera_port_type_get(port); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (port->autoneg) { 55262306a36Sopenharmony_ci if (netif_running(dev)) 55362306a36Sopenharmony_ci prestera_modes_to_eth(ecmd->link_modes.advertising, 55462306a36Sopenharmony_ci port->adver_link_modes, 55562306a36Sopenharmony_ci port->adver_fec, 55662306a36Sopenharmony_ci port->caps.type); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (netif_carrier_ok(dev) && 55962306a36Sopenharmony_ci port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) 56062306a36Sopenharmony_ci prestera_port_remote_cap_get(ecmd, port); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (port->caps.type == PRESTERA_PORT_TYPE_TP && 56462306a36Sopenharmony_ci port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) 56562306a36Sopenharmony_ci prestera_port_mdix_get(ecmd, port); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd, 57162306a36Sopenharmony_ci struct prestera_port *port) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID && 57462306a36Sopenharmony_ci port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER && 57562306a36Sopenharmony_ci port->caps.type == PRESTERA_PORT_TYPE_TP) { 57662306a36Sopenharmony_ci port->cfg_phy.mdix = ecmd->base.eth_tp_mdix_ctrl; 57762306a36Sopenharmony_ci return prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin, 57862306a36Sopenharmony_ci port->autoneg, 57962306a36Sopenharmony_ci port->cfg_phy.mode, 58062306a36Sopenharmony_ci port->adver_link_modes, 58162306a36Sopenharmony_ci port->cfg_phy.mdix); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int prestera_port_link_mode_set(struct prestera_port *port, 58862306a36Sopenharmony_ci u32 speed, u8 duplex, u8 type) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci u32 new_mode = PRESTERA_LINK_MODE_MAX; 59162306a36Sopenharmony_ci u32 mode; 59262306a36Sopenharmony_ci int err; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 59562306a36Sopenharmony_ci if (speed != SPEED_UNKNOWN && 59662306a36Sopenharmony_ci speed != port_link_modes[mode].speed) 59762306a36Sopenharmony_ci continue; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (duplex != DUPLEX_UNKNOWN && 60062306a36Sopenharmony_ci duplex != port_link_modes[mode].duplex) 60162306a36Sopenharmony_ci continue; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (!(port_link_modes[mode].pr_mask & 60462306a36Sopenharmony_ci port->caps.supp_link_modes)) 60562306a36Sopenharmony_ci continue; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (type != port_link_modes[mode].port_type) 60862306a36Sopenharmony_ci continue; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci new_mode = mode; 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (new_mode == PRESTERA_LINK_MODE_MAX) 61562306a36Sopenharmony_ci return -EOPNOTSUPP; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin, 61862306a36Sopenharmony_ci false, new_mode, 0, 61962306a36Sopenharmony_ci port->cfg_phy.mdix); 62062306a36Sopenharmony_ci if (err) 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF); 62462306a36Sopenharmony_ci port->adver_link_modes = 0; 62562306a36Sopenharmony_ci port->cfg_phy.mode = new_mode; 62662306a36Sopenharmony_ci port->autoneg = false; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic int 63262306a36Sopenharmony_ciprestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd, 63362306a36Sopenharmony_ci struct prestera_port *port) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci u8 duplex = DUPLEX_UNKNOWN; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (ecmd->base.duplex != DUPLEX_UNKNOWN) 63862306a36Sopenharmony_ci duplex = ecmd->base.duplex == DUPLEX_FULL ? 63962306a36Sopenharmony_ci PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return prestera_port_link_mode_set(port, ecmd->base.speed, duplex, 64262306a36Sopenharmony_ci port->caps.type); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int 64662306a36Sopenharmony_ciprestera_ethtool_set_link_ksettings(struct net_device *dev, 64762306a36Sopenharmony_ci const struct ethtool_link_ksettings *ecmd) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 65062306a36Sopenharmony_ci u64 adver_modes; 65162306a36Sopenharmony_ci u8 adver_fec; 65262306a36Sopenharmony_ci int err; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (port->phy_link) 65562306a36Sopenharmony_ci return phylink_ethtool_ksettings_set(port->phy_link, ecmd); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci err = prestera_port_type_set(ecmd, port); 65862306a36Sopenharmony_ci if (err) 65962306a36Sopenharmony_ci return err; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) { 66262306a36Sopenharmony_ci err = prestera_port_mdix_set(ecmd, port); 66362306a36Sopenharmony_ci if (err) 66462306a36Sopenharmony_ci return err; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes, 66862306a36Sopenharmony_ci &adver_fec, port->caps.type); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (ecmd->base.autoneg == AUTONEG_ENABLE) 67162306a36Sopenharmony_ci err = prestera_port_autoneg_set(port, adver_modes); 67262306a36Sopenharmony_ci else 67362306a36Sopenharmony_ci err = prestera_port_speed_duplex_set(ecmd, port); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci return err; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic int prestera_ethtool_get_fecparam(struct net_device *dev, 67962306a36Sopenharmony_ci struct ethtool_fecparam *fecparam) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 68262306a36Sopenharmony_ci u8 active; 68362306a36Sopenharmony_ci u32 mode; 68462306a36Sopenharmony_ci int err; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci err = prestera_hw_port_mac_mode_get(port, NULL, NULL, NULL, &active); 68762306a36Sopenharmony_ci if (err) 68862306a36Sopenharmony_ci return err; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci fecparam->fec = 0; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { 69362306a36Sopenharmony_ci if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0) 69462306a36Sopenharmony_ci continue; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci fecparam->fec |= port_fec_caps[mode].eth_fec; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (active < PRESTERA_PORT_FEC_MAX) 70062306a36Sopenharmony_ci fecparam->active_fec = port_fec_caps[active].eth_fec; 70162306a36Sopenharmony_ci else 70262306a36Sopenharmony_ci fecparam->active_fec = ETHTOOL_FEC_AUTO; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int prestera_ethtool_set_fecparam(struct net_device *dev, 70862306a36Sopenharmony_ci struct ethtool_fecparam *fecparam) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 71162306a36Sopenharmony_ci struct prestera_port_mac_config cfg_mac; 71262306a36Sopenharmony_ci u32 mode; 71362306a36Sopenharmony_ci u8 fec; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (port->autoneg) { 71662306a36Sopenharmony_ci netdev_err(dev, "FEC set is not allowed while autoneg is on\n"); 71762306a36Sopenharmony_ci return -EINVAL; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { 72162306a36Sopenharmony_ci netdev_err(dev, "FEC set is not allowed on non-SFP ports\n"); 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci fec = PRESTERA_PORT_FEC_MAX; 72662306a36Sopenharmony_ci for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { 72762306a36Sopenharmony_ci if ((port_fec_caps[mode].eth_fec & fecparam->fec) && 72862306a36Sopenharmony_ci (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) { 72962306a36Sopenharmony_ci fec = mode; 73062306a36Sopenharmony_ci break; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci prestera_port_cfg_mac_read(port, &cfg_mac); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (fec == cfg_mac.fec) 73762306a36Sopenharmony_ci return 0; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (fec == PRESTERA_PORT_FEC_MAX) { 74062306a36Sopenharmony_ci netdev_err(dev, "Unsupported FEC requested"); 74162306a36Sopenharmony_ci return -EINVAL; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci cfg_mac.fec = fec; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return prestera_port_cfg_mac_write(port, &cfg_mac); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic int prestera_ethtool_get_sset_count(struct net_device *dev, int sset) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci switch (sset) { 75262306a36Sopenharmony_ci case ETH_SS_STATS: 75362306a36Sopenharmony_ci return PRESTERA_STATS_CNT; 75462306a36Sopenharmony_ci default: 75562306a36Sopenharmony_ci return -EOPNOTSUPP; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic void prestera_ethtool_get_strings(struct net_device *dev, 76062306a36Sopenharmony_ci u32 stringset, u8 *data) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci if (stringset != ETH_SS_STATS) 76362306a36Sopenharmony_ci return; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name)); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic void prestera_ethtool_get_stats(struct net_device *dev, 76962306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 77262306a36Sopenharmony_ci struct prestera_port_stats *port_stats; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci port_stats = &port->cached_hw_stats.stats; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci memcpy(data, port_stats, sizeof(*port_stats)); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int prestera_ethtool_nway_reset(struct net_device *dev) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct prestera_port *port = netdev_priv(dev); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (netif_running(dev) && 78462306a36Sopenharmony_ci port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER && 78562306a36Sopenharmony_ci port->caps.type == PRESTERA_PORT_TYPE_TP) 78662306a36Sopenharmony_ci return prestera_hw_port_autoneg_restart(port); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return -EINVAL; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ciconst struct ethtool_ops prestera_ethtool_ops = { 79262306a36Sopenharmony_ci .get_drvinfo = prestera_ethtool_get_drvinfo, 79362306a36Sopenharmony_ci .get_link_ksettings = prestera_ethtool_get_link_ksettings, 79462306a36Sopenharmony_ci .set_link_ksettings = prestera_ethtool_set_link_ksettings, 79562306a36Sopenharmony_ci .get_fecparam = prestera_ethtool_get_fecparam, 79662306a36Sopenharmony_ci .set_fecparam = prestera_ethtool_set_fecparam, 79762306a36Sopenharmony_ci .get_sset_count = prestera_ethtool_get_sset_count, 79862306a36Sopenharmony_ci .get_strings = prestera_ethtool_get_strings, 79962306a36Sopenharmony_ci .get_ethtool_stats = prestera_ethtool_get_stats, 80062306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 80162306a36Sopenharmony_ci .nway_reset = prestera_ethtool_nway_reset 80262306a36Sopenharmony_ci}; 803