18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
58c2ecf20Sopenharmony_ci#include <linux/kernel.h>
68c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "prestera_ethtool.h"
98c2ecf20Sopenharmony_ci#include "prestera.h"
108c2ecf20Sopenharmony_ci#include "prestera_hw.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define PRESTERA_STATS_CNT \
138c2ecf20Sopenharmony_ci	(sizeof(struct prestera_port_stats) / sizeof(u64))
148c2ecf20Sopenharmony_ci#define PRESTERA_STATS_IDX(name) \
158c2ecf20Sopenharmony_ci	(offsetof(struct prestera_port_stats, name) / sizeof(u64))
168c2ecf20Sopenharmony_ci#define PRESTERA_STATS_FIELD(name)	\
178c2ecf20Sopenharmony_ci	[PRESTERA_STATS_IDX(name)] = __stringify(name)
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic const char driver_kind[] = "prestera";
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic const struct prestera_link_mode {
228c2ecf20Sopenharmony_ci	enum ethtool_link_mode_bit_indices eth_mode;
238c2ecf20Sopenharmony_ci	u32 speed;
248c2ecf20Sopenharmony_ci	u64 pr_mask;
258c2ecf20Sopenharmony_ci	u8 duplex;
268c2ecf20Sopenharmony_ci	u8 port_type;
278c2ecf20Sopenharmony_ci} port_link_modes[PRESTERA_LINK_MODE_MAX] = {
288c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_10baseT_Half] = {
298c2ecf20Sopenharmony_ci		.eth_mode =  ETHTOOL_LINK_MODE_10baseT_Half_BIT,
308c2ecf20Sopenharmony_ci		.speed = 10,
318c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Half,
328c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_HALF,
338c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
348c2ecf20Sopenharmony_ci	},
358c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_10baseT_Full] = {
368c2ecf20Sopenharmony_ci		.eth_mode =  ETHTOOL_LINK_MODE_10baseT_Full_BIT,
378c2ecf20Sopenharmony_ci		.speed = 10,
388c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Full,
398c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
408c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
418c2ecf20Sopenharmony_ci	},
428c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_100baseT_Half] = {
438c2ecf20Sopenharmony_ci		.eth_mode =  ETHTOOL_LINK_MODE_100baseT_Half_BIT,
448c2ecf20Sopenharmony_ci		.speed = 100,
458c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Half,
468c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_HALF,
478c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
488c2ecf20Sopenharmony_ci	},
498c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_100baseT_Full] = {
508c2ecf20Sopenharmony_ci		.eth_mode =  ETHTOOL_LINK_MODE_100baseT_Full_BIT,
518c2ecf20Sopenharmony_ci		.speed = 100,
528c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Full,
538c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
548c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
558c2ecf20Sopenharmony_ci	},
568c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_1000baseT_Half] = {
578c2ecf20Sopenharmony_ci		.eth_mode =  ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
588c2ecf20Sopenharmony_ci		.speed = 1000,
598c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Half,
608c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_HALF,
618c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
628c2ecf20Sopenharmony_ci	},
638c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_1000baseT_Full] = {
648c2ecf20Sopenharmony_ci		.eth_mode =  ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
658c2ecf20Sopenharmony_ci		.speed = 1000,
668c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Full,
678c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
688c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
698c2ecf20Sopenharmony_ci	},
708c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_1000baseX_Full] = {
718c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
728c2ecf20Sopenharmony_ci		.speed = 1000,
738c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_1000baseX_Full,
748c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
758c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_FIBRE,
768c2ecf20Sopenharmony_ci	},
778c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_1000baseKX_Full] = {
788c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
798c2ecf20Sopenharmony_ci		.speed = 1000,
808c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_1000baseKX_Full,
818c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
828c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_2500baseX_Full] = {
858c2ecf20Sopenharmony_ci		.eth_mode =  ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
868c2ecf20Sopenharmony_ci		.speed = 2500,
878c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_2500baseX_Full,
888c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
898c2ecf20Sopenharmony_ci	},
908c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_10GbaseKR_Full] = {
918c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
928c2ecf20Sopenharmony_ci		.speed = 10000,
938c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseKR_Full,
948c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
958c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
968c2ecf20Sopenharmony_ci	},
978c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_10GbaseSR_Full] = {
988c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
998c2ecf20Sopenharmony_ci		.speed = 10000,
1008c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseSR_Full,
1018c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1028c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_FIBRE,
1038c2ecf20Sopenharmony_ci	},
1048c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_10GbaseLR_Full] = {
1058c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
1068c2ecf20Sopenharmony_ci		.speed = 10000,
1078c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseLR_Full,
1088c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1098c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_FIBRE,
1108c2ecf20Sopenharmony_ci	},
1118c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_20GbaseKR2_Full] = {
1128c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
1138c2ecf20Sopenharmony_ci		.speed = 20000,
1148c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_20GbaseKR2_Full,
1158c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1168c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
1178c2ecf20Sopenharmony_ci	},
1188c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_25GbaseCR_Full] = {
1198c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
1208c2ecf20Sopenharmony_ci		.speed = 25000,
1218c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseCR_Full,
1228c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1238c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_DA,
1248c2ecf20Sopenharmony_ci	},
1258c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_25GbaseKR_Full] = {
1268c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
1278c2ecf20Sopenharmony_ci		.speed = 25000,
1288c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseKR_Full,
1298c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1308c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
1318c2ecf20Sopenharmony_ci	},
1328c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_25GbaseSR_Full] = {
1338c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
1348c2ecf20Sopenharmony_ci		.speed = 25000,
1358c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseSR_Full,
1368c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1378c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_FIBRE,
1388c2ecf20Sopenharmony_ci	},
1398c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_40GbaseKR4_Full] = {
1408c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
1418c2ecf20Sopenharmony_ci		.speed = 40000,
1428c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseKR4_Full,
1438c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1448c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
1458c2ecf20Sopenharmony_ci	},
1468c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_40GbaseCR4_Full] = {
1478c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
1488c2ecf20Sopenharmony_ci		.speed = 40000,
1498c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseCR4_Full,
1508c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1518c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_DA,
1528c2ecf20Sopenharmony_ci	},
1538c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_40GbaseSR4_Full] = {
1548c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
1558c2ecf20Sopenharmony_ci		.speed = 40000,
1568c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseSR4_Full,
1578c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1588c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_FIBRE,
1598c2ecf20Sopenharmony_ci	},
1608c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_50GbaseCR2_Full] = {
1618c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
1628c2ecf20Sopenharmony_ci		.speed = 50000,
1638c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseCR2_Full,
1648c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1658c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_DA,
1668c2ecf20Sopenharmony_ci	},
1678c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_50GbaseKR2_Full] = {
1688c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
1698c2ecf20Sopenharmony_ci		.speed = 50000,
1708c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseKR2_Full,
1718c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1728c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
1738c2ecf20Sopenharmony_ci	},
1748c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_50GbaseSR2_Full] = {
1758c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
1768c2ecf20Sopenharmony_ci		.speed = 50000,
1778c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseSR2_Full,
1788c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1798c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_FIBRE,
1808c2ecf20Sopenharmony_ci	},
1818c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_100GbaseKR4_Full] = {
1828c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
1838c2ecf20Sopenharmony_ci		.speed = 100000,
1848c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseKR4_Full,
1858c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1868c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_TP,
1878c2ecf20Sopenharmony_ci	},
1888c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_100GbaseSR4_Full] = {
1898c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
1908c2ecf20Sopenharmony_ci		.speed = 100000,
1918c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseSR4_Full,
1928c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
1938c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_FIBRE,
1948c2ecf20Sopenharmony_ci	},
1958c2ecf20Sopenharmony_ci	[PRESTERA_LINK_MODE_100GbaseCR4_Full] = {
1968c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
1978c2ecf20Sopenharmony_ci		.speed = 100000,
1988c2ecf20Sopenharmony_ci		.pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseCR4_Full,
1998c2ecf20Sopenharmony_ci		.duplex = PRESTERA_PORT_DUPLEX_FULL,
2008c2ecf20Sopenharmony_ci		.port_type = PRESTERA_PORT_TYPE_DA,
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci};
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic const struct prestera_fec {
2058c2ecf20Sopenharmony_ci	u32 eth_fec;
2068c2ecf20Sopenharmony_ci	enum ethtool_link_mode_bit_indices eth_mode;
2078c2ecf20Sopenharmony_ci	u8 pr_fec;
2088c2ecf20Sopenharmony_ci} port_fec_caps[PRESTERA_PORT_FEC_MAX] = {
2098c2ecf20Sopenharmony_ci	[PRESTERA_PORT_FEC_OFF] = {
2108c2ecf20Sopenharmony_ci		.eth_fec = ETHTOOL_FEC_OFF,
2118c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_FEC_NONE_BIT,
2128c2ecf20Sopenharmony_ci		.pr_fec = 1 << PRESTERA_PORT_FEC_OFF,
2138c2ecf20Sopenharmony_ci	},
2148c2ecf20Sopenharmony_ci	[PRESTERA_PORT_FEC_BASER] = {
2158c2ecf20Sopenharmony_ci		.eth_fec = ETHTOOL_FEC_BASER,
2168c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_FEC_BASER_BIT,
2178c2ecf20Sopenharmony_ci		.pr_fec = 1 << PRESTERA_PORT_FEC_BASER,
2188c2ecf20Sopenharmony_ci	},
2198c2ecf20Sopenharmony_ci	[PRESTERA_PORT_FEC_RS] = {
2208c2ecf20Sopenharmony_ci		.eth_fec = ETHTOOL_FEC_RS,
2218c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_FEC_RS_BIT,
2228c2ecf20Sopenharmony_ci		.pr_fec = 1 << PRESTERA_PORT_FEC_RS,
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci};
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic const struct prestera_port_type {
2278c2ecf20Sopenharmony_ci	enum ethtool_link_mode_bit_indices eth_mode;
2288c2ecf20Sopenharmony_ci	u8 eth_type;
2298c2ecf20Sopenharmony_ci} port_types[PRESTERA_PORT_TYPE_MAX] = {
2308c2ecf20Sopenharmony_ci	[PRESTERA_PORT_TYPE_NONE] = {
2318c2ecf20Sopenharmony_ci		.eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS,
2328c2ecf20Sopenharmony_ci		.eth_type = PORT_NONE,
2338c2ecf20Sopenharmony_ci	},
2348c2ecf20Sopenharmony_ci	[PRESTERA_PORT_TYPE_TP] = {
2358c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_TP_BIT,
2368c2ecf20Sopenharmony_ci		.eth_type = PORT_TP,
2378c2ecf20Sopenharmony_ci	},
2388c2ecf20Sopenharmony_ci	[PRESTERA_PORT_TYPE_AUI] = {
2398c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_AUI_BIT,
2408c2ecf20Sopenharmony_ci		.eth_type = PORT_AUI,
2418c2ecf20Sopenharmony_ci	},
2428c2ecf20Sopenharmony_ci	[PRESTERA_PORT_TYPE_MII] = {
2438c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_MII_BIT,
2448c2ecf20Sopenharmony_ci		.eth_type = PORT_MII,
2458c2ecf20Sopenharmony_ci	},
2468c2ecf20Sopenharmony_ci	[PRESTERA_PORT_TYPE_FIBRE] = {
2478c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_FIBRE_BIT,
2488c2ecf20Sopenharmony_ci		.eth_type = PORT_FIBRE,
2498c2ecf20Sopenharmony_ci	},
2508c2ecf20Sopenharmony_ci	[PRESTERA_PORT_TYPE_BNC] = {
2518c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_BNC_BIT,
2528c2ecf20Sopenharmony_ci		.eth_type = PORT_BNC,
2538c2ecf20Sopenharmony_ci	},
2548c2ecf20Sopenharmony_ci	[PRESTERA_PORT_TYPE_DA] = {
2558c2ecf20Sopenharmony_ci		.eth_mode = ETHTOOL_LINK_MODE_TP_BIT,
2568c2ecf20Sopenharmony_ci		.eth_type = PORT_TP,
2578c2ecf20Sopenharmony_ci	},
2588c2ecf20Sopenharmony_ci	[PRESTERA_PORT_TYPE_OTHER] = {
2598c2ecf20Sopenharmony_ci		.eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS,
2608c2ecf20Sopenharmony_ci		.eth_type = PORT_OTHER,
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic const char prestera_cnt_name[PRESTERA_STATS_CNT][ETH_GSTRING_LEN] = {
2658c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(good_octets_received),
2668c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(bad_octets_received),
2678c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(mac_trans_error),
2688c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(broadcast_frames_received),
2698c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(multicast_frames_received),
2708c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(frames_64_octets),
2718c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(frames_65_to_127_octets),
2728c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(frames_128_to_255_octets),
2738c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(frames_256_to_511_octets),
2748c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(frames_512_to_1023_octets),
2758c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(frames_1024_to_max_octets),
2768c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(excessive_collision),
2778c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(multicast_frames_sent),
2788c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(broadcast_frames_sent),
2798c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(fc_sent),
2808c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(fc_received),
2818c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(buffer_overrun),
2828c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(undersize),
2838c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(fragments),
2848c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(oversize),
2858c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(jabber),
2868c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(rx_error_frame_received),
2878c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(bad_crc),
2888c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(collisions),
2898c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(late_collision),
2908c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(unicast_frames_received),
2918c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(unicast_frames_sent),
2928c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(sent_multiple),
2938c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(sent_deferred),
2948c2ecf20Sopenharmony_ci	PRESTERA_STATS_FIELD(good_octets_sent),
2958c2ecf20Sopenharmony_ci};
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic void prestera_ethtool_get_drvinfo(struct net_device *dev,
2988c2ecf20Sopenharmony_ci					 struct ethtool_drvinfo *drvinfo)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
3018c2ecf20Sopenharmony_ci	struct prestera_switch *sw = port->sw;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	strlcpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver));
3048c2ecf20Sopenharmony_ci	strlcpy(drvinfo->bus_info, dev_name(prestera_dev(sw)),
3058c2ecf20Sopenharmony_ci		sizeof(drvinfo->bus_info));
3068c2ecf20Sopenharmony_ci	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
3078c2ecf20Sopenharmony_ci		 "%d.%d.%d",
3088c2ecf20Sopenharmony_ci		 sw->dev->fw_rev.maj,
3098c2ecf20Sopenharmony_ci		 sw->dev->fw_rev.min,
3108c2ecf20Sopenharmony_ci		 sw->dev->fw_rev.sub);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic u8 prestera_port_type_get(struct prestera_port *port)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	if (port->caps.type < PRESTERA_PORT_TYPE_MAX)
3168c2ecf20Sopenharmony_ci		return port_types[port->caps.type].eth_type;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return PORT_OTHER;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int prestera_port_type_set(const struct ethtool_link_ksettings *ecmd,
3228c2ecf20Sopenharmony_ci				  struct prestera_port *port)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	u32 new_mode = PRESTERA_LINK_MODE_MAX;
3258c2ecf20Sopenharmony_ci	u32 type, mode;
3268c2ecf20Sopenharmony_ci	int err;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) {
3298c2ecf20Sopenharmony_ci		if (port_types[type].eth_type == ecmd->base.port &&
3308c2ecf20Sopenharmony_ci		    test_bit(port_types[type].eth_mode,
3318c2ecf20Sopenharmony_ci			     ecmd->link_modes.supported)) {
3328c2ecf20Sopenharmony_ci			break;
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (type == port->caps.type)
3378c2ecf20Sopenharmony_ci		return 0;
3388c2ecf20Sopenharmony_ci	if (type != port->caps.type && ecmd->base.autoneg == AUTONEG_ENABLE)
3398c2ecf20Sopenharmony_ci		return -EINVAL;
3408c2ecf20Sopenharmony_ci	if (type == PRESTERA_PORT_TYPE_MAX)
3418c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
3448c2ecf20Sopenharmony_ci		if ((port_link_modes[mode].pr_mask &
3458c2ecf20Sopenharmony_ci		    port->caps.supp_link_modes) &&
3468c2ecf20Sopenharmony_ci		    type == port_link_modes[mode].port_type) {
3478c2ecf20Sopenharmony_ci			new_mode = mode;
3488c2ecf20Sopenharmony_ci		}
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (new_mode < PRESTERA_LINK_MODE_MAX)
3528c2ecf20Sopenharmony_ci		err = prestera_hw_port_link_mode_set(port, new_mode);
3538c2ecf20Sopenharmony_ci	else
3548c2ecf20Sopenharmony_ci		err = -EINVAL;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (err)
3578c2ecf20Sopenharmony_ci		return err;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	port->caps.type = type;
3608c2ecf20Sopenharmony_ci	port->autoneg = false;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return 0;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic void prestera_modes_to_eth(unsigned long *eth_modes, u64 link_modes,
3668c2ecf20Sopenharmony_ci				  u8 fec, u8 type)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	u32 mode;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
3718c2ecf20Sopenharmony_ci		if ((port_link_modes[mode].pr_mask & link_modes) == 0)
3728c2ecf20Sopenharmony_ci			continue;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		if (type != PRESTERA_PORT_TYPE_NONE &&
3758c2ecf20Sopenharmony_ci		    port_link_modes[mode].port_type != type)
3768c2ecf20Sopenharmony_ci			continue;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		__set_bit(port_link_modes[mode].eth_mode, eth_modes);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
3828c2ecf20Sopenharmony_ci		if ((port_fec_caps[mode].pr_fec & fec) == 0)
3838c2ecf20Sopenharmony_ci			continue;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		__set_bit(port_fec_caps[mode].eth_mode, eth_modes);
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic void prestera_modes_from_eth(const unsigned long *eth_modes,
3908c2ecf20Sopenharmony_ci				    u64 *link_modes, u8 *fec, u8 type)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	u64 adver_modes = 0;
3938c2ecf20Sopenharmony_ci	u32 fec_modes = 0;
3948c2ecf20Sopenharmony_ci	u32 mode;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
3978c2ecf20Sopenharmony_ci		if (!test_bit(port_link_modes[mode].eth_mode, eth_modes))
3988c2ecf20Sopenharmony_ci			continue;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		if (port_link_modes[mode].port_type != type)
4018c2ecf20Sopenharmony_ci			continue;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		adver_modes |= port_link_modes[mode].pr_mask;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
4078c2ecf20Sopenharmony_ci		if (!test_bit(port_fec_caps[mode].eth_mode, eth_modes))
4088c2ecf20Sopenharmony_ci			continue;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		fec_modes |= port_fec_caps[mode].pr_fec;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	*link_modes = adver_modes;
4148c2ecf20Sopenharmony_ci	*fec = fec_modes;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd,
4188c2ecf20Sopenharmony_ci					 struct prestera_port *port)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	u32 mode;
4218c2ecf20Sopenharmony_ci	u8 ptype;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
4248c2ecf20Sopenharmony_ci		if ((port_link_modes[mode].pr_mask &
4258c2ecf20Sopenharmony_ci		    port->caps.supp_link_modes) == 0)
4268c2ecf20Sopenharmony_ci			continue;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		ptype = port_link_modes[mode].port_type;
4298c2ecf20Sopenharmony_ci		__set_bit(port_types[ptype].eth_mode,
4308c2ecf20Sopenharmony_ci			  ecmd->link_modes.supported);
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd,
4358c2ecf20Sopenharmony_ci					 struct prestera_port *port)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	bool asym_pause;
4388c2ecf20Sopenharmony_ci	bool pause;
4398c2ecf20Sopenharmony_ci	u64 bitmap;
4408c2ecf20Sopenharmony_ci	int err;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	err = prestera_hw_port_remote_cap_get(port, &bitmap);
4438c2ecf20Sopenharmony_ci	if (!err) {
4448c2ecf20Sopenharmony_ci		prestera_modes_to_eth(ecmd->link_modes.lp_advertising,
4458c2ecf20Sopenharmony_ci				      bitmap, 0, PRESTERA_PORT_TYPE_NONE);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		if (!bitmap_empty(ecmd->link_modes.lp_advertising,
4488c2ecf20Sopenharmony_ci				  __ETHTOOL_LINK_MODE_MASK_NBITS)) {
4498c2ecf20Sopenharmony_ci			ethtool_link_ksettings_add_link_mode(ecmd,
4508c2ecf20Sopenharmony_ci							     lp_advertising,
4518c2ecf20Sopenharmony_ci							     Autoneg);
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	err = prestera_hw_port_remote_fc_get(port, &pause, &asym_pause);
4568c2ecf20Sopenharmony_ci	if (err)
4578c2ecf20Sopenharmony_ci		return;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	if (pause)
4608c2ecf20Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(ecmd,
4618c2ecf20Sopenharmony_ci						     lp_advertising,
4628c2ecf20Sopenharmony_ci						     Pause);
4638c2ecf20Sopenharmony_ci	if (asym_pause)
4648c2ecf20Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(ecmd,
4658c2ecf20Sopenharmony_ci						     lp_advertising,
4668c2ecf20Sopenharmony_ci						     Asym_Pause);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic void prestera_port_speed_get(struct ethtool_link_ksettings *ecmd,
4708c2ecf20Sopenharmony_ci				    struct prestera_port *port)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	u32 speed;
4738c2ecf20Sopenharmony_ci	int err;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	err = prestera_hw_port_speed_get(port, &speed);
4768c2ecf20Sopenharmony_ci	ecmd->base.speed = err ? SPEED_UNKNOWN : speed;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic void prestera_port_duplex_get(struct ethtool_link_ksettings *ecmd,
4808c2ecf20Sopenharmony_ci				     struct prestera_port *port)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	u8 duplex;
4838c2ecf20Sopenharmony_ci	int err;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	err = prestera_hw_port_duplex_get(port, &duplex);
4868c2ecf20Sopenharmony_ci	if (err) {
4878c2ecf20Sopenharmony_ci		ecmd->base.duplex = DUPLEX_UNKNOWN;
4888c2ecf20Sopenharmony_ci		return;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	ecmd->base.duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ?
4928c2ecf20Sopenharmony_ci			    DUPLEX_FULL : DUPLEX_HALF;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic int
4968c2ecf20Sopenharmony_ciprestera_ethtool_get_link_ksettings(struct net_device *dev,
4978c2ecf20Sopenharmony_ci				    struct ethtool_link_ksettings *ecmd)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(ecmd, supported);
5028c2ecf20Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(ecmd, advertising);
5038c2ecf20Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (port->caps.type == PRESTERA_PORT_TYPE_TP) {
5088c2ecf20Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		if (netif_running(dev) &&
5118c2ecf20Sopenharmony_ci		    (port->autoneg ||
5128c2ecf20Sopenharmony_ci		     port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER))
5138c2ecf20Sopenharmony_ci			ethtool_link_ksettings_add_link_mode(ecmd, advertising,
5148c2ecf20Sopenharmony_ci							     Autoneg);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	prestera_modes_to_eth(ecmd->link_modes.supported,
5188c2ecf20Sopenharmony_ci			      port->caps.supp_link_modes,
5198c2ecf20Sopenharmony_ci			      port->caps.supp_fec,
5208c2ecf20Sopenharmony_ci			      port->caps.type);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	prestera_port_supp_types_get(ecmd, port);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (netif_carrier_ok(dev)) {
5258c2ecf20Sopenharmony_ci		prestera_port_speed_get(ecmd, port);
5268c2ecf20Sopenharmony_ci		prestera_port_duplex_get(ecmd, port);
5278c2ecf20Sopenharmony_ci	} else {
5288c2ecf20Sopenharmony_ci		ecmd->base.speed = SPEED_UNKNOWN;
5298c2ecf20Sopenharmony_ci		ecmd->base.duplex = DUPLEX_UNKNOWN;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	ecmd->base.port = prestera_port_type_get(port);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (port->autoneg) {
5358c2ecf20Sopenharmony_ci		if (netif_running(dev))
5368c2ecf20Sopenharmony_ci			prestera_modes_to_eth(ecmd->link_modes.advertising,
5378c2ecf20Sopenharmony_ci					      port->adver_link_modes,
5388c2ecf20Sopenharmony_ci					      port->adver_fec,
5398c2ecf20Sopenharmony_ci					      port->caps.type);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci		if (netif_carrier_ok(dev) &&
5428c2ecf20Sopenharmony_ci		    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
5438c2ecf20Sopenharmony_ci			prestera_port_remote_cap_get(ecmd, port);
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (port->caps.type == PRESTERA_PORT_TYPE_TP &&
5478c2ecf20Sopenharmony_ci	    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
5488c2ecf20Sopenharmony_ci		prestera_hw_port_mdix_get(port, &ecmd->base.eth_tp_mdix,
5498c2ecf20Sopenharmony_ci					  &ecmd->base.eth_tp_mdix_ctrl);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	return 0;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd,
5558c2ecf20Sopenharmony_ci				  struct prestera_port *port)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID &&
5588c2ecf20Sopenharmony_ci	    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&
5598c2ecf20Sopenharmony_ci	    port->caps.type == PRESTERA_PORT_TYPE_TP)
5608c2ecf20Sopenharmony_ci		return prestera_hw_port_mdix_set(port,
5618c2ecf20Sopenharmony_ci						 ecmd->base.eth_tp_mdix_ctrl);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	return 0;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int prestera_port_link_mode_set(struct prestera_port *port,
5678c2ecf20Sopenharmony_ci				       u32 speed, u8 duplex, u8 type)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	u32 new_mode = PRESTERA_LINK_MODE_MAX;
5708c2ecf20Sopenharmony_ci	u32 mode;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
5738c2ecf20Sopenharmony_ci		if (speed != port_link_modes[mode].speed)
5748c2ecf20Sopenharmony_ci			continue;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci		if (duplex != port_link_modes[mode].duplex)
5778c2ecf20Sopenharmony_ci			continue;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci		if (!(port_link_modes[mode].pr_mask &
5808c2ecf20Sopenharmony_ci		    port->caps.supp_link_modes))
5818c2ecf20Sopenharmony_ci			continue;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		if (type != port_link_modes[mode].port_type)
5848c2ecf20Sopenharmony_ci			continue;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		new_mode = mode;
5878c2ecf20Sopenharmony_ci		break;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (new_mode == PRESTERA_LINK_MODE_MAX)
5918c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return prestera_hw_port_link_mode_set(port, new_mode);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int
5978c2ecf20Sopenharmony_ciprestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd,
5988c2ecf20Sopenharmony_ci			       struct prestera_port *port)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	u32 curr_mode;
6018c2ecf20Sopenharmony_ci	u8 duplex;
6028c2ecf20Sopenharmony_ci	u32 speed;
6038c2ecf20Sopenharmony_ci	int err;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	err = prestera_hw_port_link_mode_get(port, &curr_mode);
6068c2ecf20Sopenharmony_ci	if (err)
6078c2ecf20Sopenharmony_ci		return err;
6088c2ecf20Sopenharmony_ci	if (curr_mode >= PRESTERA_LINK_MODE_MAX)
6098c2ecf20Sopenharmony_ci		return -EINVAL;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (ecmd->base.duplex != DUPLEX_UNKNOWN)
6128c2ecf20Sopenharmony_ci		duplex = ecmd->base.duplex == DUPLEX_FULL ?
6138c2ecf20Sopenharmony_ci			 PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF;
6148c2ecf20Sopenharmony_ci	else
6158c2ecf20Sopenharmony_ci		duplex = port_link_modes[curr_mode].duplex;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	if (ecmd->base.speed != SPEED_UNKNOWN)
6188c2ecf20Sopenharmony_ci		speed = ecmd->base.speed;
6198c2ecf20Sopenharmony_ci	else
6208c2ecf20Sopenharmony_ci		speed = port_link_modes[curr_mode].speed;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return prestera_port_link_mode_set(port, speed, duplex,
6238c2ecf20Sopenharmony_ci					   port->caps.type);
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic int
6278c2ecf20Sopenharmony_ciprestera_ethtool_set_link_ksettings(struct net_device *dev,
6288c2ecf20Sopenharmony_ci				    const struct ethtool_link_ksettings *ecmd)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
6318c2ecf20Sopenharmony_ci	u64 adver_modes;
6328c2ecf20Sopenharmony_ci	u8 adver_fec;
6338c2ecf20Sopenharmony_ci	int err;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	err = prestera_port_type_set(ecmd, port);
6368c2ecf20Sopenharmony_ci	if (err)
6378c2ecf20Sopenharmony_ci		return err;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) {
6408c2ecf20Sopenharmony_ci		err = prestera_port_mdix_set(ecmd, port);
6418c2ecf20Sopenharmony_ci		if (err)
6428c2ecf20Sopenharmony_ci			return err;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes,
6468c2ecf20Sopenharmony_ci				&adver_fec, port->caps.type);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	err = prestera_port_autoneg_set(port,
6498c2ecf20Sopenharmony_ci					ecmd->base.autoneg == AUTONEG_ENABLE,
6508c2ecf20Sopenharmony_ci					adver_modes, adver_fec);
6518c2ecf20Sopenharmony_ci	if (err)
6528c2ecf20Sopenharmony_ci		return err;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (ecmd->base.autoneg == AUTONEG_DISABLE) {
6558c2ecf20Sopenharmony_ci		err = prestera_port_speed_duplex_set(ecmd, port);
6568c2ecf20Sopenharmony_ci		if (err)
6578c2ecf20Sopenharmony_ci			return err;
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	return 0;
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int prestera_ethtool_get_fecparam(struct net_device *dev,
6648c2ecf20Sopenharmony_ci					 struct ethtool_fecparam *fecparam)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
6678c2ecf20Sopenharmony_ci	u8 active;
6688c2ecf20Sopenharmony_ci	u32 mode;
6698c2ecf20Sopenharmony_ci	int err;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	err = prestera_hw_port_fec_get(port, &active);
6728c2ecf20Sopenharmony_ci	if (err)
6738c2ecf20Sopenharmony_ci		return err;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	fecparam->fec = 0;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
6788c2ecf20Sopenharmony_ci		if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0)
6798c2ecf20Sopenharmony_ci			continue;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		fecparam->fec |= port_fec_caps[mode].eth_fec;
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (active < PRESTERA_PORT_FEC_MAX)
6858c2ecf20Sopenharmony_ci		fecparam->active_fec = port_fec_caps[active].eth_fec;
6868c2ecf20Sopenharmony_ci	else
6878c2ecf20Sopenharmony_ci		fecparam->active_fec = ETHTOOL_FEC_AUTO;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	return 0;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int prestera_ethtool_set_fecparam(struct net_device *dev,
6938c2ecf20Sopenharmony_ci					 struct ethtool_fecparam *fecparam)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
6968c2ecf20Sopenharmony_ci	u8 fec, active;
6978c2ecf20Sopenharmony_ci	u32 mode;
6988c2ecf20Sopenharmony_ci	int err;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (port->autoneg) {
7018c2ecf20Sopenharmony_ci		netdev_err(dev, "FEC set is not allowed while autoneg is on\n");
7028c2ecf20Sopenharmony_ci		return -EINVAL;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	err = prestera_hw_port_fec_get(port, &active);
7068c2ecf20Sopenharmony_ci	if (err)
7078c2ecf20Sopenharmony_ci		return err;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	fec = PRESTERA_PORT_FEC_MAX;
7108c2ecf20Sopenharmony_ci	for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
7118c2ecf20Sopenharmony_ci		if ((port_fec_caps[mode].eth_fec & fecparam->fec) &&
7128c2ecf20Sopenharmony_ci		    (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) {
7138c2ecf20Sopenharmony_ci			fec = mode;
7148c2ecf20Sopenharmony_ci			break;
7158c2ecf20Sopenharmony_ci		}
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (fec == active)
7198c2ecf20Sopenharmony_ci		return 0;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	if (fec == PRESTERA_PORT_FEC_MAX)
7228c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	return prestera_hw_port_fec_set(port, fec);
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic int prestera_ethtool_get_sset_count(struct net_device *dev, int sset)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	switch (sset) {
7308c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
7318c2ecf20Sopenharmony_ci		return PRESTERA_STATS_CNT;
7328c2ecf20Sopenharmony_ci	default:
7338c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic void prestera_ethtool_get_strings(struct net_device *dev,
7388c2ecf20Sopenharmony_ci					 u32 stringset, u8 *data)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	if (stringset != ETH_SS_STATS)
7418c2ecf20Sopenharmony_ci		return;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name));
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic void prestera_ethtool_get_stats(struct net_device *dev,
7478c2ecf20Sopenharmony_ci				       struct ethtool_stats *stats, u64 *data)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
7508c2ecf20Sopenharmony_ci	struct prestera_port_stats *port_stats;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	port_stats = &port->cached_hw_stats.stats;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	memcpy(data, port_stats, sizeof(*port_stats));
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic int prestera_ethtool_nway_reset(struct net_device *dev)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	if (netif_running(dev) &&
7628c2ecf20Sopenharmony_ci	    port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&
7638c2ecf20Sopenharmony_ci	    port->caps.type == PRESTERA_PORT_TYPE_TP)
7648c2ecf20Sopenharmony_ci		return prestera_hw_port_autoneg_restart(port);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	return -EINVAL;
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ciconst struct ethtool_ops prestera_ethtool_ops = {
7708c2ecf20Sopenharmony_ci	.get_drvinfo = prestera_ethtool_get_drvinfo,
7718c2ecf20Sopenharmony_ci	.get_link_ksettings = prestera_ethtool_get_link_ksettings,
7728c2ecf20Sopenharmony_ci	.set_link_ksettings = prestera_ethtool_set_link_ksettings,
7738c2ecf20Sopenharmony_ci	.get_fecparam = prestera_ethtool_get_fecparam,
7748c2ecf20Sopenharmony_ci	.set_fecparam = prestera_ethtool_set_fecparam,
7758c2ecf20Sopenharmony_ci	.get_sset_count = prestera_ethtool_get_sset_count,
7768c2ecf20Sopenharmony_ci	.get_strings = prestera_ethtool_get_strings,
7778c2ecf20Sopenharmony_ci	.get_ethtool_stats = prestera_ethtool_get_stats,
7788c2ecf20Sopenharmony_ci	.get_link = ethtool_op_get_link,
7798c2ecf20Sopenharmony_ci	.nway_reset = prestera_ethtool_nway_reset
7808c2ecf20Sopenharmony_ci};
781