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 */ 53 static 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 109 static 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 128 static 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 cmd->base.speed = hw->link_speed; 167 cmd->base.duplex = hw->duplex; 168 169 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 170 supported); 171 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 172 advertising); 173 174 return 0; 175 } 176 177 static int alx_set_link_ksettings(struct net_device *netdev, 178 const struct ethtool_link_ksettings *cmd) 179 { 180 struct alx_priv *alx = netdev_priv(netdev); 181 struct alx_hw *hw = &alx->hw; 182 u32 adv_cfg; 183 u32 advertising; 184 185 ASSERT_RTNL(); 186 187 ethtool_convert_link_mode_to_legacy_u32(&advertising, 188 cmd->link_modes.advertising); 189 190 if (cmd->base.autoneg == AUTONEG_ENABLE) { 191 if (advertising & ~alx_get_supported_speeds(hw)) 192 return -EINVAL; 193 adv_cfg = advertising | ADVERTISED_Autoneg; 194 } else { 195 adv_cfg = alx_speed_to_ethadv(cmd->base.speed, 196 cmd->base.duplex); 197 198 if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full) 199 return -EINVAL; 200 } 201 202 hw->adv_cfg = adv_cfg; 203 return alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl); 204 } 205 206 static void alx_get_pauseparam(struct net_device *netdev, 207 struct ethtool_pauseparam *pause) 208 { 209 struct alx_priv *alx = netdev_priv(netdev); 210 struct alx_hw *hw = &alx->hw; 211 212 pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG && 213 hw->adv_cfg & ADVERTISED_Autoneg); 214 pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX); 215 pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX); 216 } 217 218 219 static int alx_set_pauseparam(struct net_device *netdev, 220 struct ethtool_pauseparam *pause) 221 { 222 struct alx_priv *alx = netdev_priv(netdev); 223 struct alx_hw *hw = &alx->hw; 224 int err = 0; 225 bool reconfig_phy = false; 226 u8 fc = 0; 227 228 if (pause->tx_pause) 229 fc |= ALX_FC_TX; 230 if (pause->rx_pause) 231 fc |= ALX_FC_RX; 232 if (pause->autoneg) 233 fc |= ALX_FC_ANEG; 234 235 ASSERT_RTNL(); 236 237 /* restart auto-neg for auto-mode */ 238 if (hw->adv_cfg & ADVERTISED_Autoneg) { 239 if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG)) 240 reconfig_phy = true; 241 if (fc & hw->flowctrl & ALX_FC_ANEG && 242 (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 243 reconfig_phy = true; 244 } 245 246 if (reconfig_phy) { 247 err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc); 248 if (err) 249 return err; 250 } 251 252 /* flow control on mac */ 253 if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 254 alx_cfg_mac_flowcontrol(hw, fc); 255 256 hw->flowctrl = fc; 257 258 return 0; 259 } 260 261 static u32 alx_get_msglevel(struct net_device *netdev) 262 { 263 struct alx_priv *alx = netdev_priv(netdev); 264 265 return alx->msg_enable; 266 } 267 268 static void alx_set_msglevel(struct net_device *netdev, u32 data) 269 { 270 struct alx_priv *alx = netdev_priv(netdev); 271 272 alx->msg_enable = data; 273 } 274 275 static void alx_get_ethtool_stats(struct net_device *netdev, 276 struct ethtool_stats *estats, u64 *data) 277 { 278 struct alx_priv *alx = netdev_priv(netdev); 279 struct alx_hw *hw = &alx->hw; 280 281 spin_lock(&alx->stats_lock); 282 283 alx_update_hw_stats(hw); 284 BUILD_BUG_ON(sizeof(hw->stats) - offsetof(struct alx_hw_stats, rx_ok) < 285 ALX_NUM_STATS * sizeof(u64)); 286 memcpy(data, &hw->stats.rx_ok, ALX_NUM_STATS * sizeof(u64)); 287 288 spin_unlock(&alx->stats_lock); 289 } 290 291 static void alx_get_strings(struct net_device *netdev, u32 stringset, u8 *buf) 292 { 293 switch (stringset) { 294 case ETH_SS_STATS: 295 memcpy(buf, &alx_gstrings_stats, sizeof(alx_gstrings_stats)); 296 break; 297 default: 298 WARN_ON(1); 299 break; 300 } 301 } 302 303 static int alx_get_sset_count(struct net_device *netdev, int sset) 304 { 305 switch (sset) { 306 case ETH_SS_STATS: 307 return ALX_NUM_STATS; 308 default: 309 return -EINVAL; 310 } 311 } 312 313 const struct ethtool_ops alx_ethtool_ops = { 314 .get_pauseparam = alx_get_pauseparam, 315 .set_pauseparam = alx_set_pauseparam, 316 .get_msglevel = alx_get_msglevel, 317 .set_msglevel = alx_set_msglevel, 318 .get_link = ethtool_op_get_link, 319 .get_strings = alx_get_strings, 320 .get_sset_count = alx_get_sset_count, 321 .get_ethtool_stats = alx_get_ethtool_stats, 322 .get_link_ksettings = alx_get_link_ksettings, 323 .set_link_ksettings = alx_set_link_ksettings, 324 }; 325