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