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_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) 129 { 130 struct alx_priv *alx = netdev_priv(netdev); 131 struct alx_hw *hw = &alx->hw; 132 133 ecmd->supported = SUPPORTED_Autoneg | 134 SUPPORTED_TP | 135 SUPPORTED_Pause | 136 SUPPORTED_Asym_Pause; 137 if (alx_hw_giga(hw)) 138 ecmd->supported |= SUPPORTED_1000baseT_Full; 139 ecmd->supported |= alx_get_supported_speeds(hw); 140 141 ecmd->advertising = ADVERTISED_TP; 142 if (hw->adv_cfg & ADVERTISED_Autoneg) 143 ecmd->advertising |= hw->adv_cfg; 144 145 ecmd->port = PORT_TP; 146 ecmd->phy_address = 0; 147 148 if (hw->adv_cfg & ADVERTISED_Autoneg) 149 ecmd->autoneg = AUTONEG_ENABLE; 150 else 151 ecmd->autoneg = AUTONEG_DISABLE; 152 ecmd->transceiver = XCVR_INTERNAL; 153 154 if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) { 155 if (hw->flowctrl & ALX_FC_RX) { 156 ecmd->advertising |= ADVERTISED_Pause; 157 158 if (!(hw->flowctrl & ALX_FC_TX)) 159 ecmd->advertising |= ADVERTISED_Asym_Pause; 160 } else if (hw->flowctrl & ALX_FC_TX) { 161 ecmd->advertising |= ADVERTISED_Asym_Pause; 162 } 163 } 164 165 ethtool_cmd_speed_set(ecmd, hw->link_speed); 166 ecmd->duplex = hw->duplex; 167 168 return 0; 169 } 170 171 static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) 172 { 173 struct alx_priv *alx = netdev_priv(netdev); 174 struct alx_hw *hw = &alx->hw; 175 u32 adv_cfg; 176 177 ASSERT_RTNL(); 178 179 if (ecmd->autoneg == AUTONEG_ENABLE) { 180 if (ecmd->advertising & ~alx_get_supported_speeds(hw)) 181 return -EINVAL; 182 adv_cfg = ecmd->advertising | ADVERTISED_Autoneg; 183 } else { 184 adv_cfg = alx_speed_to_ethadv(ethtool_cmd_speed(ecmd), 185 ecmd->duplex); 186 187 if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full) 188 return -EINVAL; 189 } 190 191 hw->adv_cfg = adv_cfg; 192 return alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl); 193 } 194 195 static void alx_get_pauseparam(struct net_device *netdev, 196 struct ethtool_pauseparam *pause) 197 { 198 struct alx_priv *alx = netdev_priv(netdev); 199 struct alx_hw *hw = &alx->hw; 200 201 pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG && 202 hw->adv_cfg & ADVERTISED_Autoneg); 203 pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX); 204 pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX); 205 } 206 207 208 static int alx_set_pauseparam(struct net_device *netdev, 209 struct ethtool_pauseparam *pause) 210 { 211 struct alx_priv *alx = netdev_priv(netdev); 212 struct alx_hw *hw = &alx->hw; 213 int err = 0; 214 bool reconfig_phy = false; 215 u8 fc = 0; 216 217 if (pause->tx_pause) 218 fc |= ALX_FC_TX; 219 if (pause->rx_pause) 220 fc |= ALX_FC_RX; 221 if (pause->autoneg) 222 fc |= ALX_FC_ANEG; 223 224 ASSERT_RTNL(); 225 226 /* restart auto-neg for auto-mode */ 227 if (hw->adv_cfg & ADVERTISED_Autoneg) { 228 if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG)) 229 reconfig_phy = true; 230 if (fc & hw->flowctrl & ALX_FC_ANEG && 231 (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 232 reconfig_phy = true; 233 } 234 235 if (reconfig_phy) { 236 err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc); 237 if (err) 238 return err; 239 } 240 241 /* flow control on mac */ 242 if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 243 alx_cfg_mac_flowcontrol(hw, fc); 244 245 hw->flowctrl = fc; 246 247 return 0; 248 } 249 250 static u32 alx_get_msglevel(struct net_device *netdev) 251 { 252 struct alx_priv *alx = netdev_priv(netdev); 253 254 return alx->msg_enable; 255 } 256 257 static void alx_set_msglevel(struct net_device *netdev, u32 data) 258 { 259 struct alx_priv *alx = netdev_priv(netdev); 260 261 alx->msg_enable = data; 262 } 263 264 static void alx_get_ethtool_stats(struct net_device *netdev, 265 struct ethtool_stats *estats, u64 *data) 266 { 267 struct alx_priv *alx = netdev_priv(netdev); 268 struct alx_hw *hw = &alx->hw; 269 270 spin_lock(&alx->stats_lock); 271 272 alx_update_hw_stats(hw); 273 BUILD_BUG_ON(sizeof(hw->stats) - offsetof(struct alx_hw_stats, rx_ok) < 274 ALX_NUM_STATS * sizeof(u64)); 275 memcpy(data, &hw->stats.rx_ok, ALX_NUM_STATS * sizeof(u64)); 276 277 spin_unlock(&alx->stats_lock); 278 } 279 280 static void alx_get_strings(struct net_device *netdev, u32 stringset, u8 *buf) 281 { 282 switch (stringset) { 283 case ETH_SS_STATS: 284 memcpy(buf, &alx_gstrings_stats, sizeof(alx_gstrings_stats)); 285 break; 286 default: 287 WARN_ON(1); 288 break; 289 } 290 } 291 292 static int alx_get_sset_count(struct net_device *netdev, int sset) 293 { 294 switch (sset) { 295 case ETH_SS_STATS: 296 return ALX_NUM_STATS; 297 default: 298 return -EINVAL; 299 } 300 } 301 302 const struct ethtool_ops alx_ethtool_ops = { 303 .get_settings = alx_get_settings, 304 .set_settings = alx_set_settings, 305 .get_pauseparam = alx_get_pauseparam, 306 .set_pauseparam = alx_set_pauseparam, 307 .get_msglevel = alx_get_msglevel, 308 .set_msglevel = alx_set_msglevel, 309 .get_link = ethtool_op_get_link, 310 .get_strings = alx_get_strings, 311 .get_sset_count = alx_get_sset_count, 312 .get_ethtool_stats = alx_get_ethtool_stats, 313 }; 314