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