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 50 static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) 51 { 52 struct alx_priv *alx = netdev_priv(netdev); 53 struct alx_hw *hw = &alx->hw; 54 55 ecmd->supported = SUPPORTED_10baseT_Half | 56 SUPPORTED_10baseT_Full | 57 SUPPORTED_100baseT_Half | 58 SUPPORTED_100baseT_Full | 59 SUPPORTED_Autoneg | 60 SUPPORTED_TP | 61 SUPPORTED_Pause; 62 if (alx_hw_giga(hw)) 63 ecmd->supported |= SUPPORTED_1000baseT_Full; 64 65 ecmd->advertising = ADVERTISED_TP; 66 if (hw->adv_cfg & ADVERTISED_Autoneg) 67 ecmd->advertising |= hw->adv_cfg; 68 69 ecmd->port = PORT_TP; 70 ecmd->phy_address = 0; 71 if (hw->adv_cfg & ADVERTISED_Autoneg) 72 ecmd->autoneg = AUTONEG_ENABLE; 73 else 74 ecmd->autoneg = AUTONEG_DISABLE; 75 ecmd->transceiver = XCVR_INTERNAL; 76 77 if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) { 78 if (hw->flowctrl & ALX_FC_RX) { 79 ecmd->advertising |= ADVERTISED_Pause; 80 81 if (!(hw->flowctrl & ALX_FC_TX)) 82 ecmd->advertising |= ADVERTISED_Asym_Pause; 83 } else if (hw->flowctrl & ALX_FC_TX) { 84 ecmd->advertising |= ADVERTISED_Asym_Pause; 85 } 86 } 87 88 if (hw->link_speed != SPEED_UNKNOWN) { 89 ethtool_cmd_speed_set(ecmd, 90 hw->link_speed - hw->link_speed % 10); 91 ecmd->duplex = hw->link_speed % 10; 92 } else { 93 ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); 94 ecmd->duplex = DUPLEX_UNKNOWN; 95 } 96 97 return 0; 98 } 99 100 static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) 101 { 102 struct alx_priv *alx = netdev_priv(netdev); 103 struct alx_hw *hw = &alx->hw; 104 u32 adv_cfg; 105 106 ASSERT_RTNL(); 107 108 if (ecmd->autoneg == AUTONEG_ENABLE) { 109 if (ecmd->advertising & ADVERTISED_1000baseT_Half) 110 return -EINVAL; 111 adv_cfg = ecmd->advertising | ADVERTISED_Autoneg; 112 } else { 113 int speed = ethtool_cmd_speed(ecmd); 114 115 switch (speed + ecmd->duplex) { 116 case SPEED_10 + DUPLEX_HALF: 117 adv_cfg = ADVERTISED_10baseT_Half; 118 break; 119 case SPEED_10 + DUPLEX_FULL: 120 adv_cfg = ADVERTISED_10baseT_Full; 121 break; 122 case SPEED_100 + DUPLEX_HALF: 123 adv_cfg = ADVERTISED_100baseT_Half; 124 break; 125 case SPEED_100 + DUPLEX_FULL: 126 adv_cfg = ADVERTISED_100baseT_Full; 127 break; 128 default: 129 return -EINVAL; 130 } 131 } 132 133 hw->adv_cfg = adv_cfg; 134 return alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl); 135 } 136 137 static void alx_get_pauseparam(struct net_device *netdev, 138 struct ethtool_pauseparam *pause) 139 { 140 struct alx_priv *alx = netdev_priv(netdev); 141 struct alx_hw *hw = &alx->hw; 142 143 if (hw->flowctrl & ALX_FC_ANEG && 144 hw->adv_cfg & ADVERTISED_Autoneg) 145 pause->autoneg = AUTONEG_ENABLE; 146 else 147 pause->autoneg = AUTONEG_DISABLE; 148 149 if (hw->flowctrl & ALX_FC_TX) 150 pause->tx_pause = 1; 151 else 152 pause->tx_pause = 0; 153 154 if (hw->flowctrl & ALX_FC_RX) 155 pause->rx_pause = 1; 156 else 157 pause->rx_pause = 0; 158 } 159 160 161 static int alx_set_pauseparam(struct net_device *netdev, 162 struct ethtool_pauseparam *pause) 163 { 164 struct alx_priv *alx = netdev_priv(netdev); 165 struct alx_hw *hw = &alx->hw; 166 int err = 0; 167 bool reconfig_phy = false; 168 u8 fc = 0; 169 170 if (pause->tx_pause) 171 fc |= ALX_FC_TX; 172 if (pause->rx_pause) 173 fc |= ALX_FC_RX; 174 if (pause->autoneg) 175 fc |= ALX_FC_ANEG; 176 177 ASSERT_RTNL(); 178 179 /* restart auto-neg for auto-mode */ 180 if (hw->adv_cfg & ADVERTISED_Autoneg) { 181 if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG)) 182 reconfig_phy = true; 183 if (fc & hw->flowctrl & ALX_FC_ANEG && 184 (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 185 reconfig_phy = true; 186 } 187 188 if (reconfig_phy) { 189 err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc); 190 return err; 191 } 192 193 /* flow control on mac */ 194 if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX)) 195 alx_cfg_mac_flowcontrol(hw, fc); 196 197 hw->flowctrl = fc; 198 199 return 0; 200 } 201 202 static u32 alx_get_msglevel(struct net_device *netdev) 203 { 204 struct alx_priv *alx = netdev_priv(netdev); 205 206 return alx->msg_enable; 207 } 208 209 static void alx_set_msglevel(struct net_device *netdev, u32 data) 210 { 211 struct alx_priv *alx = netdev_priv(netdev); 212 213 alx->msg_enable = data; 214 } 215 216 static void alx_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 217 { 218 struct alx_priv *alx = netdev_priv(netdev); 219 struct alx_hw *hw = &alx->hw; 220 221 wol->supported = WAKE_MAGIC | WAKE_PHY; 222 wol->wolopts = 0; 223 224 if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC) 225 wol->wolopts |= WAKE_MAGIC; 226 if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) 227 wol->wolopts |= WAKE_PHY; 228 } 229 230 static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 231 { 232 struct alx_priv *alx = netdev_priv(netdev); 233 struct alx_hw *hw = &alx->hw; 234 235 if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | 236 WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) 237 return -EOPNOTSUPP; 238 239 hw->sleep_ctrl = 0; 240 241 if (wol->wolopts & WAKE_MAGIC) 242 hw->sleep_ctrl |= ALX_SLEEP_WOL_MAGIC; 243 if (wol->wolopts & WAKE_PHY) 244 hw->sleep_ctrl |= ALX_SLEEP_WOL_PHY; 245 246 device_set_wakeup_enable(&alx->hw.pdev->dev, hw->sleep_ctrl); 247 248 return 0; 249 } 250 251 static void alx_get_drvinfo(struct net_device *netdev, 252 struct ethtool_drvinfo *drvinfo) 253 { 254 struct alx_priv *alx = netdev_priv(netdev); 255 256 strlcpy(drvinfo->driver, alx_drv_name, sizeof(drvinfo->driver)); 257 strlcpy(drvinfo->bus_info, pci_name(alx->hw.pdev), 258 sizeof(drvinfo->bus_info)); 259 } 260 261 const struct ethtool_ops alx_ethtool_ops = { 262 .get_settings = alx_get_settings, 263 .set_settings = alx_set_settings, 264 .get_pauseparam = alx_get_pauseparam, 265 .set_pauseparam = alx_set_pauseparam, 266 .get_drvinfo = alx_get_drvinfo, 267 .get_msglevel = alx_get_msglevel, 268 .set_msglevel = alx_set_msglevel, 269 .get_wol = alx_get_wol, 270 .set_wol = alx_set_wol, 271 .get_link = ethtool_op_get_link, 272 }; 273