xref: /linux/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c (revision f9bff0e31881d03badf191d3b0005839391f5f2b)
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 
15 static 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 
59 static 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 
109 static u32 atl1c_get_msglevel(struct net_device *netdev)
110 {
111 	struct atl1c_adapter *adapter = netdev_priv(netdev);
112 	return adapter->msg_enable;
113 }
114 
115 static 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 
121 static int atl1c_get_regs_len(struct net_device *netdev)
122 {
123 	return AT_REGS_LEN;
124 }
125 
126 static 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 
166 static 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 
176 static 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 
218 static void atl1c_get_drvinfo(struct net_device *netdev,
219 		struct ethtool_drvinfo *drvinfo)
220 {
221 	struct atl1c_adapter *adapter = netdev_priv(netdev);
222 
223 	strscpy(drvinfo->driver,  atl1c_driver_name, sizeof(drvinfo->driver));
224 	strscpy(drvinfo->bus_info, pci_name(adapter->pdev),
225 		sizeof(drvinfo->bus_info));
226 }
227 
228 static 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 
248 static 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 
268 static 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 
276 static 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 
292 void atl1c_set_ethtool_ops(struct net_device *netdev)
293 {
294 	netdev->ethtool_ops = &atl1c_ethtool_ops;
295 }
296