Add ethtool operations, primarily related to queues and ports, as well as device statistics.
Unified · Split
| @@ -0,0 +1,97 @@ | ||
| 1 | +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ | |
| 2 | + | |
| 3 | +#ifndef _FUN_PORT_H | |
| 4 | +#define _FUN_PORT_H | |
| 5 | + | |
| 6 | +enum port_mac_rx_stats { | |
| 7 | + PORT_MAC_RX_etherStatsOctets = 0x0, | |
| 8 | + PORT_MAC_RX_OctetsReceivedOK = 0x1, | |
| 9 | + PORT_MAC_RX_aAlignmentErrors = 0x2, | |
| 10 | + PORT_MAC_RX_aPAUSEMACCtrlFramesReceived = 0x3, | |
| 11 | + PORT_MAC_RX_aFrameTooLongErrors = 0x4, | |
| 12 | + PORT_MAC_RX_aInRangeLengthErrors = 0x5, | |
| 13 | + PORT_MAC_RX_aFramesReceivedOK = 0x6, | |
| 14 | + PORT_MAC_RX_aFrameCheckSequenceErrors = 0x7, | |
| 15 | + PORT_MAC_RX_VLANReceivedOK = 0x8, | |
| 16 | + PORT_MAC_RX_ifInErrors = 0x9, | |
| 17 | + PORT_MAC_RX_ifInUcastPkts = 0xa, | |
| 18 | + PORT_MAC_RX_ifInMulticastPkts = 0xb, | |
| 19 | + PORT_MAC_RX_ifInBroadcastPkts = 0xc, | |
| 20 | + PORT_MAC_RX_etherStatsDropEvents = 0xd, | |
| 21 | + PORT_MAC_RX_etherStatsPkts = 0xe, | |
| 22 | + PORT_MAC_RX_etherStatsUndersizePkts = 0xf, | |
| 23 | + PORT_MAC_RX_etherStatsPkts64Octets = 0x10, | |
| 24 | + PORT_MAC_RX_etherStatsPkts65to127Octets = 0x11, | |
| 25 | + PORT_MAC_RX_etherStatsPkts128to255Octets = 0x12, | |
| 26 | + PORT_MAC_RX_etherStatsPkts256to511Octets = 0x13, | |
| 27 | + PORT_MAC_RX_etherStatsPkts512to1023Octets = 0x14, | |
| 28 | + PORT_MAC_RX_etherStatsPkts1024to1518Octets = 0x15, | |
| 29 | + PORT_MAC_RX_etherStatsPkts1519toMaxOctets = 0x16, | |
| 30 | + PORT_MAC_RX_etherStatsOversizePkts = 0x17, | |
| 31 | + PORT_MAC_RX_etherStatsJabbers = 0x18, | |
| 32 | + PORT_MAC_RX_etherStatsFragments = 0x19, | |
| 33 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_0 = 0x1a, | |
| 34 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_1 = 0x1b, | |
| 35 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_2 = 0x1c, | |
| 36 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_3 = 0x1d, | |
| 37 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_4 = 0x1e, | |
| 38 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_5 = 0x1f, | |
| 39 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_6 = 0x20, | |
| 40 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_7 = 0x21, | |
| 41 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_8 = 0x22, | |
| 42 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_9 = 0x23, | |
| 43 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_10 = 0x24, | |
| 44 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_11 = 0x25, | |
| 45 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_12 = 0x26, | |
| 46 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_13 = 0x27, | |
| 47 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_14 = 0x28, | |
| 48 | + PORT_MAC_RX_CBFCPAUSEFramesReceived_15 = 0x29, | |
| 49 | + PORT_MAC_RX_MACControlFramesReceived = 0x2a, | |
| 50 | + PORT_MAC_RX_STATS_MAX = 0x2b, | |
| 51 | +}; | |
| 52 | + | |
| 53 | +enum port_mac_tx_stats { | |
| 54 | + PORT_MAC_TX_etherStatsOctets = 0x0, | |
| 55 | + PORT_MAC_TX_OctetsTransmittedOK = 0x1, | |
| 56 | + PORT_MAC_TX_aPAUSEMACCtrlFramesTransmitted = 0x2, | |
| 57 | + PORT_MAC_TX_aFramesTransmittedOK = 0x3, | |
| 58 | + PORT_MAC_TX_VLANTransmittedOK = 0x4, | |
| 59 | + PORT_MAC_TX_ifOutErrors = 0x5, | |
| 60 | + PORT_MAC_TX_ifOutUcastPkts = 0x6, | |
| 61 | + PORT_MAC_TX_ifOutMulticastPkts = 0x7, | |
| 62 | + PORT_MAC_TX_ifOutBroadcastPkts = 0x8, | |
| 63 | + PORT_MAC_TX_etherStatsPkts64Octets = 0x9, | |
| 64 | + PORT_MAC_TX_etherStatsPkts65to127Octets = 0xa, | |
| 65 | + PORT_MAC_TX_etherStatsPkts128to255Octets = 0xb, | |
| 66 | + PORT_MAC_TX_etherStatsPkts256to511Octets = 0xc, | |
| 67 | + PORT_MAC_TX_etherStatsPkts512to1023Octets = 0xd, | |
| 68 | + PORT_MAC_TX_etherStatsPkts1024to1518Octets = 0xe, | |
| 69 | + PORT_MAC_TX_etherStatsPkts1519toMaxOctets = 0xf, | |
| 70 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_0 = 0x10, | |
| 71 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_1 = 0x11, | |
| 72 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_2 = 0x12, | |
| 73 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_3 = 0x13, | |
| 74 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_4 = 0x14, | |
| 75 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_5 = 0x15, | |
| 76 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_6 = 0x16, | |
| 77 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_7 = 0x17, | |
| 78 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_8 = 0x18, | |
| 79 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_9 = 0x19, | |
| 80 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_10 = 0x1a, | |
| 81 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_11 = 0x1b, | |
| 82 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_12 = 0x1c, | |
| 83 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_13 = 0x1d, | |
| 84 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_14 = 0x1e, | |
| 85 | + PORT_MAC_TX_CBFCPAUSEFramesTransmitted_15 = 0x1f, | |
| 86 | + PORT_MAC_TX_MACControlFramesTransmitted = 0x20, | |
| 87 | + PORT_MAC_TX_etherStatsPkts = 0x21, | |
| 88 | + PORT_MAC_TX_STATS_MAX = 0x22, | |
| 89 | +}; | |
| 90 | + | |
| 91 | +enum port_mac_fec_stats { | |
| 92 | + PORT_MAC_FEC_Correctable = 0x0, | |
| 93 | + PORT_MAC_FEC_Uncorrectable = 0x1, | |
| 94 | + PORT_MAC_FEC_STATS_MAX = 0x2, | |
| 95 | +}; | |
| 96 | + | |
| 97 | +#endif /* _FUN_PORT_H */ | |
| @@ -0,0 +1,1162 @@ | ||
| 1 | +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) | |
| 2 | + | |
| 3 | +#include <linux/ethtool.h> | |
| 4 | +#include <linux/linkmode.h> | |
| 5 | +#include <linux/netdevice.h> | |
| 6 | +#include <linux/nvme.h> | |
| 7 | +#include <linux/io.h> | |
| 8 | +#include <linux/io-64-nonatomic-lo-hi.h> | |
| 9 | +#include <linux/pci.h> | |
| 10 | +#include <linux/rtnetlink.h> | |
| 11 | +#include "funeth.h" | |
| 12 | +#include "fun_port.h" | |
| 13 | +#include "funeth_txrx.h" | |
| 14 | + | |
| 15 | +/* Min queue depth. The smallest power-of-2 supporting jumbo frames with 4K | |
| 16 | + * pages is 8. Require it for all types of queues though some could work with | |
| 17 | + * fewer entries. | |
| 18 | + */ | |
| 19 | +#define FUNETH_MIN_QDEPTH 8 | |
| 20 | + | |
| 21 | +static const char mac_tx_stat_names[][ETH_GSTRING_LEN] = { | |
| 22 | + "mac_tx_octets_total", | |
| 23 | + "mac_tx_frames_total", | |
| 24 | + "mac_tx_vlan_frames_ok", | |
| 25 | + "mac_tx_unicast_frames", | |
| 26 | + "mac_tx_multicast_frames", | |
| 27 | + "mac_tx_broadcast_frames", | |
| 28 | + "mac_tx_errors", | |
| 29 | + "mac_tx_CBFCPAUSE0", | |
| 30 | + "mac_tx_CBFCPAUSE1", | |
| 31 | + "mac_tx_CBFCPAUSE2", | |
| 32 | + "mac_tx_CBFCPAUSE3", | |
| 33 | + "mac_tx_CBFCPAUSE4", | |
| 34 | + "mac_tx_CBFCPAUSE5", | |
| 35 | + "mac_tx_CBFCPAUSE6", | |
| 36 | + "mac_tx_CBFCPAUSE7", | |
| 37 | + "mac_tx_CBFCPAUSE8", | |
| 38 | + "mac_tx_CBFCPAUSE9", | |
| 39 | + "mac_tx_CBFCPAUSE10", | |
| 40 | + "mac_tx_CBFCPAUSE11", | |
| 41 | + "mac_tx_CBFCPAUSE12", | |
| 42 | + "mac_tx_CBFCPAUSE13", | |
| 43 | + "mac_tx_CBFCPAUSE14", | |
| 44 | + "mac_tx_CBFCPAUSE15", | |
| 45 | +}; | |
| 46 | + | |
| 47 | +static const char mac_rx_stat_names[][ETH_GSTRING_LEN] = { | |
| 48 | + "mac_rx_octets_total", | |
| 49 | + "mac_rx_frames_total", | |
| 50 | + "mac_rx_VLAN_frames_ok", | |
| 51 | + "mac_rx_unicast_frames", | |
| 52 | + "mac_rx_multicast_frames", | |
| 53 | + "mac_rx_broadcast_frames", | |
| 54 | + "mac_rx_drop_events", | |
| 55 | + "mac_rx_errors", | |
| 56 | + "mac_rx_alignment_errors", | |
| 57 | + "mac_rx_CBFCPAUSE0", | |
| 58 | + "mac_rx_CBFCPAUSE1", | |
| 59 | + "mac_rx_CBFCPAUSE2", | |
| 60 | + "mac_rx_CBFCPAUSE3", | |
| 61 | + "mac_rx_CBFCPAUSE4", | |
| 62 | + "mac_rx_CBFCPAUSE5", | |
| 63 | + "mac_rx_CBFCPAUSE6", | |
| 64 | + "mac_rx_CBFCPAUSE7", | |
| 65 | + "mac_rx_CBFCPAUSE8", | |
| 66 | + "mac_rx_CBFCPAUSE9", | |
| 67 | + "mac_rx_CBFCPAUSE10", | |
| 68 | + "mac_rx_CBFCPAUSE11", | |
| 69 | + "mac_rx_CBFCPAUSE12", | |
| 70 | + "mac_rx_CBFCPAUSE13", | |
| 71 | + "mac_rx_CBFCPAUSE14", | |
| 72 | + "mac_rx_CBFCPAUSE15", | |
| 73 | +}; | |
| 74 | + | |
| 75 | +static const char * const txq_stat_names[] = { | |
| 76 | + "tx_pkts", | |
| 77 | + "tx_bytes", | |
| 78 | + "tx_cso", | |
| 79 | + "tx_tso", | |
| 80 | + "tx_encapsulated_tso", | |
| 81 | + "tx_more", | |
| 82 | + "tx_queue_stops", | |
| 83 | + "tx_queue_restarts", | |
| 84 | + "tx_mapping_errors", | |
| 85 | + "tx_tls_encrypted_packets", | |
| 86 | + "tx_tls_encrypted_bytes", | |
| 87 | + "tx_tls_ooo", | |
| 88 | + "tx_tls_drop_no_sync_data", | |
| 89 | +}; | |
| 90 | + | |
| 91 | +static const char * const xdpq_stat_names[] = { | |
| 92 | + "tx_xdp_pkts", | |
| 93 | + "tx_xdp_bytes", | |
| 94 | + "tx_xdp_full", | |
| 95 | + "tx_xdp_mapping_errors", | |
| 96 | +}; | |
| 97 | + | |
| 98 | +static const char * const rxq_stat_names[] = { | |
| 99 | + "rx_pkts", | |
| 100 | + "rx_bytes", | |
| 101 | + "rx_cso", | |
| 102 | + "gro_pkts", | |
| 103 | + "gro_merged", | |
| 104 | + "rx_xdp_tx", | |
| 105 | + "rx_xdp_redir", | |
| 106 | + "rx_xdp_drops", | |
| 107 | + "rx_buffers", | |
| 108 | + "rx_page_allocs", | |
| 109 | + "rx_drops", | |
| 110 | + "rx_budget_exhausted", | |
| 111 | + "rx_mapping_errors", | |
| 112 | +}; | |
| 113 | + | |
| 114 | +static const char * const tls_stat_names[] = { | |
| 115 | + "tx_tls_ctx", | |
| 116 | + "tx_tls_del", | |
| 117 | + "tx_tls_resync", | |
| 118 | +}; | |
| 119 | + | |
| 120 | +static void fun_link_modes_to_ethtool(u64 modes, | |
| 121 | + unsigned long *ethtool_modes_map) | |
| 122 | +{ | |
| 123 | +#define ADD_LINK_MODE(mode) \ | |
| 124 | + __set_bit(ETHTOOL_LINK_MODE_ ## mode ## _BIT, ethtool_modes_map) | |
| 125 | + | |
| 126 | + if (modes & FUN_PORT_CAP_AUTONEG) | |
| 127 | + ADD_LINK_MODE(Autoneg); | |
| 128 | + if (modes & FUN_PORT_CAP_1000_X) | |
| 129 | + ADD_LINK_MODE(1000baseX_Full); | |
| 130 | + if (modes & FUN_PORT_CAP_10G_R) { | |
| 131 | + ADD_LINK_MODE(10000baseCR_Full); | |
| 132 | + ADD_LINK_MODE(10000baseSR_Full); | |
| 133 | + ADD_LINK_MODE(10000baseLR_Full); | |
| 134 | + ADD_LINK_MODE(10000baseER_Full); | |
| 135 | + } | |
| 136 | + if (modes & FUN_PORT_CAP_25G_R) { | |
| 137 | + ADD_LINK_MODE(25000baseCR_Full); | |
| 138 | + ADD_LINK_MODE(25000baseSR_Full); | |
| 139 | + } | |
| 140 | + if (modes & FUN_PORT_CAP_40G_R4) { | |
| 141 | + ADD_LINK_MODE(40000baseCR4_Full); | |
| 142 | + ADD_LINK_MODE(40000baseSR4_Full); | |
| 143 | + ADD_LINK_MODE(40000baseLR4_Full); | |
| 144 | + } | |
| 145 | + if (modes & FUN_PORT_CAP_50G_R2) { | |
| 146 | + ADD_LINK_MODE(50000baseCR2_Full); | |
| 147 | + ADD_LINK_MODE(50000baseSR2_Full); | |
| 148 | + } | |
| 149 | + if (modes & FUN_PORT_CAP_50G_R) { | |
| 150 | + ADD_LINK_MODE(50000baseCR_Full); | |
| 151 | + ADD_LINK_MODE(50000baseSR_Full); | |
| 152 | + ADD_LINK_MODE(50000baseLR_ER_FR_Full); | |
| 153 | + } | |
| 154 | + if (modes & FUN_PORT_CAP_100G_R4) { | |
| 155 | + ADD_LINK_MODE(100000baseCR4_Full); | |
| 156 | + ADD_LINK_MODE(100000baseSR4_Full); | |
| 157 | + ADD_LINK_MODE(100000baseLR4_ER4_Full); | |
| 158 | + } | |
| 159 | + if (modes & FUN_PORT_CAP_100G_R2) { | |
| 160 | + ADD_LINK_MODE(100000baseCR2_Full); | |
| 161 | + ADD_LINK_MODE(100000baseSR2_Full); | |
| 162 | + ADD_LINK_MODE(100000baseLR2_ER2_FR2_Full); | |
| 163 | + } | |
| 164 | + if (modes & FUN_PORT_CAP_FEC_NONE) | |
| 165 | + ADD_LINK_MODE(FEC_NONE); | |
| 166 | + if (modes & FUN_PORT_CAP_FEC_FC) | |
| 167 | + ADD_LINK_MODE(FEC_BASER); | |
| 168 | + if (modes & FUN_PORT_CAP_FEC_RS) | |
| 169 | + ADD_LINK_MODE(FEC_RS); | |
| 170 | + if (modes & FUN_PORT_CAP_RX_PAUSE) | |
| 171 | + ADD_LINK_MODE(Pause); | |
| 172 | + | |
| 173 | +#undef ADD_LINK_MODE | |
| 174 | +} | |
| 175 | + | |
| 176 | +static void set_asym_pause(u64 advertising, struct ethtool_link_ksettings *ks) | |
| 177 | +{ | |
| 178 | + bool rx_pause, tx_pause; | |
| 179 | + | |
| 180 | + rx_pause = advertising & FUN_PORT_CAP_RX_PAUSE; | |
| 181 | + tx_pause = advertising & FUN_PORT_CAP_TX_PAUSE; | |
| 182 | + if (tx_pause ^ rx_pause) | |
| 183 | + ethtool_link_ksettings_add_link_mode(ks, advertising, | |
| 184 | + Asym_Pause); | |
| 185 | +} | |
| 186 | + | |
| 187 | +static unsigned int fun_port_type(unsigned int xcvr) | |
| 188 | +{ | |
| 189 | + if (!xcvr) | |
| 190 | + return PORT_NONE; | |
| 191 | + | |
| 192 | + switch (xcvr & 7) { | |
| 193 | + case FUN_XCVR_BASET: | |
| 194 | + return PORT_TP; | |
| 195 | + case FUN_XCVR_CU: | |
| 196 | + return PORT_DA; | |
| 197 | + default: | |
| 198 | + return PORT_FIBRE; | |
| 199 | + } | |
| 200 | +} | |
| 201 | + | |
| 202 | +static int fun_get_link_ksettings(struct net_device *netdev, | |
| 203 | + struct ethtool_link_ksettings *ks) | |
| 204 | +{ | |
| 205 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 206 | + unsigned int seq, speed, xcvr; | |
| 207 | + u64 lp_advertising; | |
| 208 | + bool link_up; | |
| 209 | + | |
| 210 | + ethtool_link_ksettings_zero_link_mode(ks, supported); | |
| 211 | + ethtool_link_ksettings_zero_link_mode(ks, advertising); | |
| 212 | + ethtool_link_ksettings_zero_link_mode(ks, lp_advertising); | |
| 213 | + | |
| 214 | + /* Link settings change asynchronously, take a consistent snapshot */ | |
| 215 | + do { | |
| 216 | + seq = read_seqcount_begin(&fp->link_seq); | |
| 217 | + link_up = netif_carrier_ok(netdev); | |
| 218 | + speed = fp->link_speed; | |
| 219 | + xcvr = fp->xcvr_type; | |
| 220 | + lp_advertising = fp->lp_advertising; | |
| 221 | + } while (read_seqcount_retry(&fp->link_seq, seq)); | |
| 222 | + | |
| 223 | + if (link_up) { | |
| 224 | + ks->base.speed = speed; | |
| 225 | + ks->base.duplex = DUPLEX_FULL; | |
| 226 | + fun_link_modes_to_ethtool(lp_advertising, | |
| 227 | + ks->link_modes.lp_advertising); | |
| 228 | + } else { | |
| 229 | + ks->base.speed = SPEED_UNKNOWN; | |
| 230 | + ks->base.duplex = DUPLEX_UNKNOWN; | |
| 231 | + } | |
| 232 | + | |
| 233 | + ks->base.autoneg = (fp->advertising & FUN_PORT_CAP_AUTONEG) ? | |
| 234 | + AUTONEG_ENABLE : AUTONEG_DISABLE; | |
| 235 | + ks->base.port = fun_port_type(xcvr); | |
| 236 | + | |
| 237 | + fun_link_modes_to_ethtool(fp->port_caps, ks->link_modes.supported); | |
| 238 | + if (fp->port_caps & (FUN_PORT_CAP_RX_PAUSE | FUN_PORT_CAP_TX_PAUSE)) | |
| 239 | + ethtool_link_ksettings_add_link_mode(ks, supported, Asym_Pause); | |
| 240 | + | |
| 241 | + fun_link_modes_to_ethtool(fp->advertising, ks->link_modes.advertising); | |
| 242 | + set_asym_pause(fp->advertising, ks); | |
| 243 | + return 0; | |
| 244 | +} | |
| 245 | + | |
| 246 | +static u64 fun_advert_modes(const struct ethtool_link_ksettings *ks) | |
| 247 | +{ | |
| 248 | + u64 modes = 0; | |
| 249 | + | |
| 250 | +#define HAS_MODE(mode) \ | |
| 251 | + ethtool_link_ksettings_test_link_mode(ks, advertising, mode) | |
| 252 | + | |
| 253 | + if (HAS_MODE(1000baseX_Full)) | |
| 254 | + modes |= FUN_PORT_CAP_1000_X; | |
| 255 | + if (HAS_MODE(10000baseCR_Full) || HAS_MODE(10000baseSR_Full) || | |
| 256 | + HAS_MODE(10000baseLR_Full) || HAS_MODE(10000baseER_Full)) | |
| 257 | + modes |= FUN_PORT_CAP_10G_R; | |
| 258 | + if (HAS_MODE(25000baseCR_Full) || HAS_MODE(25000baseSR_Full)) | |
| 259 | + modes |= FUN_PORT_CAP_25G_R; | |
| 260 | + if (HAS_MODE(40000baseCR4_Full) || HAS_MODE(40000baseSR4_Full) || | |
| 261 | + HAS_MODE(40000baseLR4_Full)) | |
| 262 | + modes |= FUN_PORT_CAP_40G_R4; | |
| 263 | + if (HAS_MODE(50000baseCR2_Full) || HAS_MODE(50000baseSR2_Full)) | |
| 264 | + modes |= FUN_PORT_CAP_50G_R2; | |
| 265 | + if (HAS_MODE(50000baseCR_Full) || HAS_MODE(50000baseSR_Full) || | |
| 266 | + HAS_MODE(50000baseLR_ER_FR_Full)) | |
| 267 | + modes |= FUN_PORT_CAP_50G_R; | |
| 268 | + if (HAS_MODE(100000baseCR4_Full) || HAS_MODE(100000baseSR4_Full) || | |
| 269 | + HAS_MODE(100000baseLR4_ER4_Full)) | |
| 270 | + modes |= FUN_PORT_CAP_100G_R4; | |
| 271 | + if (HAS_MODE(100000baseCR2_Full) || HAS_MODE(100000baseSR2_Full) || | |
| 272 | + HAS_MODE(100000baseLR2_ER2_FR2_Full)) | |
| 273 | + modes |= FUN_PORT_CAP_100G_R2; | |
| 274 | + | |
| 275 | + return modes; | |
| 276 | +#undef HAS_MODE | |
| 277 | +} | |
| 278 | + | |
| 279 | +static u64 fun_speed_to_link_mode(unsigned int speed) | |
| 280 | +{ | |
| 281 | + switch (speed) { | |
| 282 | + case SPEED_100000: | |
| 283 | + return FUN_PORT_CAP_100G_R4 | FUN_PORT_CAP_100G_R2; | |
| 284 | + case SPEED_50000: | |
| 285 | + return FUN_PORT_CAP_50G_R | FUN_PORT_CAP_50G_R2; | |
| 286 | + case SPEED_40000: | |
| 287 | + return FUN_PORT_CAP_40G_R4; | |
| 288 | + case SPEED_25000: | |
| 289 | + return FUN_PORT_CAP_25G_R; | |
| 290 | + case SPEED_10000: | |
| 291 | + return FUN_PORT_CAP_10G_R; | |
| 292 | + case SPEED_1000: | |
| 293 | + return FUN_PORT_CAP_1000_X; | |
| 294 | + default: | |
| 295 | + return 0; | |
| 296 | + } | |
| 297 | +} | |
| 298 | + | |
| 299 | +static int fun_change_advert(struct funeth_priv *fp, u64 new_advert) | |
| 300 | +{ | |
| 301 | + int err; | |
| 302 | + | |
| 303 | + if (new_advert == fp->advertising) | |
| 304 | + return 0; | |
| 305 | + | |
| 306 | + err = fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_ADVERT, new_advert); | |
| 307 | + if (!err) | |
| 308 | + fp->advertising = new_advert; | |
| 309 | + return err; | |
| 310 | +} | |
| 311 | + | |
| 312 | +#define FUN_PORT_CAP_FEC_MASK \ | |
| 313 | + (FUN_PORT_CAP_FEC_NONE | FUN_PORT_CAP_FEC_FC | FUN_PORT_CAP_FEC_RS) | |
| 314 | + | |
| 315 | +static int fun_set_link_ksettings(struct net_device *netdev, | |
| 316 | + const struct ethtool_link_ksettings *ks) | |
| 317 | +{ | |
| 318 | + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = {}; | |
| 319 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 320 | + u64 new_advert; | |
| 321 | + | |
| 322 | + /* eswitch ports don't support mode changes */ | |
| 323 | + if (fp->port_caps & FUN_PORT_CAP_VPORT) | |
| 324 | + return -EOPNOTSUPP; | |
| 325 | + | |
| 326 | + if (ks->base.duplex == DUPLEX_HALF) | |
| 327 | + return -EINVAL; | |
| 328 | + if (ks->base.autoneg == AUTONEG_ENABLE && | |
| 329 | + !(fp->port_caps & FUN_PORT_CAP_AUTONEG)) | |
| 330 | + return -EINVAL; | |
| 331 | + | |
| 332 | + if (ks->base.autoneg == AUTONEG_ENABLE) { | |
| 333 | + if (linkmode_empty(ks->link_modes.advertising)) | |
| 334 | + return -EINVAL; | |
| 335 | + | |
| 336 | + fun_link_modes_to_ethtool(fp->port_caps, supported); | |
| 337 | + if (!linkmode_subset(ks->link_modes.advertising, supported)) | |
| 338 | + return -EINVAL; | |
| 339 | + | |
| 340 | + new_advert = fun_advert_modes(ks) | FUN_PORT_CAP_AUTONEG; | |
| 341 | + } else { | |
| 342 | + new_advert = fun_speed_to_link_mode(ks->base.speed); | |
| 343 | + new_advert &= fp->port_caps; | |
| 344 | + if (!new_advert) | |
| 345 | + return -EINVAL; | |
| 346 | + } | |
| 347 | + new_advert |= fp->advertising & | |
| 348 | + (FUN_PORT_CAP_PAUSE_MASK | FUN_PORT_CAP_FEC_MASK); | |
| 349 | + | |
| 350 | + return fun_change_advert(fp, new_advert); | |
| 351 | +} | |
| 352 | + | |
| 353 | +static void fun_get_pauseparam(struct net_device *netdev, | |
| 354 | + struct ethtool_pauseparam *pause) | |
| 355 | +{ | |
| 356 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 357 | + u8 active_pause = fp->active_fc; | |
| 358 | + | |
| 359 | + pause->rx_pause = !!(active_pause & FUN_PORT_CAP_RX_PAUSE); | |
| 360 | + pause->tx_pause = !!(active_pause & FUN_PORT_CAP_TX_PAUSE); | |
| 361 | + pause->autoneg = !!(fp->advertising & FUN_PORT_CAP_AUTONEG); | |
| 362 | +} | |
| 363 | + | |
| 364 | +static int fun_set_pauseparam(struct net_device *netdev, | |
| 365 | + struct ethtool_pauseparam *pause) | |
| 366 | +{ | |
| 367 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 368 | + u64 new_advert; | |
| 369 | + | |
| 370 | + if (fp->port_caps & FUN_PORT_CAP_VPORT) | |
| 371 | + return -EOPNOTSUPP; | |
| 372 | + /* Forcing PAUSE settings with AN enabled is unsupported. */ | |
| 373 | + if (!pause->autoneg && (fp->advertising & FUN_PORT_CAP_AUTONEG)) | |
| 374 | + return -EOPNOTSUPP; | |
| 375 | + if (pause->autoneg && !(fp->advertising & FUN_PORT_CAP_AUTONEG)) | |
| 376 | + return -EINVAL; | |
| 377 | + if (pause->tx_pause && !(fp->port_caps & FUN_PORT_CAP_TX_PAUSE)) | |
| 378 | + return -EINVAL; | |
| 379 | + if (pause->rx_pause && !(fp->port_caps & FUN_PORT_CAP_RX_PAUSE)) | |
| 380 | + return -EINVAL; | |
| 381 | + | |
| 382 | + new_advert = fp->advertising & ~FUN_PORT_CAP_PAUSE_MASK; | |
| 383 | + if (pause->tx_pause) | |
| 384 | + new_advert |= FUN_PORT_CAP_TX_PAUSE; | |
| 385 | + if (pause->rx_pause) | |
| 386 | + new_advert |= FUN_PORT_CAP_RX_PAUSE; | |
| 387 | + | |
| 388 | + return fun_change_advert(fp, new_advert); | |
| 389 | +} | |
| 390 | + | |
| 391 | +static int fun_restart_an(struct net_device *netdev) | |
| 392 | +{ | |
| 393 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 394 | + | |
| 395 | + if (!(fp->advertising & FUN_PORT_CAP_AUTONEG)) | |
| 396 | + return -EOPNOTSUPP; | |
| 397 | + | |
| 398 | + return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_ADVERT, | |
| 399 | + FUN_PORT_CAP_AUTONEG); | |
| 400 | +} | |
| 401 | + | |
| 402 | +static int fun_set_phys_id(struct net_device *netdev, | |
| 403 | + enum ethtool_phys_id_state state) | |
| 404 | +{ | |
| 405 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 406 | + unsigned int beacon; | |
| 407 | + | |
| 408 | + if (fp->port_caps & FUN_PORT_CAP_VPORT) | |
| 409 | + return -EOPNOTSUPP; | |
| 410 | + if (state != ETHTOOL_ID_ACTIVE && state != ETHTOOL_ID_INACTIVE) | |
| 411 | + return -EOPNOTSUPP; | |
| 412 | + | |
| 413 | + beacon = state == ETHTOOL_ID_ACTIVE ? FUN_PORT_LED_BEACON_ON : | |
| 414 | + FUN_PORT_LED_BEACON_OFF; | |
| 415 | + return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_LED, beacon); | |
| 416 | +} | |
| 417 | + | |
| 418 | +static void fun_get_drvinfo(struct net_device *netdev, | |
| 419 | + struct ethtool_drvinfo *info) | |
| 420 | +{ | |
| 421 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 422 | + | |
| 423 | + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); | |
| 424 | + strscpy(info->bus_info, pci_name(fp->pdev), sizeof(info->bus_info)); | |
| 425 | +} | |
| 426 | + | |
| 427 | +static u32 fun_get_msglevel(struct net_device *netdev) | |
| 428 | +{ | |
| 429 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 430 | + | |
| 431 | + return fp->msg_enable; | |
| 432 | +} | |
| 433 | + | |
| 434 | +static void fun_set_msglevel(struct net_device *netdev, u32 value) | |
| 435 | +{ | |
| 436 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 437 | + | |
| 438 | + fp->msg_enable = value; | |
| 439 | +} | |
| 440 | + | |
| 441 | +static int fun_get_regs_len(struct net_device *dev) | |
| 442 | +{ | |
| 443 | + return NVME_REG_ACQ + sizeof(u64); | |
| 444 | +} | |
| 445 | + | |
| 446 | +static void fun_get_regs(struct net_device *dev, struct ethtool_regs *regs, | |
| 447 | + void *buf) | |
| 448 | +{ | |
| 449 | + const struct funeth_priv *fp = netdev_priv(dev); | |
| 450 | + void __iomem *bar = fp->fdev->bar; | |
| 451 | + | |
| 452 | + regs->version = 0; | |
| 453 | + *(u64 *)(buf + NVME_REG_CAP) = readq(bar + NVME_REG_CAP); | |
| 454 | + *(u32 *)(buf + NVME_REG_VS) = readl(bar + NVME_REG_VS); | |
| 455 | + *(u32 *)(buf + NVME_REG_INTMS) = readl(bar + NVME_REG_INTMS); | |
| 456 | + *(u32 *)(buf + NVME_REG_INTMC) = readl(bar + NVME_REG_INTMC); | |
| 457 | + *(u32 *)(buf + NVME_REG_CC) = readl(bar + NVME_REG_CC); | |
| 458 | + *(u32 *)(buf + NVME_REG_CSTS) = readl(bar + NVME_REG_CSTS); | |
| 459 | + *(u32 *)(buf + NVME_REG_AQA) = readl(bar + NVME_REG_AQA); | |
| 460 | + *(u64 *)(buf + NVME_REG_ASQ) = readq(bar + NVME_REG_ASQ); | |
| 461 | + *(u64 *)(buf + NVME_REG_ACQ) = readq(bar + NVME_REG_ACQ); | |
| 462 | +} | |
| 463 | + | |
| 464 | +static int fun_get_coalesce(struct net_device *netdev, | |
| 465 | + struct ethtool_coalesce *coal, | |
| 466 | + struct kernel_ethtool_coalesce *kcoal, | |
| 467 | + struct netlink_ext_ack *ext_ack) | |
| 468 | +{ | |
| 469 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 470 | + | |
| 471 | + coal->rx_coalesce_usecs = fp->rx_coal_usec; | |
| 472 | + coal->rx_max_coalesced_frames = fp->rx_coal_count; | |
| 473 | + coal->use_adaptive_rx_coalesce = !fp->cq_irq_db; | |
| 474 | + coal->tx_coalesce_usecs = fp->tx_coal_usec; | |
| 475 | + coal->tx_max_coalesced_frames = fp->tx_coal_count; | |
| 476 | + return 0; | |
| 477 | +} | |
| 478 | + | |
| 479 | +static int fun_set_coalesce(struct net_device *netdev, | |
| 480 | + struct ethtool_coalesce *coal, | |
| 481 | + struct kernel_ethtool_coalesce *kcoal, | |
| 482 | + struct netlink_ext_ack *ext_ack) | |
| 483 | +{ | |
| 484 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 485 | + struct funeth_rxq **rxqs; | |
| 486 | + unsigned int i, db_val; | |
| 487 | + | |
| 488 | + if (coal->rx_coalesce_usecs > FUN_DB_INTCOAL_USEC_M || | |
| 489 | + coal->rx_max_coalesced_frames > FUN_DB_INTCOAL_ENTRIES_M || | |
| 490 | + (coal->rx_coalesce_usecs | coal->rx_max_coalesced_frames) == 0 || | |
| 491 | + coal->tx_coalesce_usecs > FUN_DB_INTCOAL_USEC_M || | |
| 492 | + coal->tx_max_coalesced_frames > FUN_DB_INTCOAL_ENTRIES_M || | |
| 493 | + (coal->tx_coalesce_usecs | coal->tx_max_coalesced_frames) == 0) | |
| 494 | + return -EINVAL; | |
| 495 | + | |
| 496 | + /* a timer is required if there's any coalescing */ | |
| 497 | + if ((coal->rx_max_coalesced_frames > 1 && !coal->rx_coalesce_usecs) || | |
| 498 | + (coal->tx_max_coalesced_frames > 1 && !coal->tx_coalesce_usecs)) | |
| 499 | + return -EINVAL; | |
| 500 | + | |
| 501 | + fp->rx_coal_usec = coal->rx_coalesce_usecs; | |
| 502 | + fp->rx_coal_count = coal->rx_max_coalesced_frames; | |
| 503 | + fp->tx_coal_usec = coal->tx_coalesce_usecs; | |
| 504 | + fp->tx_coal_count = coal->tx_max_coalesced_frames; | |
| 505 | + | |
| 506 | + db_val = FUN_IRQ_CQ_DB(fp->rx_coal_usec, fp->rx_coal_count); | |
| 507 | + WRITE_ONCE(fp->cq_irq_db, db_val); | |
| 508 | + | |
| 509 | + rxqs = rtnl_dereference(fp->rxqs); | |
| 510 | + if (!rxqs) | |
| 511 | + return 0; | |
| 512 | + | |
| 513 | + for (i = 0; i < netdev->real_num_rx_queues; i++) | |
| 514 | + WRITE_ONCE(rxqs[i]->irq_db_val, db_val); | |
| 515 | + | |
| 516 | + db_val = FUN_IRQ_SQ_DB(fp->tx_coal_usec, fp->tx_coal_count); | |
| 517 | + for (i = 0; i < netdev->real_num_tx_queues; i++) | |
| 518 | + WRITE_ONCE(fp->txqs[i]->irq_db_val, db_val); | |
| 519 | + | |
| 520 | + return 0; | |
| 521 | +} | |
| 522 | + | |
| 523 | +static void fun_get_channels(struct net_device *netdev, | |
| 524 | + struct ethtool_channels *chan) | |
| 525 | +{ | |
| 526 | + chan->max_rx = netdev->num_rx_queues; | |
| 527 | + chan->rx_count = netdev->real_num_rx_queues; | |
| 528 | + | |
| 529 | + chan->max_tx = netdev->num_tx_queues; | |
| 530 | + chan->tx_count = netdev->real_num_tx_queues; | |
| 531 | +} | |
| 532 | + | |
| 533 | +static int fun_set_channels(struct net_device *netdev, | |
| 534 | + struct ethtool_channels *chan) | |
| 535 | +{ | |
| 536 | + if (!chan->tx_count || !chan->rx_count) | |
| 537 | + return -EINVAL; | |
| 538 | + | |
| 539 | + if (chan->tx_count == netdev->real_num_tx_queues && | |
| 540 | + chan->rx_count == netdev->real_num_rx_queues) | |
| 541 | + return 0; | |
| 542 | + | |
| 543 | + if (netif_running(netdev)) | |
| 544 | + return fun_change_num_queues(netdev, chan->tx_count, | |
| 545 | + chan->rx_count); | |
| 546 | + | |
| 547 | + fun_set_ring_count(netdev, chan->tx_count, chan->rx_count); | |
| 548 | + return 0; | |
| 549 | +} | |
| 550 | + | |
| 551 | +static void fun_get_ringparam(struct net_device *netdev, | |
| 552 | + struct ethtool_ringparam *ring, | |
| 553 | + struct kernel_ethtool_ringparam *kring, | |
| 554 | + struct netlink_ext_ack *extack) | |
| 555 | +{ | |
| 556 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 557 | + unsigned int max_depth = fp->fdev->q_depth; | |
| 558 | + | |
| 559 | + /* We size CQs to be twice the RQ depth so max RQ depth is half the | |
| 560 | + * max queue depth. | |
| 561 | + */ | |
| 562 | + ring->rx_max_pending = max_depth / 2; | |
| 563 | + ring->tx_max_pending = max_depth; | |
| 564 | + | |
| 565 | + ring->rx_pending = fp->rq_depth; | |
| 566 | + ring->tx_pending = fp->sq_depth; | |
| 567 | + | |
| 568 | + kring->rx_buf_len = PAGE_SIZE; | |
| 569 | + kring->cqe_size = FUNETH_CQE_SIZE; | |
| 570 | +} | |
| 571 | + | |
| 572 | +static int fun_set_ringparam(struct net_device *netdev, | |
| 573 | + struct ethtool_ringparam *ring, | |
| 574 | + struct kernel_ethtool_ringparam *kring, | |
| 575 | + struct netlink_ext_ack *extack) | |
| 576 | +{ | |
| 577 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 578 | + int rc; | |
| 579 | + | |
| 580 | + if (ring->rx_mini_pending || ring->rx_jumbo_pending) | |
| 581 | + return -EINVAL; | |
| 582 | + | |
| 583 | + /* queue depths must be powers-of-2 */ | |
| 584 | + if (!is_power_of_2(ring->rx_pending) || | |
| 585 | + !is_power_of_2(ring->tx_pending)) | |
| 586 | + return -EINVAL; | |
| 587 | + | |
| 588 | + if (ring->rx_pending < FUNETH_MIN_QDEPTH || | |
| 589 | + ring->tx_pending < FUNETH_MIN_QDEPTH) | |
| 590 | + return -EINVAL; | |
| 591 | + | |
| 592 | + if (fp->sq_depth == ring->tx_pending && | |
| 593 | + fp->rq_depth == ring->rx_pending) | |
| 594 | + return 0; | |
| 595 | + | |
| 596 | + if (netif_running(netdev)) { | |
| 597 | + struct fun_qset req = { | |
| 598 | + .cq_depth = 2 * ring->rx_pending, | |
| 599 | + .rq_depth = ring->rx_pending, | |
| 600 | + .sq_depth = ring->tx_pending | |
| 601 | + }; | |
| 602 | + | |
| 603 | + rc = fun_replace_queues(netdev, &req, extack); | |
| 604 | + if (rc) | |
| 605 | + return rc; | |
| 606 | + } | |
| 607 | + | |
| 608 | + fp->sq_depth = ring->tx_pending; | |
| 609 | + fp->rq_depth = ring->rx_pending; | |
| 610 | + fp->cq_depth = 2 * fp->rq_depth; | |
| 611 | + return 0; | |
| 612 | +} | |
| 613 | + | |
| 614 | +static int fun_get_sset_count(struct net_device *dev, int sset) | |
| 615 | +{ | |
| 616 | + const struct funeth_priv *fp = netdev_priv(dev); | |
| 617 | + int n; | |
| 618 | + | |
| 619 | + switch (sset) { | |
| 620 | + case ETH_SS_STATS: | |
| 621 | + n = (dev->real_num_tx_queues + 1) * ARRAY_SIZE(txq_stat_names) + | |
| 622 | + (dev->real_num_rx_queues + 1) * ARRAY_SIZE(rxq_stat_names) + | |
| 623 | + (fp->num_xdpqs + 1) * ARRAY_SIZE(xdpq_stat_names) + | |
| 624 | + ARRAY_SIZE(tls_stat_names); | |
| 625 | + if (fp->port_caps & FUN_PORT_CAP_STATS) { | |
| 626 | + n += ARRAY_SIZE(mac_tx_stat_names) + | |
| 627 | + ARRAY_SIZE(mac_rx_stat_names); | |
| 628 | + } | |
| 629 | + return n; | |
| 630 | + default: | |
| 631 | + break; | |
| 632 | + } | |
| 633 | + return 0; | |
| 634 | +} | |
| 635 | + | |
| 636 | +static void fun_get_strings(struct net_device *netdev, u32 sset, u8 *data) | |
| 637 | +{ | |
| 638 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 639 | + unsigned int i, j; | |
| 640 | + u8 *p = data; | |
| 641 | + | |
| 642 | + switch (sset) { | |
| 643 | + case ETH_SS_STATS: | |
| 644 | + if (fp->port_caps & FUN_PORT_CAP_STATS) { | |
| 645 | + memcpy(p, mac_tx_stat_names, sizeof(mac_tx_stat_names)); | |
| 646 | + p += sizeof(mac_tx_stat_names); | |
| 647 | + memcpy(p, mac_rx_stat_names, sizeof(mac_rx_stat_names)); | |
| 648 | + p += sizeof(mac_rx_stat_names); | |
| 649 | + } | |
| 650 | + | |
| 651 | + for (i = 0; i < netdev->real_num_tx_queues; i++) { | |
| 652 | + for (j = 0; j < ARRAY_SIZE(txq_stat_names); j++) | |
| 653 | + ethtool_sprintf(&p, "%s[%u]", txq_stat_names[j], | |
| 654 | + i); | |
| 655 | + } | |
| 656 | + for (j = 0; j < ARRAY_SIZE(txq_stat_names); j++) | |
| 657 | + ethtool_sprintf(&p, txq_stat_names[j]); | |
| 658 | + | |
| 659 | + for (i = 0; i < fp->num_xdpqs; i++) { | |
| 660 | + for (j = 0; j < ARRAY_SIZE(xdpq_stat_names); j++) | |
| 661 | + ethtool_sprintf(&p, "%s[%u]", | |
| 662 | + xdpq_stat_names[j], i); | |
| 663 | + } | |
| 664 | + for (j = 0; j < ARRAY_SIZE(xdpq_stat_names); j++) | |
| 665 | + ethtool_sprintf(&p, xdpq_stat_names[j]); | |
| 666 | + | |
| 667 | + for (i = 0; i < netdev->real_num_rx_queues; i++) { | |
| 668 | + for (j = 0; j < ARRAY_SIZE(rxq_stat_names); j++) | |
| 669 | + ethtool_sprintf(&p, "%s[%u]", rxq_stat_names[j], | |
| 670 | + i); | |
| 671 | + } | |
| 672 | + for (j = 0; j < ARRAY_SIZE(rxq_stat_names); j++) | |
| 673 | + ethtool_sprintf(&p, rxq_stat_names[j]); | |
| 674 | + | |
| 675 | + for (j = 0; j < ARRAY_SIZE(tls_stat_names); j++) | |
| 676 | + ethtool_sprintf(&p, tls_stat_names[j]); | |
| 677 | + break; | |
| 678 | + default: | |
| 679 | + break; | |
| 680 | + } | |
| 681 | +} | |
| 682 | + | |
| 683 | +static u64 *get_mac_stats(const struct funeth_priv *fp, u64 *data) | |
| 684 | +{ | |
| 685 | +#define TX_STAT(s) \ | |
| 686 | + *data++ = be64_to_cpu(fp->stats[PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_##s]) | |
| 687 | + | |
| 688 | + TX_STAT(etherStatsOctets); | |
| 689 | + TX_STAT(etherStatsPkts); | |
| 690 | + TX_STAT(VLANTransmittedOK); | |
| 691 | + TX_STAT(ifOutUcastPkts); | |
| 692 | + TX_STAT(ifOutMulticastPkts); | |
| 693 | + TX_STAT(ifOutBroadcastPkts); | |
| 694 | + TX_STAT(ifOutErrors); | |
| 695 | + TX_STAT(CBFCPAUSEFramesTransmitted_0); | |
| 696 | + TX_STAT(CBFCPAUSEFramesTransmitted_1); | |
| 697 | + TX_STAT(CBFCPAUSEFramesTransmitted_2); | |
| 698 | + TX_STAT(CBFCPAUSEFramesTransmitted_3); | |
| 699 | + TX_STAT(CBFCPAUSEFramesTransmitted_4); | |
| 700 | + TX_STAT(CBFCPAUSEFramesTransmitted_5); | |
| 701 | + TX_STAT(CBFCPAUSEFramesTransmitted_6); | |
| 702 | + TX_STAT(CBFCPAUSEFramesTransmitted_7); | |
| 703 | + TX_STAT(CBFCPAUSEFramesTransmitted_8); | |
| 704 | + TX_STAT(CBFCPAUSEFramesTransmitted_9); | |
| 705 | + TX_STAT(CBFCPAUSEFramesTransmitted_10); | |
| 706 | + TX_STAT(CBFCPAUSEFramesTransmitted_11); | |
| 707 | + TX_STAT(CBFCPAUSEFramesTransmitted_12); | |
| 708 | + TX_STAT(CBFCPAUSEFramesTransmitted_13); | |
| 709 | + TX_STAT(CBFCPAUSEFramesTransmitted_14); | |
| 710 | + TX_STAT(CBFCPAUSEFramesTransmitted_15); | |
| 711 | + | |
| 712 | +#define RX_STAT(s) *data++ = be64_to_cpu(fp->stats[PORT_MAC_RX_##s]) | |
| 713 | + | |
| 714 | + RX_STAT(etherStatsOctets); | |
| 715 | + RX_STAT(etherStatsPkts); | |
| 716 | + RX_STAT(VLANReceivedOK); | |
| 717 | + RX_STAT(ifInUcastPkts); | |
| 718 | + RX_STAT(ifInMulticastPkts); | |
| 719 | + RX_STAT(ifInBroadcastPkts); | |
| 720 | + RX_STAT(etherStatsDropEvents); | |
| 721 | + RX_STAT(ifInErrors); | |
| 722 | + RX_STAT(aAlignmentErrors); | |
| 723 | + RX_STAT(CBFCPAUSEFramesReceived_0); | |
| 724 | + RX_STAT(CBFCPAUSEFramesReceived_1); | |
| 725 | + RX_STAT(CBFCPAUSEFramesReceived_2); | |
| 726 | + RX_STAT(CBFCPAUSEFramesReceived_3); | |
| 727 | + RX_STAT(CBFCPAUSEFramesReceived_4); | |
| 728 | + RX_STAT(CBFCPAUSEFramesReceived_5); | |
| 729 | + RX_STAT(CBFCPAUSEFramesReceived_6); | |
| 730 | + RX_STAT(CBFCPAUSEFramesReceived_7); | |
| 731 | + RX_STAT(CBFCPAUSEFramesReceived_8); | |
| 732 | + RX_STAT(CBFCPAUSEFramesReceived_9); | |
| 733 | + RX_STAT(CBFCPAUSEFramesReceived_10); | |
| 734 | + RX_STAT(CBFCPAUSEFramesReceived_11); | |
| 735 | + RX_STAT(CBFCPAUSEFramesReceived_12); | |
| 736 | + RX_STAT(CBFCPAUSEFramesReceived_13); | |
| 737 | + RX_STAT(CBFCPAUSEFramesReceived_14); | |
| 738 | + RX_STAT(CBFCPAUSEFramesReceived_15); | |
| 739 | + | |
| 740 | + return data; | |
| 741 | + | |
| 742 | +#undef TX_STAT | |
| 743 | +#undef RX_STAT | |
| 744 | +} | |
| 745 | + | |
| 746 | +static void fun_get_ethtool_stats(struct net_device *netdev, | |
| 747 | + struct ethtool_stats *stats, u64 *data) | |
| 748 | +{ | |
| 749 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 750 | + struct funeth_txq_stats txs; | |
| 751 | + struct funeth_rxq_stats rxs; | |
| 752 | + struct funeth_txq **xdpqs; | |
| 753 | + struct funeth_rxq **rxqs; | |
| 754 | + unsigned int i, start; | |
| 755 | + u64 *totals, *tot; | |
| 756 | + | |
| 757 | + if (fp->port_caps & FUN_PORT_CAP_STATS) | |
| 758 | + data = get_mac_stats(fp, data); | |
| 759 | + | |
| 760 | + rxqs = rtnl_dereference(fp->rxqs); | |
| 761 | + if (!rxqs) | |
| 762 | + return; | |
| 763 | + | |
| 764 | +#define ADD_STAT(cnt) do { \ | |
| 765 | + *data = (cnt); *tot++ += *data++; \ | |
| 766 | +} while (0) | |
| 767 | + | |
| 768 | + /* Tx queues */ | |
| 769 | + totals = data + netdev->real_num_tx_queues * ARRAY_SIZE(txq_stat_names); | |
| 770 | + | |
| 771 | + for (i = 0; i < netdev->real_num_tx_queues; i++) { | |
| 772 | + tot = totals; | |
| 773 | + | |
| 774 | + FUN_QSTAT_READ(fp->txqs[i], start, txs); | |
| 775 | + | |
| 776 | + ADD_STAT(txs.tx_pkts); | |
| 777 | + ADD_STAT(txs.tx_bytes); | |
| 778 | + ADD_STAT(txs.tx_cso); | |
| 779 | + ADD_STAT(txs.tx_tso); | |
| 780 | + ADD_STAT(txs.tx_encap_tso); | |
| 781 | + ADD_STAT(txs.tx_more); | |
| 782 | + ADD_STAT(txs.tx_nstops); | |
| 783 | + ADD_STAT(txs.tx_nrestarts); | |
| 784 | + ADD_STAT(txs.tx_map_err); | |
| 785 | + ADD_STAT(txs.tx_tls_pkts); | |
| 786 | + ADD_STAT(txs.tx_tls_bytes); | |
| 787 | + ADD_STAT(txs.tx_tls_fallback); | |
| 788 | + ADD_STAT(txs.tx_tls_drops); | |
| 789 | + } | |
| 790 | + data += ARRAY_SIZE(txq_stat_names); | |
| 791 | + | |
| 792 | + /* XDP Tx queues */ | |
| 793 | + xdpqs = rtnl_dereference(fp->xdpqs); | |
| 794 | + totals = data + fp->num_xdpqs * ARRAY_SIZE(xdpq_stat_names); | |
| 795 | + | |
| 796 | + for (i = 0; i < fp->num_xdpqs; i++) { | |
| 797 | + tot = totals; | |
| 798 | + | |
| 799 | + FUN_QSTAT_READ(xdpqs[i], start, txs); | |
| 800 | + | |
| 801 | + ADD_STAT(txs.tx_pkts); | |
| 802 | + ADD_STAT(txs.tx_bytes); | |
| 803 | + ADD_STAT(txs.tx_xdp_full); | |
| 804 | + ADD_STAT(txs.tx_map_err); | |
| 805 | + } | |
| 806 | + data += ARRAY_SIZE(xdpq_stat_names); | |
| 807 | + | |
| 808 | + /* Rx queues */ | |
| 809 | + totals = data + netdev->real_num_rx_queues * ARRAY_SIZE(rxq_stat_names); | |
| 810 | + | |
| 811 | + for (i = 0; i < netdev->real_num_rx_queues; i++) { | |
| 812 | + tot = totals; | |
| 813 | + | |
| 814 | + FUN_QSTAT_READ(rxqs[i], start, rxs); | |
| 815 | + | |
| 816 | + ADD_STAT(rxs.rx_pkts); | |
| 817 | + ADD_STAT(rxs.rx_bytes); | |
| 818 | + ADD_STAT(rxs.rx_cso); | |
| 819 | + ADD_STAT(rxs.gro_pkts); | |
| 820 | + ADD_STAT(rxs.gro_merged); | |
| 821 | + ADD_STAT(rxs.xdp_tx); | |
| 822 | + ADD_STAT(rxs.xdp_redir); | |
| 823 | + ADD_STAT(rxs.xdp_drops); | |
| 824 | + ADD_STAT(rxs.rx_bufs); | |
| 825 | + ADD_STAT(rxs.rx_page_alloc); | |
| 826 | + ADD_STAT(rxs.rx_mem_drops + rxs.xdp_err); | |
| 827 | + ADD_STAT(rxs.rx_budget); | |
| 828 | + ADD_STAT(rxs.rx_map_err); | |
| 829 | + } | |
| 830 | + data += ARRAY_SIZE(rxq_stat_names); | |
| 831 | +#undef ADD_STAT | |
| 832 | + | |
| 833 | + *data++ = atomic64_read(&fp->tx_tls_add); | |
| 834 | + *data++ = atomic64_read(&fp->tx_tls_del); | |
| 835 | + *data++ = atomic64_read(&fp->tx_tls_resync); | |
| 836 | +} | |
| 837 | + | |
| 838 | +#define RX_STAT(fp, s) be64_to_cpu((fp)->stats[PORT_MAC_RX_##s]) | |
| 839 | +#define TX_STAT(fp, s) \ | |
| 840 | + be64_to_cpu((fp)->stats[PORT_MAC_RX_STATS_MAX + PORT_MAC_TX_##s]) | |
| 841 | +#define FEC_STAT(fp, s) \ | |
| 842 | + be64_to_cpu((fp)->stats[PORT_MAC_RX_STATS_MAX + \ | |
| 843 | + PORT_MAC_TX_STATS_MAX + PORT_MAC_FEC_##s]) | |
| 844 | + | |
| 845 | +static void fun_get_pause_stats(struct net_device *netdev, | |
| 846 | + struct ethtool_pause_stats *stats) | |
| 847 | +{ | |
| 848 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 849 | + | |
| 850 | + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) | |
| 851 | + return; | |
| 852 | + | |
| 853 | + stats->tx_pause_frames = TX_STAT(fp, aPAUSEMACCtrlFramesTransmitted); | |
| 854 | + stats->rx_pause_frames = RX_STAT(fp, aPAUSEMACCtrlFramesReceived); | |
| 855 | +} | |
| 856 | + | |
| 857 | +static void fun_get_802_3_stats(struct net_device *netdev, | |
| 858 | + struct ethtool_eth_mac_stats *stats) | |
| 859 | +{ | |
| 860 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 861 | + | |
| 862 | + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) | |
| 863 | + return; | |
| 864 | + | |
| 865 | + stats->FramesTransmittedOK = TX_STAT(fp, aFramesTransmittedOK); | |
| 866 | + stats->FramesReceivedOK = RX_STAT(fp, aFramesReceivedOK); | |
| 867 | + stats->FrameCheckSequenceErrors = RX_STAT(fp, aFrameCheckSequenceErrors); | |
| 868 | + stats->OctetsTransmittedOK = TX_STAT(fp, OctetsTransmittedOK); | |
| 869 | + stats->OctetsReceivedOK = RX_STAT(fp, OctetsReceivedOK); | |
| 870 | + stats->InRangeLengthErrors = RX_STAT(fp, aInRangeLengthErrors); | |
| 871 | + stats->FrameTooLongErrors = RX_STAT(fp, aFrameTooLongErrors); | |
| 872 | +} | |
| 873 | + | |
| 874 | +static void fun_get_802_3_ctrl_stats(struct net_device *netdev, | |
| 875 | + struct ethtool_eth_ctrl_stats *stats) | |
| 876 | +{ | |
| 877 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 878 | + | |
| 879 | + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) | |
| 880 | + return; | |
| 881 | + | |
| 882 | + stats->MACControlFramesTransmitted = TX_STAT(fp, MACControlFramesTransmitted); | |
| 883 | + stats->MACControlFramesReceived = RX_STAT(fp, MACControlFramesReceived); | |
| 884 | +} | |
| 885 | + | |
| 886 | +static void fun_get_rmon_stats(struct net_device *netdev, | |
| 887 | + struct ethtool_rmon_stats *stats, | |
| 888 | + const struct ethtool_rmon_hist_range **ranges) | |
| 889 | +{ | |
| 890 | + static const struct ethtool_rmon_hist_range rmon_ranges[] = { | |
| 891 | + { 64, 64 }, | |
| 892 | + { 65, 127 }, | |
| 893 | + { 128, 255 }, | |
| 894 | + { 256, 511 }, | |
| 895 | + { 512, 1023 }, | |
| 896 | + { 1024, 1518 }, | |
| 897 | + { 1519, 32767 }, | |
| 898 | + {} | |
| 899 | + }; | |
| 900 | + | |
| 901 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 902 | + | |
| 903 | + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) | |
| 904 | + return; | |
| 905 | + | |
| 906 | + stats->undersize_pkts = RX_STAT(fp, etherStatsUndersizePkts); | |
| 907 | + stats->oversize_pkts = RX_STAT(fp, etherStatsOversizePkts); | |
| 908 | + stats->fragments = RX_STAT(fp, etherStatsFragments); | |
| 909 | + stats->jabbers = RX_STAT(fp, etherStatsJabbers); | |
| 910 | + | |
| 911 | + stats->hist[0] = RX_STAT(fp, etherStatsPkts64Octets); | |
| 912 | + stats->hist[1] = RX_STAT(fp, etherStatsPkts65to127Octets); | |
| 913 | + stats->hist[2] = RX_STAT(fp, etherStatsPkts128to255Octets); | |
| 914 | + stats->hist[3] = RX_STAT(fp, etherStatsPkts256to511Octets); | |
| 915 | + stats->hist[4] = RX_STAT(fp, etherStatsPkts512to1023Octets); | |
| 916 | + stats->hist[5] = RX_STAT(fp, etherStatsPkts1024to1518Octets); | |
| 917 | + stats->hist[6] = RX_STAT(fp, etherStatsPkts1519toMaxOctets); | |
| 918 | + | |
| 919 | + stats->hist_tx[0] = TX_STAT(fp, etherStatsPkts64Octets); | |
| 920 | + stats->hist_tx[1] = TX_STAT(fp, etherStatsPkts65to127Octets); | |
| 921 | + stats->hist_tx[2] = TX_STAT(fp, etherStatsPkts128to255Octets); | |
| 922 | + stats->hist_tx[3] = TX_STAT(fp, etherStatsPkts256to511Octets); | |
| 923 | + stats->hist_tx[4] = TX_STAT(fp, etherStatsPkts512to1023Octets); | |
| 924 | + stats->hist_tx[5] = TX_STAT(fp, etherStatsPkts1024to1518Octets); | |
| 925 | + stats->hist_tx[6] = TX_STAT(fp, etherStatsPkts1519toMaxOctets); | |
| 926 | + | |
| 927 | + *ranges = rmon_ranges; | |
| 928 | +} | |
| 929 | + | |
| 930 | +static void fun_get_fec_stats(struct net_device *netdev, | |
| 931 | + struct ethtool_fec_stats *stats) | |
| 932 | +{ | |
| 933 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 934 | + | |
| 935 | + if (!(fp->port_caps & FUN_PORT_CAP_STATS)) | |
| 936 | + return; | |
| 937 | + | |
| 938 | + stats->corrected_blocks.total = FEC_STAT(fp, Correctable); | |
| 939 | + stats->uncorrectable_blocks.total = FEC_STAT(fp, Uncorrectable); | |
| 940 | +} | |
| 941 | + | |
| 942 | +#undef RX_STAT | |
| 943 | +#undef TX_STAT | |
| 944 | +#undef FEC_STAT | |
| 945 | + | |
| 946 | +static int fun_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, | |
| 947 | + u32 *rule_locs) | |
| 948 | +{ | |
| 949 | + switch (cmd->cmd) { | |
| 950 | + case ETHTOOL_GRXRINGS: | |
| 951 | + cmd->data = netdev->real_num_rx_queues; | |
| 952 | + return 0; | |
| 953 | + default: | |
| 954 | + break; | |
| 955 | + } | |
| 956 | + return -EOPNOTSUPP; | |
| 957 | +} | |
| 958 | + | |
| 959 | +static int fun_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info) | |
| 960 | +{ | |
| 961 | + return 0; | |
| 962 | +} | |
| 963 | + | |
| 964 | +static u32 fun_get_rxfh_indir_size(struct net_device *netdev) | |
| 965 | +{ | |
| 966 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 967 | + | |
| 968 | + return fp->indir_table_nentries; | |
| 969 | +} | |
| 970 | + | |
| 971 | +static u32 fun_get_rxfh_key_size(struct net_device *netdev) | |
| 972 | +{ | |
| 973 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 974 | + | |
| 975 | + return sizeof(fp->rss_key); | |
| 976 | +} | |
| 977 | + | |
| 978 | +static int fun_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, | |
| 979 | + u8 *hfunc) | |
| 980 | +{ | |
| 981 | + const struct funeth_priv *fp = netdev_priv(netdev); | |
| 982 | + | |
| 983 | + if (!fp->rss_cfg) | |
| 984 | + return -EOPNOTSUPP; | |
| 985 | + | |
| 986 | + if (indir) | |
| 987 | + memcpy(indir, fp->indir_table, | |
| 988 | + sizeof(u32) * fp->indir_table_nentries); | |
| 989 | + | |
| 990 | + if (key) | |
| 991 | + memcpy(key, fp->rss_key, sizeof(fp->rss_key)); | |
| 992 | + | |
| 993 | + if (hfunc) | |
| 994 | + *hfunc = fp->hash_algo == FUN_ETH_RSS_ALG_TOEPLITZ ? | |
| 995 | + ETH_RSS_HASH_TOP : ETH_RSS_HASH_CRC32; | |
| 996 | + | |
| 997 | + return 0; | |
| 998 | +} | |
| 999 | + | |
| 1000 | +static int fun_set_rxfh(struct net_device *netdev, const u32 *indir, | |
| 1001 | + const u8 *key, const u8 hfunc) | |
| 1002 | +{ | |
| 1003 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 1004 | + const u32 *rss_indir = indir ? indir : fp->indir_table; | |
| 1005 | + const u8 *rss_key = key ? key : fp->rss_key; | |
| 1006 | + enum fun_eth_hash_alg algo; | |
| 1007 | + | |
| 1008 | + if (!fp->rss_cfg) | |
| 1009 | + return -EOPNOTSUPP; | |
| 1010 | + | |
| 1011 | + if (hfunc == ETH_RSS_HASH_NO_CHANGE) | |
| 1012 | + algo = fp->hash_algo; | |
| 1013 | + else if (hfunc == ETH_RSS_HASH_CRC32) | |
| 1014 | + algo = FUN_ETH_RSS_ALG_CRC32; | |
| 1015 | + else if (hfunc == ETH_RSS_HASH_TOP) | |
| 1016 | + algo = FUN_ETH_RSS_ALG_TOEPLITZ; | |
| 1017 | + else | |
| 1018 | + return -EINVAL; | |
| 1019 | + | |
| 1020 | + /* If the port is enabled try to reconfigure RSS and keep the new | |
| 1021 | + * settings if successful. If it is down we update the RSS settings | |
| 1022 | + * and apply them at the next UP time. | |
| 1023 | + */ | |
| 1024 | + if (netif_running(netdev)) { | |
| 1025 | + int rc = fun_config_rss(netdev, algo, rss_key, rss_indir, | |
| 1026 | + FUN_ADMIN_SUBOP_MODIFY); | |
| 1027 | + if (rc) | |
| 1028 | + return rc; | |
| 1029 | + } | |
| 1030 | + | |
| 1031 | + fp->hash_algo = algo; | |
| 1032 | + if (key) | |
| 1033 | + memcpy(fp->rss_key, key, sizeof(fp->rss_key)); | |
| 1034 | + if (indir) | |
| 1035 | + memcpy(fp->indir_table, indir, | |
| 1036 | + sizeof(u32) * fp->indir_table_nentries); | |
| 1037 | + return 0; | |
| 1038 | +} | |
| 1039 | + | |
| 1040 | +static int fun_get_ts_info(struct net_device *netdev, | |
| 1041 | + struct ethtool_ts_info *info) | |
| 1042 | +{ | |
| 1043 | + info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | | |
| 1044 | + SOF_TIMESTAMPING_RX_HARDWARE | | |
| 1045 | + SOF_TIMESTAMPING_TX_SOFTWARE | | |
| 1046 | + SOF_TIMESTAMPING_SOFTWARE | | |
| 1047 | + SOF_TIMESTAMPING_RAW_HARDWARE; | |
| 1048 | + info->phc_index = -1; | |
| 1049 | + info->tx_types = BIT(HWTSTAMP_TX_OFF); | |
| 1050 | + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); | |
| 1051 | + return 0; | |
| 1052 | +} | |
| 1053 | + | |
| 1054 | +static unsigned int to_ethtool_fec(unsigned int fun_fec) | |
| 1055 | +{ | |
| 1056 | + unsigned int fec = 0; | |
| 1057 | + | |
| 1058 | + if (fun_fec == FUN_PORT_FEC_NA) | |
| 1059 | + fec |= ETHTOOL_FEC_NONE; | |
| 1060 | + if (fun_fec & FUN_PORT_FEC_OFF) | |
| 1061 | + fec |= ETHTOOL_FEC_OFF; | |
| 1062 | + if (fun_fec & FUN_PORT_FEC_RS) | |
| 1063 | + fec |= ETHTOOL_FEC_RS; | |
| 1064 | + if (fun_fec & FUN_PORT_FEC_FC) | |
| 1065 | + fec |= ETHTOOL_FEC_BASER; | |
| 1066 | + if (fun_fec & FUN_PORT_FEC_AUTO) | |
| 1067 | + fec |= ETHTOOL_FEC_AUTO; | |
| 1068 | + return fec; | |
| 1069 | +} | |
| 1070 | + | |
| 1071 | +static int fun_get_fecparam(struct net_device *netdev, | |
| 1072 | + struct ethtool_fecparam *fec) | |
| 1073 | +{ | |
| 1074 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 1075 | + u64 fec_data; | |
| 1076 | + int rc; | |
| 1077 | + | |
| 1078 | + rc = fun_port_read_cmd(fp, FUN_ADMIN_PORT_KEY_FEC, &fec_data); | |
| 1079 | + if (rc) | |
| 1080 | + return rc; | |
| 1081 | + | |
| 1082 | + fec->active_fec = to_ethtool_fec(fec_data & 0xff); | |
| 1083 | + fec->fec = to_ethtool_fec(fec_data >> 8); | |
| 1084 | + return 0; | |
| 1085 | +} | |
| 1086 | + | |
| 1087 | +static int fun_set_fecparam(struct net_device *netdev, | |
| 1088 | + struct ethtool_fecparam *fec) | |
| 1089 | +{ | |
| 1090 | + struct funeth_priv *fp = netdev_priv(netdev); | |
| 1091 | + u64 fec_mode; | |
| 1092 | + | |
| 1093 | + switch (fec->fec) { | |
| 1094 | + case ETHTOOL_FEC_AUTO: | |
| 1095 | + fec_mode = FUN_PORT_FEC_AUTO; | |
| 1096 | + break; | |
| 1097 | + case ETHTOOL_FEC_OFF: | |
| 1098 | + if (!(fp->port_caps & FUN_PORT_CAP_FEC_NONE)) | |
| 1099 | + return -EINVAL; | |
| 1100 | + fec_mode = FUN_PORT_FEC_OFF; | |
| 1101 | + break; | |
| 1102 | + case ETHTOOL_FEC_BASER: | |
| 1103 | + if (!(fp->port_caps & FUN_PORT_CAP_FEC_FC)) | |
| 1104 | + return -EINVAL; | |
| 1105 | + fec_mode = FUN_PORT_FEC_FC; | |
| 1106 | + break; | |
| 1107 | + case ETHTOOL_FEC_RS: | |
| 1108 | + if (!(fp->port_caps & FUN_PORT_CAP_FEC_RS)) | |
| 1109 | + return -EINVAL; | |
| 1110 | + fec_mode = FUN_PORT_FEC_RS; | |
| 1111 | + break; | |
| 1112 | + default: | |
| 1113 | + return -EINVAL; | |
| 1114 | + } | |
| 1115 | + | |
| 1116 | + return fun_port_write_cmd(fp, FUN_ADMIN_PORT_KEY_FEC, fec_mode); | |
| 1117 | +} | |
| 1118 | + | |
| 1119 | +static const struct ethtool_ops fun_ethtool_ops = { | |
| 1120 | + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | | |
| 1121 | + ETHTOOL_COALESCE_MAX_FRAMES, | |
| 1122 | + .get_link_ksettings = fun_get_link_ksettings, | |
| 1123 | + .set_link_ksettings = fun_set_link_ksettings, | |
| 1124 | + .set_phys_id = fun_set_phys_id, | |
| 1125 | + .get_drvinfo = fun_get_drvinfo, | |
| 1126 | + .get_msglevel = fun_get_msglevel, | |
| 1127 | + .set_msglevel = fun_set_msglevel, | |
| 1128 | + .get_regs_len = fun_get_regs_len, | |
| 1129 | + .get_regs = fun_get_regs, | |
| 1130 | + .get_link = ethtool_op_get_link, | |
| 1131 | + .get_coalesce = fun_get_coalesce, | |
| 1132 | + .set_coalesce = fun_set_coalesce, | |
| 1133 | + .get_ts_info = fun_get_ts_info, | |
| 1134 | + .get_ringparam = fun_get_ringparam, | |
| 1135 | + .set_ringparam = fun_set_ringparam, | |
| 1136 | + .get_sset_count = fun_get_sset_count, | |
| 1137 | + .get_strings = fun_get_strings, | |
| 1138 | + .get_ethtool_stats = fun_get_ethtool_stats, | |
| 1139 | + .get_rxnfc = fun_get_rxnfc, | |
| 1140 | + .set_rxnfc = fun_set_rxnfc, | |
| 1141 | + .get_rxfh_indir_size = fun_get_rxfh_indir_size, | |
| 1142 | + .get_rxfh_key_size = fun_get_rxfh_key_size, | |
| 1143 | + .get_rxfh = fun_get_rxfh, | |
| 1144 | + .set_rxfh = fun_set_rxfh, | |
| 1145 | + .get_channels = fun_get_channels, | |
| 1146 | + .set_channels = fun_set_channels, | |
| 1147 | + .get_fecparam = fun_get_fecparam, | |
| 1148 | + .set_fecparam = fun_set_fecparam, | |
| 1149 | + .get_pauseparam = fun_get_pauseparam, | |
| 1150 | + .set_pauseparam = fun_set_pauseparam, | |
| 1151 | + .nway_reset = fun_restart_an, | |
| 1152 | + .get_pause_stats = fun_get_pause_stats, | |
| 1153 | + .get_fec_stats = fun_get_fec_stats, | |
| 1154 | + .get_eth_mac_stats = fun_get_802_3_stats, | |
| 1155 | + .get_eth_ctrl_stats = fun_get_802_3_ctrl_stats, | |
| 1156 | + .get_rmon_stats = fun_get_rmon_stats, | |
| 1157 | +}; | |
| 1158 | + | |
| 1159 | +void fun_set_ethtool_ops(struct net_device *netdev) | |
| 1160 | +{ | |
| 1161 | + netdev->ethtool_ops = &fun_ethtool_ops; | |
| 1162 | +} | |