ethtool.c (8921B)
1/* 2 * Copyright (c) 2013 Johannes Berg <johannes@sipsolutions.net> 3 * 4 * This file is free software: you may copy, redistribute and/or modify it 5 * under the terms of the GNU General Public License as published by the 6 * Free Software Foundation, either version 2 of the License, or (at your 7 * option) any later version. 8 * 9 * This file is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * This file incorporates work covered by the following copyright and 18 * permission notice: 19 * 20 * Copyright (c) 2012 Qualcomm Atheros, Inc. 21 * 22 * Permission to use, copy, modify, and/or distribute this software for any 23 * purpose with or without fee is hereby granted, provided that the above 24 * copyright notice and this permission notice appear in all copies. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 27 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 28 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 29 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 30 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 31 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 32 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 33 */ 34 35#include <linux/pci.h> 36#include <linux/ip.h> 37#include <linux/tcp.h> 38#include <linux/netdevice.h> 39#include <linux/etherdevice.h> 40#include <linux/ethtool.h> 41#include <linux/mdio.h> 42#include <linux/interrupt.h> 43#include <asm/byteorder.h> 44 45#include "alx.h" 46#include "reg.h" 47#include "hw.h" 48 49/* The order of these strings must match the order of the fields in 50 * struct alx_hw_stats 51 * See hw.h 52 */ 53static const char alx_gstrings_stats[][ETH_GSTRING_LEN] = { 54 "rx_packets", 55 "rx_bcast_packets", 56 "rx_mcast_packets", 57 "rx_pause_packets", 58 "rx_ctrl_packets", 59 "rx_fcs_errors", 60 "rx_length_errors", 61 "rx_bytes", 62 "rx_runt_packets", 63 "rx_fragments", 64 "rx_64B_or_less_packets", 65 "rx_65B_to_127B_packets", 66 "rx_128B_to_255B_packets", 67 "rx_256B_to_511B_packets", 68 "rx_512B_to_1023B_packets", 69 "rx_1024B_to_1518B_packets", 70 "rx_1519B_to_mtu_packets", 71 "rx_oversize_packets", 72 "rx_rxf_ov_drop_packets", 73 "rx_rrd_ov_drop_packets", 74 "rx_align_errors", 75 "rx_bcast_bytes", 76 "rx_mcast_bytes", 77 "rx_address_errors", 78 "tx_packets", 79 "tx_bcast_packets", 80 "tx_mcast_packets", 81 "tx_pause_packets", 82 "tx_exc_defer_packets", 83 "tx_ctrl_packets", 84 "tx_defer_packets", 85 "tx_bytes", 86 "tx_64B_or_less_packets", 87 "tx_65B_to_127B_packets", 88 "tx_128B_to_255B_packets", 89 "tx_256B_to_511B_packets", 90 "tx_512B_to_1023B_packets", 91 "tx_1024B_to_1518B_packets", 92 "tx_1519B_to_mtu_packets", 93 "tx_single_collision", 94 "tx_multiple_collisions", 95 "tx_late_collision", 96 "tx_abort_collision", 97 "tx_underrun", 98 "tx_trd_eop", 99 "tx_length_errors", 100 "tx_trunc_packets", 101 "tx_bcast_bytes", 102 "tx_mcast_bytes", 103 "tx_update", 104}; 105 106#define ALX_NUM_STATS ARRAY_SIZE(alx_gstrings_stats) 107 108 109static u32 alx_get_supported_speeds(struct alx_hw *hw) 110{ 111 u32 supported = SUPPORTED_10baseT_Half | 112 SUPPORTED_10baseT_Full | 113 SUPPORTED_100baseT_Half | 114 SUPPORTED_100baseT_Full; 115 116 if (alx_hw_giga(hw)) 117 supported |= SUPPORTED_1000baseT_Full; 118 119 BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half); 120 BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full); 121 BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half); 122 BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full); 123 BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full); 124 125 return supported; 126} 127 128static int alx_get_link_ksettings(struct net_device *netdev, 129 struct ethtool_link_ksettings *cmd) 130{ 131 struct alx_priv *alx = netdev_priv(netdev); 132 struct alx_hw *hw = &alx->hw; 133 u32 supported, advertising; 134 135 supported = SUPPORTED_Autoneg | 136 SUPPORTED_TP | 137 SUPPORTED_Pause | 138 SUPPORTED_Asym_Pause; 139 if (alx_hw_giga(hw)) 140 supported |= SUPPORTED_1000baseT_Full; 141 supported |= alx_get_supported_speeds(hw); 142 143 advertising = ADVERTISED_TP; 144 if (hw->adv_cfg & ADVERTISED_Autoneg) 145 advertising |= hw->adv_cfg; 146 147 cmd->base.port = PORT_TP; 148 cmd->base.phy_address = 0; 149 150 if (hw->adv_cfg & ADVERTISED_Autoneg) 151 cmd->base.autoneg = AUTONEG_ENABLE; 152 else 153 cmd->base.autoneg = AUTONEG_DISABLE; 154 155 if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) { 156 if (hw->flowctrl & ALX_FC_RX) { 157 advertising |= ADVERTISED_Pause; 158 159 if (!(hw->flowctrl & ALX_FC_TX)) 160 advertising |= ADVERTISED_Asym_Pause; 161 } else if (hw->flowctrl & ALX_FC_TX) { 162 advertising |= ADVERTISED_Asym_Pause; 163 } 164 } 165 166 mutex_lock(&alx->mtx); 167 cmd->base.speed = hw->link_speed; 168 cmd->base.duplex = hw->duplex; 169 mutex_unlock(&alx->mtx); 170 171 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 172 supported); 173 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 174 advertising); 175 176 return 0; 177} 178 179static int alx_set_link_ksettings(struct net_device *netdev, 180 const struct ethtool_link_ksettings *cmd) 181{ 182 struct alx_priv *alx = netdev_priv(netdev); 183 struct alx_hw *hw = &alx->hw; 184 u32 adv_cfg; 185 u32 advertising; 186 int ret; 187 188 ethtool_convert_link_mode_to_legacy_u32(&advertising, 189 cmd->link_modes.advertising); 190 191 if (cmd->base.autoneg == AUTONEG_ENABLE) { 192 if (advertising & ~alx_get_supported_speeds(hw)) 193 return -EINVAL; 194 adv_cfg = advertising | ADVERTISED_Autoneg; 195 } else { 196 adv_cfg = alx_speed_to_ethadv(cmd->base.speed, 197 cmd->base.duplex); 198 199 if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full) 200 return -EINVAL; 201 } 202 203 hw->adv_cfg = adv_cfg; 204 205 mutex_lock(&alx->mtx); 206 ret = alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl); 207 mutex_unlock(&alx->mtx); 208 209 return ret; 210} 211 212static void alx_get_pauseparam(struct net_device *netdev, 213 struct ethtool_pauseparam *pause) 214{ 215 struct alx_priv *alx = netdev_priv(netdev); 216 struct alx_hw *hw = &alx->hw; 217 218 mutex_lock(&alx->mtx); 219 pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG && 220 hw->adv_cfg & ADVERTISED_Autoneg); 221 pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX); 222 pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX); 223 mutex_unlock(&alx->mtx); 224} 225 226 227static int alx_set_pauseparam(struct net_device *netdev, 228 struct ethtool_pauseparam *pause) 229{ 230 struct alx_priv *alx = netdev_priv(netdev); 231 struct alx_hw *hw = &alx->hw; 232 int err = 0; 233 bool reconfig_phy = false; 234 u8 fc = 0; 235 236 if (pause->tx_pause) 237 fc |= ALX_FC_TX; 238 if (pause->rx_pause) 239 fc |= ALX_FC_RX; 240 if (pause->autoneg) 241 fc |= ALX_FC_ANEG; 242 243 mutex_lock(&alx->mtx); 244 245 /* restart auto-neg for auto-mode */ 246 if (hw->adv_cfg & ADVERTISED_Autoneg) { 247 if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG)) 248 reconfig_phy = true; 249 if (fc & hw->flowctrl & ALX_FC_ANEG && 250 (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 251 reconfig_phy = true; 252 } 253 254 if (reconfig_phy) { 255 err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc); 256 if (err) { 257 mutex_unlock(&alx->mtx); 258 return err; 259 } 260 } 261 262 /* flow control on mac */ 263 if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 264 alx_cfg_mac_flowcontrol(hw, fc); 265 266 hw->flowctrl = fc; 267 mutex_unlock(&alx->mtx); 268 269 return 0; 270} 271 272static u32 alx_get_msglevel(struct net_device *netdev) 273{ 274 struct alx_priv *alx = netdev_priv(netdev); 275 276 return alx->msg_enable; 277} 278 279static void alx_set_msglevel(struct net_device *netdev, u32 data) 280{ 281 struct alx_priv *alx = netdev_priv(netdev); 282 283 alx->msg_enable = data; 284} 285 286static void alx_get_ethtool_stats(struct net_device *netdev, 287 struct ethtool_stats *estats, u64 *data) 288{ 289 struct alx_priv *alx = netdev_priv(netdev); 290 struct alx_hw *hw = &alx->hw; 291 292 spin_lock(&alx->stats_lock); 293 294 alx_update_hw_stats(hw); 295 BUILD_BUG_ON(sizeof(hw->stats) - offsetof(struct alx_hw_stats, rx_ok) < 296 ALX_NUM_STATS * sizeof(u64)); 297 memcpy(data, &hw->stats.rx_ok, ALX_NUM_STATS * sizeof(u64)); 298 299 spin_unlock(&alx->stats_lock); 300} 301 302static void alx_get_strings(struct net_device *netdev, u32 stringset, u8 *buf) 303{ 304 switch (stringset) { 305 case ETH_SS_STATS: 306 memcpy(buf, &alx_gstrings_stats, sizeof(alx_gstrings_stats)); 307 break; 308 default: 309 WARN_ON(1); 310 break; 311 } 312} 313 314static int alx_get_sset_count(struct net_device *netdev, int sset) 315{ 316 switch (sset) { 317 case ETH_SS_STATS: 318 return ALX_NUM_STATS; 319 default: 320 return -EINVAL; 321 } 322} 323 324const struct ethtool_ops alx_ethtool_ops = { 325 .get_pauseparam = alx_get_pauseparam, 326 .set_pauseparam = alx_set_pauseparam, 327 .get_msglevel = alx_get_msglevel, 328 .set_msglevel = alx_set_msglevel, 329 .get_link = ethtool_op_get_link, 330 .get_strings = alx_get_strings, 331 .get_sset_count = alx_get_sset_count, 332 .get_ethtool_stats = alx_get_ethtool_stats, 333 .get_link_ksettings = alx_get_link_ksettings, 334 .set_link_ksettings = alx_set_link_ksettings, 335};