linux/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
<<
>>
Prefs
   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}
 296