1a97d3c69SVadym Kochan // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2a97d3c69SVadym Kochan /* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ 3a97d3c69SVadym Kochan 4a97d3c69SVadym Kochan #include <linux/ethtool.h> 5a97d3c69SVadym Kochan #include <linux/kernel.h> 6a97d3c69SVadym Kochan #include <linux/netdevice.h> 7a97d3c69SVadym Kochan 8a97d3c69SVadym Kochan #include "prestera_ethtool.h" 9a97d3c69SVadym Kochan #include "prestera.h" 10a97d3c69SVadym Kochan #include "prestera_hw.h" 11a97d3c69SVadym Kochan 12a97d3c69SVadym Kochan #define PRESTERA_STATS_CNT \ 13a97d3c69SVadym Kochan (sizeof(struct prestera_port_stats) / sizeof(u64)) 14a97d3c69SVadym Kochan #define PRESTERA_STATS_IDX(name) \ 15a97d3c69SVadym Kochan (offsetof(struct prestera_port_stats, name) / sizeof(u64)) 16a97d3c69SVadym Kochan #define PRESTERA_STATS_FIELD(name) \ 17a97d3c69SVadym Kochan [PRESTERA_STATS_IDX(name)] = __stringify(name) 18a97d3c69SVadym Kochan 19a97d3c69SVadym Kochan static const char driver_kind[] = "prestera"; 20a97d3c69SVadym Kochan 21a97d3c69SVadym Kochan static const struct prestera_link_mode { 22a97d3c69SVadym Kochan enum ethtool_link_mode_bit_indices eth_mode; 23a97d3c69SVadym Kochan u32 speed; 24a97d3c69SVadym Kochan u64 pr_mask; 25a97d3c69SVadym Kochan u8 duplex; 26a97d3c69SVadym Kochan u8 port_type; 27a97d3c69SVadym Kochan } port_link_modes[PRESTERA_LINK_MODE_MAX] = { 28a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_10baseT_Half] = { 29a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_10baseT_Half_BIT, 30a97d3c69SVadym Kochan .speed = 10, 31a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Half, 32a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_HALF, 33a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 34a97d3c69SVadym Kochan }, 35a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_10baseT_Full] = { 36a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_10baseT_Full_BIT, 37a97d3c69SVadym Kochan .speed = 10, 38a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Full, 39a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 40a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 41a97d3c69SVadym Kochan }, 42a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_100baseT_Half] = { 43a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_100baseT_Half_BIT, 44a97d3c69SVadym Kochan .speed = 100, 45a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Half, 46a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_HALF, 47a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 48a97d3c69SVadym Kochan }, 49a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_100baseT_Full] = { 50a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_100baseT_Full_BIT, 51a97d3c69SVadym Kochan .speed = 100, 52a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Full, 53a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 54a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 55a97d3c69SVadym Kochan }, 56a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_1000baseT_Half] = { 57a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 58a97d3c69SVadym Kochan .speed = 1000, 59a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Half, 60a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_HALF, 61a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 62a97d3c69SVadym Kochan }, 63a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_1000baseT_Full] = { 64a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 65a97d3c69SVadym Kochan .speed = 1000, 66a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Full, 67a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 68a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 69a97d3c69SVadym Kochan }, 70a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_1000baseX_Full] = { 71a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 72a97d3c69SVadym Kochan .speed = 1000, 73a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseX_Full, 74a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 75a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_FIBRE, 76a97d3c69SVadym Kochan }, 77a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_1000baseKX_Full] = { 78a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, 79a97d3c69SVadym Kochan .speed = 1000, 80a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseKX_Full, 81a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 82a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 83a97d3c69SVadym Kochan }, 84a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_2500baseX_Full] = { 85a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, 86a97d3c69SVadym Kochan .speed = 2500, 87a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_2500baseX_Full, 88a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 89a97d3c69SVadym Kochan }, 90a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_10GbaseKR_Full] = { 91a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, 92a97d3c69SVadym Kochan .speed = 10000, 93a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseKR_Full, 94a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 95a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 96a97d3c69SVadym Kochan }, 97a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_10GbaseSR_Full] = { 98a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, 99a97d3c69SVadym Kochan .speed = 10000, 100a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseSR_Full, 101a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 102a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_FIBRE, 103a97d3c69SVadym Kochan }, 104a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_10GbaseLR_Full] = { 105a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, 106a97d3c69SVadym Kochan .speed = 10000, 107a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseLR_Full, 108a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 109a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_FIBRE, 110a97d3c69SVadym Kochan }, 111a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_20GbaseKR2_Full] = { 112a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT, 113a97d3c69SVadym Kochan .speed = 20000, 114a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_20GbaseKR2_Full, 115a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 116a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 117a97d3c69SVadym Kochan }, 118a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_25GbaseCR_Full] = { 119a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, 120a97d3c69SVadym Kochan .speed = 25000, 121a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseCR_Full, 122a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 123a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_DA, 124a97d3c69SVadym Kochan }, 125a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_25GbaseKR_Full] = { 126a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, 127a97d3c69SVadym Kochan .speed = 25000, 128a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseKR_Full, 129a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 130a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 131a97d3c69SVadym Kochan }, 132a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_25GbaseSR_Full] = { 133a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, 134a97d3c69SVadym Kochan .speed = 25000, 135a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseSR_Full, 136a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 137a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_FIBRE, 138a97d3c69SVadym Kochan }, 139a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_40GbaseKR4_Full] = { 140a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, 141a97d3c69SVadym Kochan .speed = 40000, 142a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseKR4_Full, 143a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 144a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 145a97d3c69SVadym Kochan }, 146a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_40GbaseCR4_Full] = { 147a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, 148a97d3c69SVadym Kochan .speed = 40000, 149a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseCR4_Full, 150a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 151a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_DA, 152a97d3c69SVadym Kochan }, 153a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_40GbaseSR4_Full] = { 154a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, 155a97d3c69SVadym Kochan .speed = 40000, 156a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseSR4_Full, 157a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 158a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_FIBRE, 159a97d3c69SVadym Kochan }, 160a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_50GbaseCR2_Full] = { 161a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, 162a97d3c69SVadym Kochan .speed = 50000, 163a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseCR2_Full, 164a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 165a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_DA, 166a97d3c69SVadym Kochan }, 167a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_50GbaseKR2_Full] = { 168a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, 169a97d3c69SVadym Kochan .speed = 50000, 170a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseKR2_Full, 171a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 172a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 173a97d3c69SVadym Kochan }, 174a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_50GbaseSR2_Full] = { 175a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, 176a97d3c69SVadym Kochan .speed = 50000, 177a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseSR2_Full, 178a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 179a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_FIBRE, 180a97d3c69SVadym Kochan }, 181a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_100GbaseKR4_Full] = { 182a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, 183a97d3c69SVadym Kochan .speed = 100000, 184a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseKR4_Full, 185a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 186a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_TP, 187a97d3c69SVadym Kochan }, 188a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_100GbaseSR4_Full] = { 189a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, 190a97d3c69SVadym Kochan .speed = 100000, 191a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseSR4_Full, 192a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 193a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_FIBRE, 194a97d3c69SVadym Kochan }, 195a97d3c69SVadym Kochan [PRESTERA_LINK_MODE_100GbaseCR4_Full] = { 196a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, 197a97d3c69SVadym Kochan .speed = 100000, 198a97d3c69SVadym Kochan .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseCR4_Full, 199a97d3c69SVadym Kochan .duplex = PRESTERA_PORT_DUPLEX_FULL, 200a97d3c69SVadym Kochan .port_type = PRESTERA_PORT_TYPE_DA, 201a97d3c69SVadym Kochan } 202a97d3c69SVadym Kochan }; 203a97d3c69SVadym Kochan 204a97d3c69SVadym Kochan static const struct prestera_fec { 205a97d3c69SVadym Kochan u32 eth_fec; 206a97d3c69SVadym Kochan enum ethtool_link_mode_bit_indices eth_mode; 207a97d3c69SVadym Kochan u8 pr_fec; 208a97d3c69SVadym Kochan } port_fec_caps[PRESTERA_PORT_FEC_MAX] = { 209a97d3c69SVadym Kochan [PRESTERA_PORT_FEC_OFF] = { 210a97d3c69SVadym Kochan .eth_fec = ETHTOOL_FEC_OFF, 211a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_FEC_NONE_BIT, 212a97d3c69SVadym Kochan .pr_fec = 1 << PRESTERA_PORT_FEC_OFF, 213a97d3c69SVadym Kochan }, 214a97d3c69SVadym Kochan [PRESTERA_PORT_FEC_BASER] = { 215a97d3c69SVadym Kochan .eth_fec = ETHTOOL_FEC_BASER, 216a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_FEC_BASER_BIT, 217a97d3c69SVadym Kochan .pr_fec = 1 << PRESTERA_PORT_FEC_BASER, 218a97d3c69SVadym Kochan }, 219a97d3c69SVadym Kochan [PRESTERA_PORT_FEC_RS] = { 220a97d3c69SVadym Kochan .eth_fec = ETHTOOL_FEC_RS, 221a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_FEC_RS_BIT, 222a97d3c69SVadym Kochan .pr_fec = 1 << PRESTERA_PORT_FEC_RS, 223a97d3c69SVadym Kochan } 224a97d3c69SVadym Kochan }; 225a97d3c69SVadym Kochan 226a97d3c69SVadym Kochan static const struct prestera_port_type { 227a97d3c69SVadym Kochan enum ethtool_link_mode_bit_indices eth_mode; 228a97d3c69SVadym Kochan u8 eth_type; 229a97d3c69SVadym Kochan } port_types[PRESTERA_PORT_TYPE_MAX] = { 230a97d3c69SVadym Kochan [PRESTERA_PORT_TYPE_NONE] = { 231a97d3c69SVadym Kochan .eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS, 232a97d3c69SVadym Kochan .eth_type = PORT_NONE, 233a97d3c69SVadym Kochan }, 234a97d3c69SVadym Kochan [PRESTERA_PORT_TYPE_TP] = { 235a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_TP_BIT, 236a97d3c69SVadym Kochan .eth_type = PORT_TP, 237a97d3c69SVadym Kochan }, 238a97d3c69SVadym Kochan [PRESTERA_PORT_TYPE_AUI] = { 239a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_AUI_BIT, 240a97d3c69SVadym Kochan .eth_type = PORT_AUI, 241a97d3c69SVadym Kochan }, 242a97d3c69SVadym Kochan [PRESTERA_PORT_TYPE_MII] = { 243a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_MII_BIT, 244a97d3c69SVadym Kochan .eth_type = PORT_MII, 245a97d3c69SVadym Kochan }, 246a97d3c69SVadym Kochan [PRESTERA_PORT_TYPE_FIBRE] = { 247a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_FIBRE_BIT, 248a97d3c69SVadym Kochan .eth_type = PORT_FIBRE, 249a97d3c69SVadym Kochan }, 250a97d3c69SVadym Kochan [PRESTERA_PORT_TYPE_BNC] = { 251a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_BNC_BIT, 252a97d3c69SVadym Kochan .eth_type = PORT_BNC, 253a97d3c69SVadym Kochan }, 254a97d3c69SVadym Kochan [PRESTERA_PORT_TYPE_DA] = { 255a97d3c69SVadym Kochan .eth_mode = ETHTOOL_LINK_MODE_TP_BIT, 256a97d3c69SVadym Kochan .eth_type = PORT_TP, 257a97d3c69SVadym Kochan }, 258a97d3c69SVadym Kochan [PRESTERA_PORT_TYPE_OTHER] = { 259a97d3c69SVadym Kochan .eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS, 260a97d3c69SVadym Kochan .eth_type = PORT_OTHER, 261a97d3c69SVadym Kochan } 262a97d3c69SVadym Kochan }; 263a97d3c69SVadym Kochan 264a97d3c69SVadym Kochan static const char prestera_cnt_name[PRESTERA_STATS_CNT][ETH_GSTRING_LEN] = { 265a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(good_octets_received), 266a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(bad_octets_received), 267a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(mac_trans_error), 268a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(broadcast_frames_received), 269a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(multicast_frames_received), 270a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(frames_64_octets), 271a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(frames_65_to_127_octets), 272a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(frames_128_to_255_octets), 273a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(frames_256_to_511_octets), 274a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(frames_512_to_1023_octets), 275a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(frames_1024_to_max_octets), 276a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(excessive_collision), 277a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(multicast_frames_sent), 278a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(broadcast_frames_sent), 279a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(fc_sent), 280a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(fc_received), 281a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(buffer_overrun), 282a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(undersize), 283a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(fragments), 284a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(oversize), 285a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(jabber), 286a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(rx_error_frame_received), 287a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(bad_crc), 288a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(collisions), 289a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(late_collision), 290a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(unicast_frames_received), 291a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(unicast_frames_sent), 292a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(sent_multiple), 293a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(sent_deferred), 294a97d3c69SVadym Kochan PRESTERA_STATS_FIELD(good_octets_sent), 295a97d3c69SVadym Kochan }; 296a97d3c69SVadym Kochan 297a97d3c69SVadym Kochan static void prestera_ethtool_get_drvinfo(struct net_device *dev, 298a97d3c69SVadym Kochan struct ethtool_drvinfo *drvinfo) 299a97d3c69SVadym Kochan { 300a97d3c69SVadym Kochan struct prestera_port *port = netdev_priv(dev); 301a97d3c69SVadym Kochan struct prestera_switch *sw = port->sw; 302a97d3c69SVadym Kochan 303a97d3c69SVadym Kochan strlcpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver)); 304a97d3c69SVadym Kochan strlcpy(drvinfo->bus_info, dev_name(prestera_dev(sw)), 305a97d3c69SVadym Kochan sizeof(drvinfo->bus_info)); 306a97d3c69SVadym Kochan snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), 307a97d3c69SVadym Kochan "%d.%d.%d", 308a97d3c69SVadym Kochan sw->dev->fw_rev.maj, 309a97d3c69SVadym Kochan sw->dev->fw_rev.min, 310a97d3c69SVadym Kochan sw->dev->fw_rev.sub); 311a97d3c69SVadym Kochan } 312a97d3c69SVadym Kochan 313a97d3c69SVadym Kochan static u8 prestera_port_type_get(struct prestera_port *port) 314a97d3c69SVadym Kochan { 315a97d3c69SVadym Kochan if (port->caps.type < PRESTERA_PORT_TYPE_MAX) 316a97d3c69SVadym Kochan return port_types[port->caps.type].eth_type; 317a97d3c69SVadym Kochan 318a97d3c69SVadym Kochan return PORT_OTHER; 319a97d3c69SVadym Kochan } 320a97d3c69SVadym Kochan 321a97d3c69SVadym Kochan static int prestera_port_type_set(const struct ethtool_link_ksettings *ecmd, 322a97d3c69SVadym Kochan struct prestera_port *port) 323a97d3c69SVadym Kochan { 324a97d3c69SVadym Kochan u32 new_mode = PRESTERA_LINK_MODE_MAX; 325a97d3c69SVadym Kochan u32 type, mode; 326a97d3c69SVadym Kochan 327a97d3c69SVadym Kochan for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) { 328a97d3c69SVadym Kochan if (port_types[type].eth_type == ecmd->base.port && 329a97d3c69SVadym Kochan test_bit(port_types[type].eth_mode, 330a97d3c69SVadym Kochan ecmd->link_modes.supported)) { 331a97d3c69SVadym Kochan break; 332a97d3c69SVadym Kochan } 333a97d3c69SVadym Kochan } 334a97d3c69SVadym Kochan 335a97d3c69SVadym Kochan if (type == port->caps.type) 336a97d3c69SVadym Kochan return 0; 337a97d3c69SVadym Kochan if (type != port->caps.type && ecmd->base.autoneg == AUTONEG_ENABLE) 338a97d3c69SVadym Kochan return -EINVAL; 339a97d3c69SVadym Kochan if (type == PRESTERA_PORT_TYPE_MAX) 340a97d3c69SVadym Kochan return -EOPNOTSUPP; 341a97d3c69SVadym Kochan 342a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 343a97d3c69SVadym Kochan if ((port_link_modes[mode].pr_mask & 344a97d3c69SVadym Kochan port->caps.supp_link_modes) && 345a97d3c69SVadym Kochan type == port_link_modes[mode].port_type) { 346a97d3c69SVadym Kochan new_mode = mode; 347a97d3c69SVadym Kochan } 348a97d3c69SVadym Kochan } 349a97d3c69SVadym Kochan 350bb5dbf2cSVolodymyr Mytnyk if (new_mode >= PRESTERA_LINK_MODE_MAX) 351bb5dbf2cSVolodymyr Mytnyk return -EINVAL; 352a97d3c69SVadym Kochan 353a97d3c69SVadym Kochan port->caps.type = type; 354a97d3c69SVadym Kochan port->autoneg = false; 355a97d3c69SVadym Kochan 356a97d3c69SVadym Kochan return 0; 357a97d3c69SVadym Kochan } 358a97d3c69SVadym Kochan 359a97d3c69SVadym Kochan static void prestera_modes_to_eth(unsigned long *eth_modes, u64 link_modes, 360a97d3c69SVadym Kochan u8 fec, u8 type) 361a97d3c69SVadym Kochan { 362a97d3c69SVadym Kochan u32 mode; 363a97d3c69SVadym Kochan 364a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 365a97d3c69SVadym Kochan if ((port_link_modes[mode].pr_mask & link_modes) == 0) 366a97d3c69SVadym Kochan continue; 367a97d3c69SVadym Kochan 368a97d3c69SVadym Kochan if (type != PRESTERA_PORT_TYPE_NONE && 369a97d3c69SVadym Kochan port_link_modes[mode].port_type != type) 370a97d3c69SVadym Kochan continue; 371a97d3c69SVadym Kochan 372a97d3c69SVadym Kochan __set_bit(port_link_modes[mode].eth_mode, eth_modes); 373a97d3c69SVadym Kochan } 374a97d3c69SVadym Kochan 375a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { 376a97d3c69SVadym Kochan if ((port_fec_caps[mode].pr_fec & fec) == 0) 377a97d3c69SVadym Kochan continue; 378a97d3c69SVadym Kochan 379a97d3c69SVadym Kochan __set_bit(port_fec_caps[mode].eth_mode, eth_modes); 380a97d3c69SVadym Kochan } 381a97d3c69SVadym Kochan } 382a97d3c69SVadym Kochan 383a97d3c69SVadym Kochan static void prestera_modes_from_eth(const unsigned long *eth_modes, 384a97d3c69SVadym Kochan u64 *link_modes, u8 *fec, u8 type) 385a97d3c69SVadym Kochan { 386a97d3c69SVadym Kochan u64 adver_modes = 0; 387a97d3c69SVadym Kochan u32 fec_modes = 0; 388a97d3c69SVadym Kochan u32 mode; 389a97d3c69SVadym Kochan 390a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 391a97d3c69SVadym Kochan if (!test_bit(port_link_modes[mode].eth_mode, eth_modes)) 392a97d3c69SVadym Kochan continue; 393a97d3c69SVadym Kochan 394a97d3c69SVadym Kochan if (port_link_modes[mode].port_type != type) 395a97d3c69SVadym Kochan continue; 396a97d3c69SVadym Kochan 397a97d3c69SVadym Kochan adver_modes |= port_link_modes[mode].pr_mask; 398a97d3c69SVadym Kochan } 399a97d3c69SVadym Kochan 400a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { 401a97d3c69SVadym Kochan if (!test_bit(port_fec_caps[mode].eth_mode, eth_modes)) 402a97d3c69SVadym Kochan continue; 403a97d3c69SVadym Kochan 404a97d3c69SVadym Kochan fec_modes |= port_fec_caps[mode].pr_fec; 405a97d3c69SVadym Kochan } 406a97d3c69SVadym Kochan 407a97d3c69SVadym Kochan *link_modes = adver_modes; 408a97d3c69SVadym Kochan *fec = fec_modes; 409a97d3c69SVadym Kochan } 410a97d3c69SVadym Kochan 411a97d3c69SVadym Kochan static void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd, 412a97d3c69SVadym Kochan struct prestera_port *port) 413a97d3c69SVadym Kochan { 414a97d3c69SVadym Kochan u32 mode; 415a97d3c69SVadym Kochan u8 ptype; 416a97d3c69SVadym Kochan 417a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 418a97d3c69SVadym Kochan if ((port_link_modes[mode].pr_mask & 419a97d3c69SVadym Kochan port->caps.supp_link_modes) == 0) 420a97d3c69SVadym Kochan continue; 421a97d3c69SVadym Kochan 422a97d3c69SVadym Kochan ptype = port_link_modes[mode].port_type; 423a97d3c69SVadym Kochan __set_bit(port_types[ptype].eth_mode, 424a97d3c69SVadym Kochan ecmd->link_modes.supported); 425a97d3c69SVadym Kochan } 426a97d3c69SVadym Kochan } 427a97d3c69SVadym Kochan 428a97d3c69SVadym Kochan static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd, 429a97d3c69SVadym Kochan struct prestera_port *port) 430a97d3c69SVadym Kochan { 431bb5dbf2cSVolodymyr Mytnyk struct prestera_port_phy_state *state = &port->state_phy; 432a97d3c69SVadym Kochan bool asym_pause; 433a97d3c69SVadym Kochan bool pause; 434a97d3c69SVadym Kochan u64 bitmap; 435a97d3c69SVadym Kochan int err; 436a97d3c69SVadym Kochan 437bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_phy_mode_get(port, NULL, &state->lmode_bmap, 438bb5dbf2cSVolodymyr Mytnyk &state->remote_fc.pause, 439bb5dbf2cSVolodymyr Mytnyk &state->remote_fc.asym_pause); 440bb5dbf2cSVolodymyr Mytnyk if (err) 441bb5dbf2cSVolodymyr Mytnyk netdev_warn(port->dev, "Remote link caps get failed %d", 442bb5dbf2cSVolodymyr Mytnyk port->caps.transceiver); 443bb5dbf2cSVolodymyr Mytnyk 444bb5dbf2cSVolodymyr Mytnyk bitmap = state->lmode_bmap; 445bb5dbf2cSVolodymyr Mytnyk 446a97d3c69SVadym Kochan prestera_modes_to_eth(ecmd->link_modes.lp_advertising, 447a97d3c69SVadym Kochan bitmap, 0, PRESTERA_PORT_TYPE_NONE); 448a97d3c69SVadym Kochan 449a97d3c69SVadym Kochan if (!bitmap_empty(ecmd->link_modes.lp_advertising, 450a97d3c69SVadym Kochan __ETHTOOL_LINK_MODE_MASK_NBITS)) { 451a97d3c69SVadym Kochan ethtool_link_ksettings_add_link_mode(ecmd, 452a97d3c69SVadym Kochan lp_advertising, 453a97d3c69SVadym Kochan Autoneg); 454a97d3c69SVadym Kochan } 455a97d3c69SVadym Kochan 456bb5dbf2cSVolodymyr Mytnyk pause = state->remote_fc.pause; 457bb5dbf2cSVolodymyr Mytnyk asym_pause = state->remote_fc.asym_pause; 458a97d3c69SVadym Kochan 459a97d3c69SVadym Kochan if (pause) 460a97d3c69SVadym Kochan ethtool_link_ksettings_add_link_mode(ecmd, 461a97d3c69SVadym Kochan lp_advertising, 462a97d3c69SVadym Kochan Pause); 463a97d3c69SVadym Kochan if (asym_pause) 464a97d3c69SVadym Kochan ethtool_link_ksettings_add_link_mode(ecmd, 465a97d3c69SVadym Kochan lp_advertising, 466a97d3c69SVadym Kochan Asym_Pause); 467a97d3c69SVadym Kochan } 468a97d3c69SVadym Kochan 469bb5dbf2cSVolodymyr Mytnyk static void prestera_port_link_mode_get(struct ethtool_link_ksettings *ecmd, 470a97d3c69SVadym Kochan struct prestera_port *port) 471a97d3c69SVadym Kochan { 472bb5dbf2cSVolodymyr Mytnyk struct prestera_port_mac_state *state = &port->state_mac; 473a97d3c69SVadym Kochan u32 speed; 474a97d3c69SVadym Kochan u8 duplex; 475a97d3c69SVadym Kochan int err; 476a97d3c69SVadym Kochan 477bb5dbf2cSVolodymyr Mytnyk if (!port->state_mac.oper) 478a97d3c69SVadym Kochan return; 479bb5dbf2cSVolodymyr Mytnyk 480bb5dbf2cSVolodymyr Mytnyk if (state->speed == SPEED_UNKNOWN || state->duplex == DUPLEX_UNKNOWN) { 481bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_mac_mode_get(port, NULL, &speed, 482bb5dbf2cSVolodymyr Mytnyk &duplex, NULL); 483bb5dbf2cSVolodymyr Mytnyk if (err) { 484bb5dbf2cSVolodymyr Mytnyk state->speed = SPEED_UNKNOWN; 485bb5dbf2cSVolodymyr Mytnyk state->duplex = DUPLEX_UNKNOWN; 486bb5dbf2cSVolodymyr Mytnyk } else { 487bb5dbf2cSVolodymyr Mytnyk state->speed = speed; 488bb5dbf2cSVolodymyr Mytnyk state->duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ? 489bb5dbf2cSVolodymyr Mytnyk DUPLEX_FULL : DUPLEX_HALF; 490bb5dbf2cSVolodymyr Mytnyk } 491a97d3c69SVadym Kochan } 492a97d3c69SVadym Kochan 493bb5dbf2cSVolodymyr Mytnyk ecmd->base.speed = port->state_mac.speed; 494bb5dbf2cSVolodymyr Mytnyk ecmd->base.duplex = port->state_mac.duplex; 495bb5dbf2cSVolodymyr Mytnyk } 496bb5dbf2cSVolodymyr Mytnyk 497bb5dbf2cSVolodymyr Mytnyk static void prestera_port_mdix_get(struct ethtool_link_ksettings *ecmd, 498bb5dbf2cSVolodymyr Mytnyk struct prestera_port *port) 499bb5dbf2cSVolodymyr Mytnyk { 500bb5dbf2cSVolodymyr Mytnyk struct prestera_port_phy_state *state = &port->state_phy; 501bb5dbf2cSVolodymyr Mytnyk 502*a46a5036SVolodymyr Mytnyk if (prestera_hw_port_phy_mode_get(port, 503*a46a5036SVolodymyr Mytnyk &state->mdix, NULL, NULL, NULL)) { 504bb5dbf2cSVolodymyr Mytnyk netdev_warn(port->dev, "MDIX params get failed"); 505bb5dbf2cSVolodymyr Mytnyk state->mdix = ETH_TP_MDI_INVALID; 506bb5dbf2cSVolodymyr Mytnyk } 507bb5dbf2cSVolodymyr Mytnyk 508bb5dbf2cSVolodymyr Mytnyk ecmd->base.eth_tp_mdix = port->state_phy.mdix; 509bb5dbf2cSVolodymyr Mytnyk ecmd->base.eth_tp_mdix_ctrl = port->cfg_phy.mdix; 510a97d3c69SVadym Kochan } 511a97d3c69SVadym Kochan 512a97d3c69SVadym Kochan static int 513a97d3c69SVadym Kochan prestera_ethtool_get_link_ksettings(struct net_device *dev, 514a97d3c69SVadym Kochan struct ethtool_link_ksettings *ecmd) 515a97d3c69SVadym Kochan { 516a97d3c69SVadym Kochan struct prestera_port *port = netdev_priv(dev); 517a97d3c69SVadym Kochan 518a97d3c69SVadym Kochan ethtool_link_ksettings_zero_link_mode(ecmd, supported); 519a97d3c69SVadym Kochan ethtool_link_ksettings_zero_link_mode(ecmd, advertising); 520a97d3c69SVadym Kochan ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising); 521bb5dbf2cSVolodymyr Mytnyk ecmd->base.speed = SPEED_UNKNOWN; 522bb5dbf2cSVolodymyr Mytnyk ecmd->base.duplex = DUPLEX_UNKNOWN; 523a97d3c69SVadym Kochan 524a97d3c69SVadym Kochan ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; 525a97d3c69SVadym Kochan 526a97d3c69SVadym Kochan if (port->caps.type == PRESTERA_PORT_TYPE_TP) { 527a97d3c69SVadym Kochan ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg); 528a97d3c69SVadym Kochan 529a97d3c69SVadym Kochan if (netif_running(dev) && 530a97d3c69SVadym Kochan (port->autoneg || 531a97d3c69SVadym Kochan port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)) 532a97d3c69SVadym Kochan ethtool_link_ksettings_add_link_mode(ecmd, advertising, 533a97d3c69SVadym Kochan Autoneg); 534a97d3c69SVadym Kochan } 535a97d3c69SVadym Kochan 536a97d3c69SVadym Kochan prestera_modes_to_eth(ecmd->link_modes.supported, 537a97d3c69SVadym Kochan port->caps.supp_link_modes, 538a97d3c69SVadym Kochan port->caps.supp_fec, 539a97d3c69SVadym Kochan port->caps.type); 540a97d3c69SVadym Kochan 541a97d3c69SVadym Kochan prestera_port_supp_types_get(ecmd, port); 542a97d3c69SVadym Kochan 543bb5dbf2cSVolodymyr Mytnyk if (netif_carrier_ok(dev)) 544bb5dbf2cSVolodymyr Mytnyk prestera_port_link_mode_get(ecmd, port); 545a97d3c69SVadym Kochan 546a97d3c69SVadym Kochan ecmd->base.port = prestera_port_type_get(port); 547a97d3c69SVadym Kochan 548a97d3c69SVadym Kochan if (port->autoneg) { 549a97d3c69SVadym Kochan if (netif_running(dev)) 550a97d3c69SVadym Kochan prestera_modes_to_eth(ecmd->link_modes.advertising, 551a97d3c69SVadym Kochan port->adver_link_modes, 552a97d3c69SVadym Kochan port->adver_fec, 553a97d3c69SVadym Kochan port->caps.type); 554a97d3c69SVadym Kochan 555a97d3c69SVadym Kochan if (netif_carrier_ok(dev) && 556a97d3c69SVadym Kochan port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) 557a97d3c69SVadym Kochan prestera_port_remote_cap_get(ecmd, port); 558a97d3c69SVadym Kochan } 559a97d3c69SVadym Kochan 560a97d3c69SVadym Kochan if (port->caps.type == PRESTERA_PORT_TYPE_TP && 561a97d3c69SVadym Kochan port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) 562bb5dbf2cSVolodymyr Mytnyk prestera_port_mdix_get(ecmd, port); 563a97d3c69SVadym Kochan 564a97d3c69SVadym Kochan return 0; 565a97d3c69SVadym Kochan } 566a97d3c69SVadym Kochan 567a97d3c69SVadym Kochan static int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd, 568a97d3c69SVadym Kochan struct prestera_port *port) 569a97d3c69SVadym Kochan { 570a97d3c69SVadym Kochan if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID && 571a97d3c69SVadym Kochan port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER && 572bb5dbf2cSVolodymyr Mytnyk port->caps.type == PRESTERA_PORT_TYPE_TP) { 573bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mdix = ecmd->base.eth_tp_mdix_ctrl; 574bb5dbf2cSVolodymyr Mytnyk return prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin, 575bb5dbf2cSVolodymyr Mytnyk port->autoneg, 576bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mode, 577bb5dbf2cSVolodymyr Mytnyk port->adver_link_modes, 578bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mdix); 579bb5dbf2cSVolodymyr Mytnyk } 580a97d3c69SVadym Kochan return 0; 581bb5dbf2cSVolodymyr Mytnyk 582a97d3c69SVadym Kochan } 583a97d3c69SVadym Kochan 584a97d3c69SVadym Kochan static int prestera_port_link_mode_set(struct prestera_port *port, 585a97d3c69SVadym Kochan u32 speed, u8 duplex, u8 type) 586a97d3c69SVadym Kochan { 587a97d3c69SVadym Kochan u32 new_mode = PRESTERA_LINK_MODE_MAX; 588a97d3c69SVadym Kochan u32 mode; 589bb5dbf2cSVolodymyr Mytnyk int err; 590a97d3c69SVadym Kochan 591a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { 592bb5dbf2cSVolodymyr Mytnyk if (speed != SPEED_UNKNOWN && 593bb5dbf2cSVolodymyr Mytnyk speed != port_link_modes[mode].speed) 594a97d3c69SVadym Kochan continue; 595a97d3c69SVadym Kochan 596bb5dbf2cSVolodymyr Mytnyk if (duplex != DUPLEX_UNKNOWN && 597bb5dbf2cSVolodymyr Mytnyk duplex != port_link_modes[mode].duplex) 598a97d3c69SVadym Kochan continue; 599a97d3c69SVadym Kochan 600a97d3c69SVadym Kochan if (!(port_link_modes[mode].pr_mask & 601a97d3c69SVadym Kochan port->caps.supp_link_modes)) 602a97d3c69SVadym Kochan continue; 603a97d3c69SVadym Kochan 604a97d3c69SVadym Kochan if (type != port_link_modes[mode].port_type) 605a97d3c69SVadym Kochan continue; 606a97d3c69SVadym Kochan 607a97d3c69SVadym Kochan new_mode = mode; 608a97d3c69SVadym Kochan break; 609a97d3c69SVadym Kochan } 610a97d3c69SVadym Kochan 611a97d3c69SVadym Kochan if (new_mode == PRESTERA_LINK_MODE_MAX) 612a97d3c69SVadym Kochan return -EOPNOTSUPP; 613a97d3c69SVadym Kochan 614bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin, 615bb5dbf2cSVolodymyr Mytnyk false, new_mode, 0, 616bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mdix); 617bb5dbf2cSVolodymyr Mytnyk if (err) 618bb5dbf2cSVolodymyr Mytnyk return err; 619bb5dbf2cSVolodymyr Mytnyk 620bb5dbf2cSVolodymyr Mytnyk port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF); 621bb5dbf2cSVolodymyr Mytnyk port->adver_link_modes = 0; 622bb5dbf2cSVolodymyr Mytnyk port->cfg_phy.mode = new_mode; 623bb5dbf2cSVolodymyr Mytnyk port->autoneg = false; 624bb5dbf2cSVolodymyr Mytnyk 625bb5dbf2cSVolodymyr Mytnyk return 0; 626a97d3c69SVadym Kochan } 627a97d3c69SVadym Kochan 628a97d3c69SVadym Kochan static int 629a97d3c69SVadym Kochan prestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd, 630a97d3c69SVadym Kochan struct prestera_port *port) 631a97d3c69SVadym Kochan { 632bb5dbf2cSVolodymyr Mytnyk u8 duplex = DUPLEX_UNKNOWN; 633a97d3c69SVadym Kochan 634a97d3c69SVadym Kochan if (ecmd->base.duplex != DUPLEX_UNKNOWN) 635a97d3c69SVadym Kochan duplex = ecmd->base.duplex == DUPLEX_FULL ? 636a97d3c69SVadym Kochan PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF; 637a97d3c69SVadym Kochan 638bb5dbf2cSVolodymyr Mytnyk return prestera_port_link_mode_set(port, ecmd->base.speed, duplex, 639a97d3c69SVadym Kochan port->caps.type); 640a97d3c69SVadym Kochan } 641a97d3c69SVadym Kochan 642a97d3c69SVadym Kochan static int 643a97d3c69SVadym Kochan prestera_ethtool_set_link_ksettings(struct net_device *dev, 644a97d3c69SVadym Kochan const struct ethtool_link_ksettings *ecmd) 645a97d3c69SVadym Kochan { 646a97d3c69SVadym Kochan struct prestera_port *port = netdev_priv(dev); 647a97d3c69SVadym Kochan u64 adver_modes; 648a97d3c69SVadym Kochan u8 adver_fec; 649a97d3c69SVadym Kochan int err; 650a97d3c69SVadym Kochan 651a97d3c69SVadym Kochan err = prestera_port_type_set(ecmd, port); 652a97d3c69SVadym Kochan if (err) 653a97d3c69SVadym Kochan return err; 654a97d3c69SVadym Kochan 655a97d3c69SVadym Kochan if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) { 656a97d3c69SVadym Kochan err = prestera_port_mdix_set(ecmd, port); 657a97d3c69SVadym Kochan if (err) 658a97d3c69SVadym Kochan return err; 659a97d3c69SVadym Kochan } 660a97d3c69SVadym Kochan 661a97d3c69SVadym Kochan prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes, 662a97d3c69SVadym Kochan &adver_fec, port->caps.type); 663a97d3c69SVadym Kochan 664bb5dbf2cSVolodymyr Mytnyk if (ecmd->base.autoneg == AUTONEG_ENABLE) 665bb5dbf2cSVolodymyr Mytnyk err = prestera_port_autoneg_set(port, adver_modes); 666bb5dbf2cSVolodymyr Mytnyk else 667a97d3c69SVadym Kochan err = prestera_port_speed_duplex_set(ecmd, port); 668a97d3c69SVadym Kochan 669bb5dbf2cSVolodymyr Mytnyk return err; 670a97d3c69SVadym Kochan } 671a97d3c69SVadym Kochan 672a97d3c69SVadym Kochan static int prestera_ethtool_get_fecparam(struct net_device *dev, 673a97d3c69SVadym Kochan struct ethtool_fecparam *fecparam) 674a97d3c69SVadym Kochan { 675a97d3c69SVadym Kochan struct prestera_port *port = netdev_priv(dev); 676a97d3c69SVadym Kochan u8 active; 677a97d3c69SVadym Kochan u32 mode; 678a97d3c69SVadym Kochan int err; 679a97d3c69SVadym Kochan 680bb5dbf2cSVolodymyr Mytnyk err = prestera_hw_port_mac_mode_get(port, NULL, NULL, NULL, &active); 681a97d3c69SVadym Kochan if (err) 682a97d3c69SVadym Kochan return err; 683a97d3c69SVadym Kochan 684a97d3c69SVadym Kochan fecparam->fec = 0; 685a97d3c69SVadym Kochan 686a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { 687a97d3c69SVadym Kochan if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0) 688a97d3c69SVadym Kochan continue; 689a97d3c69SVadym Kochan 690a97d3c69SVadym Kochan fecparam->fec |= port_fec_caps[mode].eth_fec; 691a97d3c69SVadym Kochan } 692a97d3c69SVadym Kochan 693a97d3c69SVadym Kochan if (active < PRESTERA_PORT_FEC_MAX) 694a97d3c69SVadym Kochan fecparam->active_fec = port_fec_caps[active].eth_fec; 695a97d3c69SVadym Kochan else 696a97d3c69SVadym Kochan fecparam->active_fec = ETHTOOL_FEC_AUTO; 697a97d3c69SVadym Kochan 698a97d3c69SVadym Kochan return 0; 699a97d3c69SVadym Kochan } 700a97d3c69SVadym Kochan 701a97d3c69SVadym Kochan static int prestera_ethtool_set_fecparam(struct net_device *dev, 702a97d3c69SVadym Kochan struct ethtool_fecparam *fecparam) 703a97d3c69SVadym Kochan { 704a97d3c69SVadym Kochan struct prestera_port *port = netdev_priv(dev); 705bb5dbf2cSVolodymyr Mytnyk struct prestera_port_mac_config cfg_mac; 706a97d3c69SVadym Kochan u32 mode; 707bb5dbf2cSVolodymyr Mytnyk u8 fec; 708a97d3c69SVadym Kochan 709a97d3c69SVadym Kochan if (port->autoneg) { 710a97d3c69SVadym Kochan netdev_err(dev, "FEC set is not allowed while autoneg is on\n"); 711a97d3c69SVadym Kochan return -EINVAL; 712a97d3c69SVadym Kochan } 713a97d3c69SVadym Kochan 714bb5dbf2cSVolodymyr Mytnyk if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { 715bb5dbf2cSVolodymyr Mytnyk netdev_err(dev, "FEC set is not allowed on non-SFP ports\n"); 716bb5dbf2cSVolodymyr Mytnyk return -EINVAL; 717bb5dbf2cSVolodymyr Mytnyk } 718a97d3c69SVadym Kochan 719a97d3c69SVadym Kochan fec = PRESTERA_PORT_FEC_MAX; 720a97d3c69SVadym Kochan for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { 721a97d3c69SVadym Kochan if ((port_fec_caps[mode].eth_fec & fecparam->fec) && 722a97d3c69SVadym Kochan (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) { 723a97d3c69SVadym Kochan fec = mode; 724a97d3c69SVadym Kochan break; 725a97d3c69SVadym Kochan } 726a97d3c69SVadym Kochan } 727a97d3c69SVadym Kochan 728bb5dbf2cSVolodymyr Mytnyk prestera_port_cfg_mac_read(port, &cfg_mac); 729bb5dbf2cSVolodymyr Mytnyk 730bb5dbf2cSVolodymyr Mytnyk if (fec == cfg_mac.fec) 731a97d3c69SVadym Kochan return 0; 732a97d3c69SVadym Kochan 733bb5dbf2cSVolodymyr Mytnyk if (fec == PRESTERA_PORT_FEC_MAX) { 734bb5dbf2cSVolodymyr Mytnyk netdev_err(dev, "Unsupported FEC requested"); 735bb5dbf2cSVolodymyr Mytnyk return -EINVAL; 736bb5dbf2cSVolodymyr Mytnyk } 737a97d3c69SVadym Kochan 738bb5dbf2cSVolodymyr Mytnyk cfg_mac.fec = fec; 739bb5dbf2cSVolodymyr Mytnyk 740bb5dbf2cSVolodymyr Mytnyk return prestera_port_cfg_mac_write(port, &cfg_mac); 741a97d3c69SVadym Kochan } 742a97d3c69SVadym Kochan 743a97d3c69SVadym Kochan static int prestera_ethtool_get_sset_count(struct net_device *dev, int sset) 744a97d3c69SVadym Kochan { 745a97d3c69SVadym Kochan switch (sset) { 746a97d3c69SVadym Kochan case ETH_SS_STATS: 747a97d3c69SVadym Kochan return PRESTERA_STATS_CNT; 748a97d3c69SVadym Kochan default: 749a97d3c69SVadym Kochan return -EOPNOTSUPP; 750a97d3c69SVadym Kochan } 751a97d3c69SVadym Kochan } 752a97d3c69SVadym Kochan 753a97d3c69SVadym Kochan static void prestera_ethtool_get_strings(struct net_device *dev, 754a97d3c69SVadym Kochan u32 stringset, u8 *data) 755a97d3c69SVadym Kochan { 756a97d3c69SVadym Kochan if (stringset != ETH_SS_STATS) 757a97d3c69SVadym Kochan return; 758a97d3c69SVadym Kochan 759a97d3c69SVadym Kochan memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name)); 760a97d3c69SVadym Kochan } 761a97d3c69SVadym Kochan 762a97d3c69SVadym Kochan static void prestera_ethtool_get_stats(struct net_device *dev, 763a97d3c69SVadym Kochan struct ethtool_stats *stats, u64 *data) 764a97d3c69SVadym Kochan { 765a97d3c69SVadym Kochan struct prestera_port *port = netdev_priv(dev); 766a97d3c69SVadym Kochan struct prestera_port_stats *port_stats; 767a97d3c69SVadym Kochan 768a97d3c69SVadym Kochan port_stats = &port->cached_hw_stats.stats; 769a97d3c69SVadym Kochan 770a97d3c69SVadym Kochan memcpy(data, port_stats, sizeof(*port_stats)); 771a97d3c69SVadym Kochan } 772a97d3c69SVadym Kochan 773a97d3c69SVadym Kochan static int prestera_ethtool_nway_reset(struct net_device *dev) 774a97d3c69SVadym Kochan { 775a97d3c69SVadym Kochan struct prestera_port *port = netdev_priv(dev); 776a97d3c69SVadym Kochan 777a97d3c69SVadym Kochan if (netif_running(dev) && 778a97d3c69SVadym Kochan port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER && 779a97d3c69SVadym Kochan port->caps.type == PRESTERA_PORT_TYPE_TP) 780a97d3c69SVadym Kochan return prestera_hw_port_autoneg_restart(port); 781a97d3c69SVadym Kochan 782a97d3c69SVadym Kochan return -EINVAL; 783a97d3c69SVadym Kochan } 784a97d3c69SVadym Kochan 785bb5dbf2cSVolodymyr Mytnyk void prestera_ethtool_port_state_changed(struct prestera_port *port, 786bb5dbf2cSVolodymyr Mytnyk struct prestera_port_event *evt) 787bb5dbf2cSVolodymyr Mytnyk { 788bb5dbf2cSVolodymyr Mytnyk struct prestera_port_mac_state *smac = &port->state_mac; 789bb5dbf2cSVolodymyr Mytnyk 790bb5dbf2cSVolodymyr Mytnyk smac->oper = evt->data.mac.oper; 791bb5dbf2cSVolodymyr Mytnyk 792bb5dbf2cSVolodymyr Mytnyk if (smac->oper) { 793bb5dbf2cSVolodymyr Mytnyk smac->mode = evt->data.mac.mode; 794bb5dbf2cSVolodymyr Mytnyk smac->speed = evt->data.mac.speed; 795bb5dbf2cSVolodymyr Mytnyk smac->duplex = evt->data.mac.duplex; 796bb5dbf2cSVolodymyr Mytnyk smac->fc = evt->data.mac.fc; 797bb5dbf2cSVolodymyr Mytnyk smac->fec = evt->data.mac.fec; 798bb5dbf2cSVolodymyr Mytnyk } else { 799bb5dbf2cSVolodymyr Mytnyk smac->mode = PRESTERA_MAC_MODE_MAX; 800bb5dbf2cSVolodymyr Mytnyk smac->speed = SPEED_UNKNOWN; 801bb5dbf2cSVolodymyr Mytnyk smac->duplex = DUPLEX_UNKNOWN; 802bb5dbf2cSVolodymyr Mytnyk smac->fc = 0; 803bb5dbf2cSVolodymyr Mytnyk smac->fec = 0; 804bb5dbf2cSVolodymyr Mytnyk } 805bb5dbf2cSVolodymyr Mytnyk } 806bb5dbf2cSVolodymyr Mytnyk 807a97d3c69SVadym Kochan const struct ethtool_ops prestera_ethtool_ops = { 808a97d3c69SVadym Kochan .get_drvinfo = prestera_ethtool_get_drvinfo, 809a97d3c69SVadym Kochan .get_link_ksettings = prestera_ethtool_get_link_ksettings, 810a97d3c69SVadym Kochan .set_link_ksettings = prestera_ethtool_set_link_ksettings, 811a97d3c69SVadym Kochan .get_fecparam = prestera_ethtool_get_fecparam, 812a97d3c69SVadym Kochan .set_fecparam = prestera_ethtool_set_fecparam, 813a97d3c69SVadym Kochan .get_sset_count = prestera_ethtool_get_sset_count, 814a97d3c69SVadym Kochan .get_strings = prestera_ethtool_get_strings, 815a97d3c69SVadym Kochan .get_ethtool_stats = prestera_ethtool_get_stats, 816a97d3c69SVadym Kochan .get_link = ethtool_op_get_link, 817a97d3c69SVadym Kochan .nway_reset = prestera_ethtool_nway_reset 818a97d3c69SVadym Kochan }; 819