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 497ec56894SJohannes Berg static u32 alx_get_supported_speeds(struct alx_hw *hw) 507ec56894SJohannes Berg { 517ec56894SJohannes Berg u32 supported = SUPPORTED_10baseT_Half | 527ec56894SJohannes Berg SUPPORTED_10baseT_Full | 537ec56894SJohannes Berg SUPPORTED_100baseT_Half | 547ec56894SJohannes Berg SUPPORTED_100baseT_Full; 557ec56894SJohannes Berg 567ec56894SJohannes Berg if (alx_hw_giga(hw)) 577ec56894SJohannes Berg supported |= SUPPORTED_1000baseT_Full; 587ec56894SJohannes Berg 597ec56894SJohannes Berg BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half); 607ec56894SJohannes Berg BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full); 617ec56894SJohannes Berg BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half); 627ec56894SJohannes Berg BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full); 637ec56894SJohannes Berg BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full); 647ec56894SJohannes Berg 657ec56894SJohannes Berg return supported; 667ec56894SJohannes Berg } 67ab69bde6SJohannes Berg 68ab69bde6SJohannes Berg static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) 69ab69bde6SJohannes Berg { 70ab69bde6SJohannes Berg struct alx_priv *alx = netdev_priv(netdev); 71ab69bde6SJohannes Berg struct alx_hw *hw = &alx->hw; 72ab69bde6SJohannes Berg 737ec56894SJohannes Berg ecmd->supported = SUPPORTED_Autoneg | 74ab69bde6SJohannes Berg SUPPORTED_TP | 757ec56894SJohannes Berg SUPPORTED_Pause | 767ec56894SJohannes Berg SUPPORTED_Asym_Pause; 77ab69bde6SJohannes Berg if (alx_hw_giga(hw)) 78ab69bde6SJohannes Berg ecmd->supported |= SUPPORTED_1000baseT_Full; 797ec56894SJohannes Berg ecmd->supported |= alx_get_supported_speeds(hw); 80ab69bde6SJohannes Berg 81ab69bde6SJohannes Berg ecmd->advertising = ADVERTISED_TP; 82ab69bde6SJohannes Berg if (hw->adv_cfg & ADVERTISED_Autoneg) 83ab69bde6SJohannes Berg ecmd->advertising |= hw->adv_cfg; 84ab69bde6SJohannes Berg 85ab69bde6SJohannes Berg ecmd->port = PORT_TP; 86ab69bde6SJohannes Berg ecmd->phy_address = 0; 877ec56894SJohannes Berg 88ab69bde6SJohannes Berg if (hw->adv_cfg & ADVERTISED_Autoneg) 89ab69bde6SJohannes Berg ecmd->autoneg = AUTONEG_ENABLE; 90ab69bde6SJohannes Berg else 91ab69bde6SJohannes Berg ecmd->autoneg = AUTONEG_DISABLE; 92ab69bde6SJohannes Berg ecmd->transceiver = XCVR_INTERNAL; 93ab69bde6SJohannes Berg 94ab69bde6SJohannes Berg if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) { 95ab69bde6SJohannes Berg if (hw->flowctrl & ALX_FC_RX) { 96ab69bde6SJohannes Berg ecmd->advertising |= ADVERTISED_Pause; 97ab69bde6SJohannes Berg 98ab69bde6SJohannes Berg if (!(hw->flowctrl & ALX_FC_TX)) 99ab69bde6SJohannes Berg ecmd->advertising |= ADVERTISED_Asym_Pause; 100ab69bde6SJohannes Berg } else if (hw->flowctrl & ALX_FC_TX) { 101ab69bde6SJohannes Berg ecmd->advertising |= ADVERTISED_Asym_Pause; 102ab69bde6SJohannes Berg } 103ab69bde6SJohannes Berg } 104ab69bde6SJohannes Berg 105a5b87cc9SJohannes Berg ethtool_cmd_speed_set(ecmd, hw->link_speed); 106a5b87cc9SJohannes Berg ecmd->duplex = hw->duplex; 107ab69bde6SJohannes Berg 108ab69bde6SJohannes Berg return 0; 109ab69bde6SJohannes Berg } 110ab69bde6SJohannes Berg 111ab69bde6SJohannes Berg static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) 112ab69bde6SJohannes Berg { 113ab69bde6SJohannes Berg struct alx_priv *alx = netdev_priv(netdev); 114ab69bde6SJohannes Berg struct alx_hw *hw = &alx->hw; 115ab69bde6SJohannes Berg u32 adv_cfg; 116ab69bde6SJohannes Berg 117ab69bde6SJohannes Berg ASSERT_RTNL(); 118ab69bde6SJohannes Berg 119ab69bde6SJohannes Berg if (ecmd->autoneg == AUTONEG_ENABLE) { 1207ec56894SJohannes Berg if (ecmd->advertising & ~alx_get_supported_speeds(hw)) 121ab69bde6SJohannes Berg return -EINVAL; 122ab69bde6SJohannes Berg adv_cfg = ecmd->advertising | ADVERTISED_Autoneg; 123ab69bde6SJohannes Berg } else { 124a5b87cc9SJohannes Berg adv_cfg = alx_speed_to_ethadv(ethtool_cmd_speed(ecmd), 125a5b87cc9SJohannes Berg ecmd->duplex); 126ab69bde6SJohannes Berg 127a5b87cc9SJohannes Berg if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full) 128ab69bde6SJohannes Berg return -EINVAL; 129ab69bde6SJohannes Berg } 130ab69bde6SJohannes Berg 131ab69bde6SJohannes Berg hw->adv_cfg = adv_cfg; 132ab69bde6SJohannes Berg return alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl); 133ab69bde6SJohannes Berg } 134ab69bde6SJohannes Berg 135ab69bde6SJohannes Berg static void alx_get_pauseparam(struct net_device *netdev, 136ab69bde6SJohannes Berg struct ethtool_pauseparam *pause) 137ab69bde6SJohannes Berg { 138ab69bde6SJohannes Berg struct alx_priv *alx = netdev_priv(netdev); 139ab69bde6SJohannes Berg struct alx_hw *hw = &alx->hw; 140ab69bde6SJohannes Berg 1417ec56894SJohannes Berg pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG && 1427ec56894SJohannes Berg hw->adv_cfg & ADVERTISED_Autoneg); 1437ec56894SJohannes Berg pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX); 1447ec56894SJohannes Berg pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX); 145ab69bde6SJohannes Berg } 146ab69bde6SJohannes Berg 147ab69bde6SJohannes Berg 148ab69bde6SJohannes Berg static int alx_set_pauseparam(struct net_device *netdev, 149ab69bde6SJohannes Berg struct ethtool_pauseparam *pause) 150ab69bde6SJohannes Berg { 151ab69bde6SJohannes Berg struct alx_priv *alx = netdev_priv(netdev); 152ab69bde6SJohannes Berg struct alx_hw *hw = &alx->hw; 153ab69bde6SJohannes Berg int err = 0; 154ab69bde6SJohannes Berg bool reconfig_phy = false; 155ab69bde6SJohannes Berg u8 fc = 0; 156ab69bde6SJohannes Berg 157ab69bde6SJohannes Berg if (pause->tx_pause) 158ab69bde6SJohannes Berg fc |= ALX_FC_TX; 159ab69bde6SJohannes Berg if (pause->rx_pause) 160ab69bde6SJohannes Berg fc |= ALX_FC_RX; 161ab69bde6SJohannes Berg if (pause->autoneg) 162ab69bde6SJohannes Berg fc |= ALX_FC_ANEG; 163ab69bde6SJohannes Berg 164ab69bde6SJohannes Berg ASSERT_RTNL(); 165ab69bde6SJohannes Berg 166ab69bde6SJohannes Berg /* restart auto-neg for auto-mode */ 167ab69bde6SJohannes Berg if (hw->adv_cfg & ADVERTISED_Autoneg) { 168ab69bde6SJohannes Berg if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG)) 169ab69bde6SJohannes Berg reconfig_phy = true; 170ab69bde6SJohannes Berg if (fc & hw->flowctrl & ALX_FC_ANEG && 171ab69bde6SJohannes Berg (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 172ab69bde6SJohannes Berg reconfig_phy = true; 173ab69bde6SJohannes Berg } 174ab69bde6SJohannes Berg 175ab69bde6SJohannes Berg if (reconfig_phy) { 176ab69bde6SJohannes Berg err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc); 177ef0cc4b1SJohannes Berg if (err) 178ab69bde6SJohannes Berg return err; 179ab69bde6SJohannes Berg } 180ab69bde6SJohannes Berg 181ab69bde6SJohannes Berg /* flow control on mac */ 182ab69bde6SJohannes Berg if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 183ab69bde6SJohannes Berg alx_cfg_mac_flowcontrol(hw, fc); 184ab69bde6SJohannes Berg 185ab69bde6SJohannes Berg hw->flowctrl = fc; 186ab69bde6SJohannes Berg 187ab69bde6SJohannes Berg return 0; 188ab69bde6SJohannes Berg } 189ab69bde6SJohannes Berg 190ab69bde6SJohannes Berg static u32 alx_get_msglevel(struct net_device *netdev) 191ab69bde6SJohannes Berg { 192ab69bde6SJohannes Berg struct alx_priv *alx = netdev_priv(netdev); 193ab69bde6SJohannes Berg 194ab69bde6SJohannes Berg return alx->msg_enable; 195ab69bde6SJohannes Berg } 196ab69bde6SJohannes Berg 197ab69bde6SJohannes Berg static void alx_set_msglevel(struct net_device *netdev, u32 data) 198ab69bde6SJohannes Berg { 199ab69bde6SJohannes Berg struct alx_priv *alx = netdev_priv(netdev); 200ab69bde6SJohannes Berg 201ab69bde6SJohannes Berg alx->msg_enable = data; 202ab69bde6SJohannes Berg } 203ab69bde6SJohannes Berg 204ab69bde6SJohannes Berg static void alx_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 205ab69bde6SJohannes Berg { 206ab69bde6SJohannes Berg struct alx_priv *alx = netdev_priv(netdev); 207ab69bde6SJohannes Berg struct alx_hw *hw = &alx->hw; 208ab69bde6SJohannes Berg 209ab69bde6SJohannes Berg wol->supported = WAKE_MAGIC | WAKE_PHY; 210ab69bde6SJohannes Berg wol->wolopts = 0; 211ab69bde6SJohannes Berg 212ab69bde6SJohannes Berg if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC) 213ab69bde6SJohannes Berg wol->wolopts |= WAKE_MAGIC; 214ab69bde6SJohannes Berg if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) 215ab69bde6SJohannes Berg wol->wolopts |= WAKE_PHY; 216ab69bde6SJohannes Berg } 217ab69bde6SJohannes Berg 218ab69bde6SJohannes Berg static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 219ab69bde6SJohannes Berg { 220ab69bde6SJohannes Berg struct alx_priv *alx = netdev_priv(netdev); 221ab69bde6SJohannes Berg struct alx_hw *hw = &alx->hw; 222ab69bde6SJohannes Berg 2237ec56894SJohannes Berg if (wol->wolopts & ~(WAKE_MAGIC | WAKE_PHY)) 224ab69bde6SJohannes Berg return -EOPNOTSUPP; 225ab69bde6SJohannes Berg 226ab69bde6SJohannes Berg hw->sleep_ctrl = 0; 227ab69bde6SJohannes Berg 228ab69bde6SJohannes Berg if (wol->wolopts & WAKE_MAGIC) 229ab69bde6SJohannes Berg hw->sleep_ctrl |= ALX_SLEEP_WOL_MAGIC; 230ab69bde6SJohannes Berg if (wol->wolopts & WAKE_PHY) 231ab69bde6SJohannes Berg hw->sleep_ctrl |= ALX_SLEEP_WOL_PHY; 232ab69bde6SJohannes Berg 233ab69bde6SJohannes Berg device_set_wakeup_enable(&alx->hw.pdev->dev, hw->sleep_ctrl); 234ab69bde6SJohannes Berg 235ab69bde6SJohannes Berg return 0; 236ab69bde6SJohannes Berg } 237ab69bde6SJohannes Berg 238ab69bde6SJohannes Berg const struct ethtool_ops alx_ethtool_ops = { 239ab69bde6SJohannes Berg .get_settings = alx_get_settings, 240ab69bde6SJohannes Berg .set_settings = alx_set_settings, 241ab69bde6SJohannes Berg .get_pauseparam = alx_get_pauseparam, 242ab69bde6SJohannes Berg .set_pauseparam = alx_set_pauseparam, 243ab69bde6SJohannes Berg .get_msglevel = alx_get_msglevel, 244ab69bde6SJohannes Berg .set_msglevel = alx_set_msglevel, 245ab69bde6SJohannes Berg .get_wol = alx_get_wol, 246ab69bde6SJohannes Berg .set_wol = alx_set_wol, 247ab69bde6SJohannes Berg .get_link = ethtool_op_get_link, 248ab69bde6SJohannes Berg }; 249