atl1c_ethtool.c (8280B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved. 4 * 5 * Derived from Intel e1000 driver 6 * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. 7 */ 8 9#include <linux/netdevice.h> 10#include <linux/ethtool.h> 11#include <linux/slab.h> 12 13#include "atl1c.h" 14 15static int atl1c_get_link_ksettings(struct net_device *netdev, 16 struct ethtool_link_ksettings *cmd) 17{ 18 struct atl1c_adapter *adapter = netdev_priv(netdev); 19 struct atl1c_hw *hw = &adapter->hw; 20 u32 supported, advertising; 21 22 supported = (SUPPORTED_10baseT_Half | 23 SUPPORTED_10baseT_Full | 24 SUPPORTED_100baseT_Half | 25 SUPPORTED_100baseT_Full | 26 SUPPORTED_Autoneg | 27 SUPPORTED_TP); 28 if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) 29 supported |= SUPPORTED_1000baseT_Full; 30 31 advertising = ADVERTISED_TP; 32 33 advertising |= hw->autoneg_advertised; 34 35 cmd->base.port = PORT_TP; 36 cmd->base.phy_address = 0; 37 38 if (adapter->link_speed != SPEED_0) { 39 cmd->base.speed = adapter->link_speed; 40 if (adapter->link_duplex == FULL_DUPLEX) 41 cmd->base.duplex = DUPLEX_FULL; 42 else 43 cmd->base.duplex = DUPLEX_HALF; 44 } else { 45 cmd->base.speed = SPEED_UNKNOWN; 46 cmd->base.duplex = DUPLEX_UNKNOWN; 47 } 48 49 cmd->base.autoneg = AUTONEG_ENABLE; 50 51 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 52 supported); 53 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 54 advertising); 55 56 return 0; 57} 58 59static int atl1c_set_link_ksettings(struct net_device *netdev, 60 const struct ethtool_link_ksettings *cmd) 61{ 62 struct atl1c_adapter *adapter = netdev_priv(netdev); 63 struct atl1c_hw *hw = &adapter->hw; 64 u16 autoneg_advertised; 65 66 while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) 67 msleep(1); 68 69 if (cmd->base.autoneg == AUTONEG_ENABLE) { 70 autoneg_advertised = ADVERTISED_Autoneg; 71 } else { 72 u32 speed = cmd->base.speed; 73 if (speed == SPEED_1000) { 74 if (cmd->base.duplex != DUPLEX_FULL) { 75 if (netif_msg_link(adapter)) 76 dev_warn(&adapter->pdev->dev, 77 "1000M half is invalid\n"); 78 clear_bit(__AT_RESETTING, &adapter->flags); 79 return -EINVAL; 80 } 81 autoneg_advertised = ADVERTISED_1000baseT_Full; 82 } else if (speed == SPEED_100) { 83 if (cmd->base.duplex == DUPLEX_FULL) 84 autoneg_advertised = ADVERTISED_100baseT_Full; 85 else 86 autoneg_advertised = ADVERTISED_100baseT_Half; 87 } else { 88 if (cmd->base.duplex == DUPLEX_FULL) 89 autoneg_advertised = ADVERTISED_10baseT_Full; 90 else 91 autoneg_advertised = ADVERTISED_10baseT_Half; 92 } 93 } 94 95 if (hw->autoneg_advertised != autoneg_advertised) { 96 hw->autoneg_advertised = autoneg_advertised; 97 if (atl1c_restart_autoneg(hw) != 0) { 98 if (netif_msg_link(adapter)) 99 dev_warn(&adapter->pdev->dev, 100 "ethtool speed/duplex setting failed\n"); 101 clear_bit(__AT_RESETTING, &adapter->flags); 102 return -EINVAL; 103 } 104 } 105 clear_bit(__AT_RESETTING, &adapter->flags); 106 return 0; 107} 108 109static u32 atl1c_get_msglevel(struct net_device *netdev) 110{ 111 struct atl1c_adapter *adapter = netdev_priv(netdev); 112 return adapter->msg_enable; 113} 114 115static void atl1c_set_msglevel(struct net_device *netdev, u32 data) 116{ 117 struct atl1c_adapter *adapter = netdev_priv(netdev); 118 adapter->msg_enable = data; 119} 120 121static int atl1c_get_regs_len(struct net_device *netdev) 122{ 123 return AT_REGS_LEN; 124} 125 126static void atl1c_get_regs(struct net_device *netdev, 127 struct ethtool_regs *regs, void *p) 128{ 129 struct atl1c_adapter *adapter = netdev_priv(netdev); 130 struct atl1c_hw *hw = &adapter->hw; 131 u32 *regs_buff = p; 132 u16 phy_data; 133 134 memset(p, 0, AT_REGS_LEN); 135 136 regs->version = 1; 137 AT_READ_REG(hw, REG_PM_CTRL, p++); 138 AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++); 139 AT_READ_REG(hw, REG_TWSI_CTRL, p++); 140 AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++); 141 AT_READ_REG(hw, REG_MASTER_CTRL, p++); 142 AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++); 143 AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++); 144 AT_READ_REG(hw, REG_GPHY_CTRL, p++); 145 AT_READ_REG(hw, REG_LINK_CTRL, p++); 146 AT_READ_REG(hw, REG_IDLE_STATUS, p++); 147 AT_READ_REG(hw, REG_MDIO_CTRL, p++); 148 AT_READ_REG(hw, REG_SERDES, p++); 149 AT_READ_REG(hw, REG_MAC_CTRL, p++); 150 AT_READ_REG(hw, REG_MAC_IPG_IFG, p++); 151 AT_READ_REG(hw, REG_MAC_STA_ADDR, p++); 152 AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++); 153 AT_READ_REG(hw, REG_RX_HASH_TABLE, p++); 154 AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++); 155 AT_READ_REG(hw, REG_RXQ_CTRL, p++); 156 AT_READ_REG(hw, REG_TXQ_CTRL, p++); 157 AT_READ_REG(hw, REG_MTU, p++); 158 AT_READ_REG(hw, REG_WOL_CTRL, p++); 159 160 atl1c_read_phy_reg(hw, MII_BMCR, &phy_data); 161 regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data; 162 atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); 163 regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data; 164} 165 166static int atl1c_get_eeprom_len(struct net_device *netdev) 167{ 168 struct atl1c_adapter *adapter = netdev_priv(netdev); 169 170 if (atl1c_check_eeprom_exist(&adapter->hw)) 171 return AT_EEPROM_LEN; 172 else 173 return 0; 174} 175 176static int atl1c_get_eeprom(struct net_device *netdev, 177 struct ethtool_eeprom *eeprom, u8 *bytes) 178{ 179 struct atl1c_adapter *adapter = netdev_priv(netdev); 180 struct atl1c_hw *hw = &adapter->hw; 181 u32 *eeprom_buff; 182 int first_dword, last_dword; 183 int ret_val = 0; 184 int i; 185 186 if (eeprom->len == 0) 187 return -EINVAL; 188 189 if (!atl1c_check_eeprom_exist(hw)) /* not exist */ 190 return -EINVAL; 191 192 eeprom->magic = adapter->pdev->vendor | 193 (adapter->pdev->device << 16); 194 195 first_dword = eeprom->offset >> 2; 196 last_dword = (eeprom->offset + eeprom->len - 1) >> 2; 197 198 eeprom_buff = kmalloc_array(last_dword - first_dword + 1, sizeof(u32), 199 GFP_KERNEL); 200 if (eeprom_buff == NULL) 201 return -ENOMEM; 202 203 for (i = first_dword; i < last_dword; i++) { 204 if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { 205 kfree(eeprom_buff); 206 return -EIO; 207 } 208 } 209 210 memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), 211 eeprom->len); 212 kfree(eeprom_buff); 213 214 return ret_val; 215 return 0; 216} 217 218static void atl1c_get_drvinfo(struct net_device *netdev, 219 struct ethtool_drvinfo *drvinfo) 220{ 221 struct atl1c_adapter *adapter = netdev_priv(netdev); 222 223 strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); 224 strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 225 sizeof(drvinfo->bus_info)); 226} 227 228static void atl1c_get_wol(struct net_device *netdev, 229 struct ethtool_wolinfo *wol) 230{ 231 struct atl1c_adapter *adapter = netdev_priv(netdev); 232 233 wol->supported = WAKE_MAGIC | WAKE_PHY; 234 wol->wolopts = 0; 235 236 if (adapter->wol & AT_WUFC_EX) 237 wol->wolopts |= WAKE_UCAST; 238 if (adapter->wol & AT_WUFC_MC) 239 wol->wolopts |= WAKE_MCAST; 240 if (adapter->wol & AT_WUFC_BC) 241 wol->wolopts |= WAKE_BCAST; 242 if (adapter->wol & AT_WUFC_MAG) 243 wol->wolopts |= WAKE_MAGIC; 244 if (adapter->wol & AT_WUFC_LNKC) 245 wol->wolopts |= WAKE_PHY; 246} 247 248static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 249{ 250 struct atl1c_adapter *adapter = netdev_priv(netdev); 251 252 if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | 253 WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) 254 return -EOPNOTSUPP; 255 /* these settings will always override what we currently have */ 256 adapter->wol = 0; 257 258 if (wol->wolopts & WAKE_MAGIC) 259 adapter->wol |= AT_WUFC_MAG; 260 if (wol->wolopts & WAKE_PHY) 261 adapter->wol |= AT_WUFC_LNKC; 262 263 device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); 264 265 return 0; 266} 267 268static int atl1c_nway_reset(struct net_device *netdev) 269{ 270 struct atl1c_adapter *adapter = netdev_priv(netdev); 271 if (netif_running(netdev)) 272 atl1c_reinit_locked(adapter); 273 return 0; 274} 275 276static const struct ethtool_ops atl1c_ethtool_ops = { 277 .get_drvinfo = atl1c_get_drvinfo, 278 .get_regs_len = atl1c_get_regs_len, 279 .get_regs = atl1c_get_regs, 280 .get_wol = atl1c_get_wol, 281 .set_wol = atl1c_set_wol, 282 .get_msglevel = atl1c_get_msglevel, 283 .set_msglevel = atl1c_set_msglevel, 284 .nway_reset = atl1c_nway_reset, 285 .get_link = ethtool_op_get_link, 286 .get_eeprom_len = atl1c_get_eeprom_len, 287 .get_eeprom = atl1c_get_eeprom, 288 .get_link_ksettings = atl1c_get_link_ksettings, 289 .set_link_ksettings = atl1c_set_link_ksettings, 290}; 291 292void atl1c_set_ethtool_ops(struct net_device *netdev) 293{ 294 netdev->ethtool_ops = &atl1c_ethtool_ops; 295}