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