11a59d1b8SThomas Gleixner// SPDX-License-Identifier: GPL-2.0-or-later
243250dddSJie Yang/*
343250dddSJie Yang * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved.
443250dddSJie Yang *
543250dddSJie Yang * Derived from Intel e1000 driver
643250dddSJie Yang * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
743250dddSJie Yang */
843250dddSJie Yang
943250dddSJie Yang#include <linux/netdevice.h>
1043250dddSJie Yang#include <linux/ethtool.h>
115a0e3ad6STejun Heo#include <linux/slab.h>
1243250dddSJie Yang
1343250dddSJie Yang#include "atl1c.h"
1443250dddSJie Yang
1558046c70SPhilippe Reynesstatic int atl1c_get_link_ksettings(struct net_device *netdev,
1658046c70SPhilippe Reynes				    struct ethtool_link_ksettings *cmd)
1743250dddSJie Yang{
1843250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
1943250dddSJie Yang	struct atl1c_hw *hw = &adapter->hw;
2058046c70SPhilippe Reynes	u32 supported, advertising;
2143250dddSJie Yang
2258046c70SPhilippe Reynes	supported = (SUPPORTED_10baseT_Half  |
2343250dddSJie Yang			   SUPPORTED_10baseT_Full  |
2443250dddSJie Yang			   SUPPORTED_100baseT_Half |
2543250dddSJie Yang			   SUPPORTED_100baseT_Full |
2643250dddSJie Yang			   SUPPORTED_Autoneg       |
2743250dddSJie Yang			   SUPPORTED_TP);
28496c185cSLuis R. Rodriguez	if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M)
2958046c70SPhilippe Reynes		supported |= SUPPORTED_1000baseT_Full;
3043250dddSJie Yang
3158046c70SPhilippe Reynes	advertising = ADVERTISED_TP;
3243250dddSJie Yang
3358046c70SPhilippe Reynes	advertising |= hw->autoneg_advertised;
3443250dddSJie Yang
3558046c70SPhilippe Reynes	cmd->base.port = PORT_TP;
3658046c70SPhilippe Reynes	cmd->base.phy_address = 0;
3743250dddSJie Yang
3843250dddSJie Yang	if (adapter->link_speed != SPEED_0) {
3958046c70SPhilippe Reynes		cmd->base.speed = adapter->link_speed;
4043250dddSJie Yang		if (adapter->link_duplex == FULL_DUPLEX)
4158046c70SPhilippe Reynes			cmd->base.duplex = DUPLEX_FULL;
4243250dddSJie Yang		else
4358046c70SPhilippe Reynes			cmd->base.duplex = DUPLEX_HALF;
4443250dddSJie Yang	} else {
4558046c70SPhilippe Reynes		cmd->base.speed = SPEED_UNKNOWN;
4658046c70SPhilippe Reynes		cmd->base.duplex = DUPLEX_UNKNOWN;
4743250dddSJie Yang	}
4843250dddSJie Yang
4958046c70SPhilippe Reynes	cmd->base.autoneg = AUTONEG_ENABLE;
5058046c70SPhilippe Reynes
5158046c70SPhilippe Reynes	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
5258046c70SPhilippe Reynes						supported);
5358046c70SPhilippe Reynes	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
5458046c70SPhilippe Reynes						advertising);
5558046c70SPhilippe Reynes
5643250dddSJie Yang	return 0;
5743250dddSJie Yang}
5843250dddSJie Yang
5958046c70SPhilippe Reynesstatic int atl1c_set_link_ksettings(struct net_device *netdev,
6058046c70SPhilippe Reynes				    const struct ethtool_link_ksettings *cmd)
6143250dddSJie Yang{
6243250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
6343250dddSJie Yang	struct atl1c_hw *hw = &adapter->hw;
6443250dddSJie Yang	u16  autoneg_advertised;
6543250dddSJie Yang
6643250dddSJie Yang	while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
6743250dddSJie Yang		msleep(1);
6843250dddSJie Yang
6958046c70SPhilippe Reynes	if (cmd->base.autoneg == AUTONEG_ENABLE) {
7043250dddSJie Yang		autoneg_advertised = ADVERTISED_Autoneg;
7143250dddSJie Yang	} else {
7258046c70SPhilippe Reynes		u32 speed = cmd->base.speed;
7325db0338SDavid Decotigny		if (speed == SPEED_1000) {
7458046c70SPhilippe Reynes			if (cmd->base.duplex != DUPLEX_FULL) {
7543250dddSJie Yang				if (netif_msg_link(adapter))
7643250dddSJie Yang					dev_warn(&adapter->pdev->dev,
7743250dddSJie Yang						"1000M half is invalid\n");
7843250dddSJie Yang				clear_bit(__AT_RESETTING, &adapter->flags);
7943250dddSJie Yang				return -EINVAL;
8043250dddSJie Yang			}
8143250dddSJie Yang			autoneg_advertised = ADVERTISED_1000baseT_Full;
8225db0338SDavid Decotigny		} else if (speed == SPEED_100) {
8358046c70SPhilippe Reynes			if (cmd->base.duplex == DUPLEX_FULL)
8443250dddSJie Yang				autoneg_advertised = ADVERTISED_100baseT_Full;
8543250dddSJie Yang			else
8643250dddSJie Yang				autoneg_advertised = ADVERTISED_100baseT_Half;
8743250dddSJie Yang		} else {
8858046c70SPhilippe Reynes			if (cmd->base.duplex == DUPLEX_FULL)
8943250dddSJie Yang				autoneg_advertised = ADVERTISED_10baseT_Full;
9043250dddSJie Yang			else
9143250dddSJie Yang				autoneg_advertised = ADVERTISED_10baseT_Half;
9243250dddSJie Yang		}
9343250dddSJie Yang	}
9443250dddSJie Yang
9543250dddSJie Yang	if (hw->autoneg_advertised != autoneg_advertised) {
9643250dddSJie Yang		hw->autoneg_advertised = autoneg_advertised;
9743250dddSJie Yang		if (atl1c_restart_autoneg(hw) != 0) {
9843250dddSJie Yang			if (netif_msg_link(adapter))
9943250dddSJie Yang				dev_warn(&adapter->pdev->dev,
10043250dddSJie Yang					"ethtool speed/duplex setting failed\n");
10143250dddSJie Yang			clear_bit(__AT_RESETTING, &adapter->flags);
10243250dddSJie Yang			return -EINVAL;
10343250dddSJie Yang		}
10443250dddSJie Yang	}
10543250dddSJie Yang	clear_bit(__AT_RESETTING, &adapter->flags);
10643250dddSJie Yang	return 0;
10743250dddSJie Yang}
10843250dddSJie Yang
10943250dddSJie Yangstatic u32 atl1c_get_msglevel(struct net_device *netdev)
11043250dddSJie Yang{
11143250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
11243250dddSJie Yang	return adapter->msg_enable;
11343250dddSJie Yang}
11443250dddSJie Yang
11543250dddSJie Yangstatic void atl1c_set_msglevel(struct net_device *netdev, u32 data)
11643250dddSJie Yang{
11743250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
11843250dddSJie Yang	adapter->msg_enable = data;
11943250dddSJie Yang}
12043250dddSJie Yang
12143250dddSJie Yangstatic int atl1c_get_regs_len(struct net_device *netdev)
12243250dddSJie Yang{
12343250dddSJie Yang	return AT_REGS_LEN;
12443250dddSJie Yang}
12543250dddSJie Yang
12643250dddSJie Yangstatic void atl1c_get_regs(struct net_device *netdev,
12743250dddSJie Yang			   struct ethtool_regs *regs, void *p)
12843250dddSJie Yang{
12943250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
13043250dddSJie Yang	struct atl1c_hw *hw = &adapter->hw;
13143250dddSJie Yang	u32 *regs_buff = p;
13243250dddSJie Yang	u16 phy_data;
13343250dddSJie Yang
13443250dddSJie Yang	memset(p, 0, AT_REGS_LEN);
13543250dddSJie Yang
136864ad85fSHuang, Xiong	regs->version = 1;
13743250dddSJie Yang	AT_READ_REG(hw, REG_PM_CTRL, 		  p++);
13843250dddSJie Yang	AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL,  p++);
13943250dddSJie Yang	AT_READ_REG(hw, REG_TWSI_CTRL, 		  p++);
14043250dddSJie Yang	AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL,   p++);
14143250dddSJie Yang	AT_READ_REG(hw, REG_MASTER_CTRL, 	  p++);
14243250dddSJie Yang	AT_READ_REG(hw, REG_MANUAL_TIMER_INIT,    p++);
14343250dddSJie Yang	AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++);
14443250dddSJie Yang	AT_READ_REG(hw, REG_GPHY_CTRL, 		  p++);
14543250dddSJie Yang	AT_READ_REG(hw, REG_LINK_CTRL, 		  p++);
14643250dddSJie Yang	AT_READ_REG(hw, REG_IDLE_STATUS, 	  p++);
14743250dddSJie Yang	AT_READ_REG(hw, REG_MDIO_CTRL, 		  p++);
1487737fd96SHuang, Xiong	AT_READ_REG(hw, REG_SERDES,		  p++);
14943250dddSJie Yang	AT_READ_REG(hw, REG_MAC_CTRL, 		  p++);
15043250dddSJie Yang	AT_READ_REG(hw, REG_MAC_IPG_IFG, 	  p++);
15143250dddSJie Yang	AT_READ_REG(hw, REG_MAC_STA_ADDR, 	  p++);
15243250dddSJie Yang	AT_READ_REG(hw, REG_MAC_STA_ADDR+4, 	  p++);
15343250dddSJie Yang	AT_READ_REG(hw, REG_RX_HASH_TABLE, 	  p++);
15443250dddSJie Yang	AT_READ_REG(hw, REG_RX_HASH_TABLE+4, 	  p++);
15543250dddSJie Yang	AT_READ_REG(hw, REG_RXQ_CTRL, 		  p++);
15643250dddSJie Yang	AT_READ_REG(hw, REG_TXQ_CTRL, 		  p++);
15743250dddSJie Yang	AT_READ_REG(hw, REG_MTU, 		  p++);
15843250dddSJie Yang	AT_READ_REG(hw, REG_WOL_CTRL, 		  p++);
15943250dddSJie Yang
16043250dddSJie Yang	atl1c_read_phy_reg(hw, MII_BMCR, &phy_data);
161864ad85fSHuang, Xiong	regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data;
16243250dddSJie Yang	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
163864ad85fSHuang, Xiong	regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data;
16443250dddSJie Yang}
16543250dddSJie Yang
16643250dddSJie Yangstatic int atl1c_get_eeprom_len(struct net_device *netdev)
16743250dddSJie Yang{
16843250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
16943250dddSJie Yang
17043250dddSJie Yang	if (atl1c_check_eeprom_exist(&adapter->hw))
17143250dddSJie Yang		return AT_EEPROM_LEN;
17243250dddSJie Yang	else
17343250dddSJie Yang		return 0;
17443250dddSJie Yang}
17543250dddSJie Yang
17643250dddSJie Yangstatic int atl1c_get_eeprom(struct net_device *netdev,
17743250dddSJie Yang		struct ethtool_eeprom *eeprom, u8 *bytes)
17843250dddSJie Yang{
17943250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
18043250dddSJie Yang	struct atl1c_hw *hw = &adapter->hw;
18143250dddSJie Yang	u32 *eeprom_buff;
18243250dddSJie Yang	int first_dword, last_dword;
18343250dddSJie Yang	int ret_val = 0;
18443250dddSJie Yang	int i;
18543250dddSJie Yang
18643250dddSJie Yang	if (eeprom->len == 0)
18743250dddSJie Yang		return -EINVAL;
18843250dddSJie Yang
18943250dddSJie Yang	if (!atl1c_check_eeprom_exist(hw)) /* not exist */
19043250dddSJie Yang		return -EINVAL;
19143250dddSJie Yang
19243250dddSJie Yang	eeprom->magic = adapter->pdev->vendor |
19343250dddSJie Yang			(adapter->pdev->device << 16);
19443250dddSJie Yang
19543250dddSJie Yang	first_dword = eeprom->offset >> 2;
19643250dddSJie Yang	last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
19743250dddSJie Yang
1986da2ec56SKees Cook	eeprom_buff = kmalloc_array(last_dword - first_dword + 1, sizeof(u32),
1996da2ec56SKees Cook				    GFP_KERNEL);
20043250dddSJie Yang	if (eeprom_buff == NULL)
20143250dddSJie Yang		return -ENOMEM;
20243250dddSJie Yang
20343250dddSJie Yang	for (i = first_dword; i < last_dword; i++) {
20443250dddSJie Yang		if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {
20543250dddSJie Yang			kfree(eeprom_buff);
20643250dddSJie Yang			return -EIO;
20743250dddSJie Yang		}
20843250dddSJie Yang	}
20943250dddSJie Yang
21043250dddSJie Yang	memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
21143250dddSJie Yang			eeprom->len);
21243250dddSJie Yang	kfree(eeprom_buff);
21343250dddSJie Yang
21443250dddSJie Yang	return ret_val;
21543250dddSJie Yang	return 0;
21643250dddSJie Yang}
21743250dddSJie Yang
21843250dddSJie Yangstatic void atl1c_get_drvinfo(struct net_device *netdev,
21943250dddSJie Yang		struct ethtool_drvinfo *drvinfo)
22043250dddSJie Yang{
22143250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
22243250dddSJie Yang
223082ba88aSRoel Kluin	strlcpy(drvinfo->driver,  atl1c_driver_name, sizeof(drvinfo->driver));
224082ba88aSRoel Kluin	strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
22543250dddSJie Yang		sizeof(drvinfo->bus_info));
22643250dddSJie Yang}
22743250dddSJie Yang
22843250dddSJie Yangstatic void atl1c_get_wol(struct net_device *netdev,
22943250dddSJie Yang			  struct ethtool_wolinfo *wol)
23043250dddSJie Yang{
23143250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
23243250dddSJie Yang
23343250dddSJie Yang	wol->supported = WAKE_MAGIC | WAKE_PHY;
23443250dddSJie Yang	wol->wolopts = 0;
23543250dddSJie Yang
23643250dddSJie Yang	if (adapter->wol & AT_WUFC_EX)
23743250dddSJie Yang		wol->wolopts |= WAKE_UCAST;
23843250dddSJie Yang	if (adapter->wol & AT_WUFC_MC)
23943250dddSJie Yang		wol->wolopts |= WAKE_MCAST;
24043250dddSJie Yang	if (adapter->wol & AT_WUFC_BC)
24143250dddSJie Yang		wol->wolopts |= WAKE_BCAST;
24243250dddSJie Yang	if (adapter->wol & AT_WUFC_MAG)
24343250dddSJie Yang		wol->wolopts |= WAKE_MAGIC;
24443250dddSJie Yang	if (adapter->wol & AT_WUFC_LNKC)
24543250dddSJie Yang		wol->wolopts |= WAKE_PHY;
24643250dddSJie Yang}
24743250dddSJie Yang
24843250dddSJie Yangstatic int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
24943250dddSJie Yang{
25043250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
25143250dddSJie Yang
25243250dddSJie Yang	if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
2530ed586d0SRoel Kluin			    WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
25443250dddSJie Yang		return -EOPNOTSUPP;
25543250dddSJie Yang	/* these settings will always override what we currently have */
25643250dddSJie Yang	adapter->wol = 0;
25743250dddSJie Yang
25843250dddSJie Yang	if (wol->wolopts & WAKE_MAGIC)
25943250dddSJie Yang		adapter->wol |= AT_WUFC_MAG;
26043250dddSJie Yang	if (wol->wolopts & WAKE_PHY)
26143250dddSJie Yang		adapter->wol |= AT_WUFC_LNKC;
26243250dddSJie Yang
263d8146bb2SBrandon Philips	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
264d8146bb2SBrandon Philips
26543250dddSJie Yang	return 0;
26643250dddSJie Yang}
26743250dddSJie Yang
26843250dddSJie Yangstatic int atl1c_nway_reset(struct net_device *netdev)
26943250dddSJie Yang{
27043250dddSJie Yang	struct atl1c_adapter *adapter = netdev_priv(netdev);
27143250dddSJie Yang	if (netif_running(netdev))
27243250dddSJie Yang		atl1c_reinit_locked(adapter);
27343250dddSJie Yang	return 0;
27443250dddSJie Yang}
27543250dddSJie Yang
2760fc0b732SStephen Hemmingerstatic const struct ethtool_ops atl1c_ethtool_ops = {
27743250dddSJie Yang	.get_drvinfo            = atl1c_get_drvinfo,
27843250dddSJie Yang	.get_regs_len           = atl1c_get_regs_len,
27943250dddSJie Yang	.get_regs               = atl1c_get_regs,
28043250dddSJie Yang	.get_wol                = atl1c_get_wol,
28143250dddSJie Yang	.set_wol                = atl1c_set_wol,
28243250dddSJie Yang	.get_msglevel           = atl1c_get_msglevel,
28343250dddSJie Yang	.set_msglevel           = atl1c_set_msglevel,
28443250dddSJie Yang	.nway_reset             = atl1c_nway_reset,
28543250dddSJie Yang	.get_link               = ethtool_op_get_link,
28643250dddSJie Yang	.get_eeprom_len         = atl1c_get_eeprom_len,
28743250dddSJie Yang	.get_eeprom             = atl1c_get_eeprom,
28858046c70SPhilippe Reynes	.get_link_ksettings     = atl1c_get_link_ksettings,
28958046c70SPhilippe Reynes	.set_link_ksettings     = atl1c_set_link_ksettings,
29043250dddSJie Yang};
29143250dddSJie Yang
29243250dddSJie Yangvoid atl1c_set_ethtool_ops(struct net_device *netdev)
29343250dddSJie Yang{
2947ad24ea4SWilfried Klaebe	netdev->ethtool_ops = &atl1c_ethtool_ops;
29543250dddSJie Yang}
296