1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright(c) 2007 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 "atl1e.h" |
14 | |
15 | static int atl1e_get_link_ksettings(struct net_device *netdev, |
16 | struct ethtool_link_ksettings *cmd) |
17 | { |
18 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
19 | struct atl1e_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->nic_type == athr_l1e) |
29 | supported |= SUPPORTED_1000baseT_Full; |
30 | |
31 | advertising = ADVERTISED_TP; |
32 | |
33 | advertising |= ADVERTISED_Autoneg; |
34 | advertising |= hw->autoneg_advertised; |
35 | |
36 | cmd->base.port = PORT_TP; |
37 | cmd->base.phy_address = 0; |
38 | |
39 | if (adapter->link_speed != SPEED_0) { |
40 | cmd->base.speed = adapter->link_speed; |
41 | if (adapter->link_duplex == FULL_DUPLEX) |
42 | cmd->base.duplex = DUPLEX_FULL; |
43 | else |
44 | cmd->base.duplex = DUPLEX_HALF; |
45 | } else { |
46 | cmd->base.speed = SPEED_UNKNOWN; |
47 | cmd->base.duplex = DUPLEX_UNKNOWN; |
48 | } |
49 | |
50 | cmd->base.autoneg = AUTONEG_ENABLE; |
51 | |
52 | ethtool_convert_legacy_u32_to_link_mode(dst: cmd->link_modes.supported, |
53 | legacy_u32: supported); |
54 | ethtool_convert_legacy_u32_to_link_mode(dst: cmd->link_modes.advertising, |
55 | legacy_u32: advertising); |
56 | |
57 | return 0; |
58 | } |
59 | |
60 | static int atl1e_set_link_ksettings(struct net_device *netdev, |
61 | const struct ethtool_link_ksettings *cmd) |
62 | { |
63 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
64 | struct atl1e_hw *hw = &adapter->hw; |
65 | u32 advertising; |
66 | |
67 | ethtool_convert_link_mode_to_legacy_u32(legacy_u32: &advertising, |
68 | src: cmd->link_modes.advertising); |
69 | |
70 | while (test_and_set_bit(__AT_RESETTING, addr: &adapter->flags)) |
71 | msleep(msecs: 1); |
72 | |
73 | if (cmd->base.autoneg == AUTONEG_ENABLE) { |
74 | u16 adv4, adv9; |
75 | |
76 | if (advertising & ADVERTISE_1000_FULL) { |
77 | if (hw->nic_type == athr_l1e) { |
78 | hw->autoneg_advertised = |
79 | advertising & AT_ADV_MASK; |
80 | } else { |
81 | clear_bit(__AT_RESETTING, addr: &adapter->flags); |
82 | return -EINVAL; |
83 | } |
84 | } else if (advertising & ADVERTISE_1000_HALF) { |
85 | clear_bit(__AT_RESETTING, addr: &adapter->flags); |
86 | return -EINVAL; |
87 | } else { |
88 | hw->autoneg_advertised = |
89 | advertising & AT_ADV_MASK; |
90 | } |
91 | advertising = hw->autoneg_advertised | |
92 | ADVERTISED_TP | ADVERTISED_Autoneg; |
93 | |
94 | adv4 = hw->mii_autoneg_adv_reg & ~ADVERTISE_ALL; |
95 | adv9 = hw->mii_1000t_ctrl_reg & ~MII_AT001_CR_1000T_SPEED_MASK; |
96 | if (hw->autoneg_advertised & ADVERTISE_10_HALF) |
97 | adv4 |= ADVERTISE_10HALF; |
98 | if (hw->autoneg_advertised & ADVERTISE_10_FULL) |
99 | adv4 |= ADVERTISE_10FULL; |
100 | if (hw->autoneg_advertised & ADVERTISE_100_HALF) |
101 | adv4 |= ADVERTISE_100HALF; |
102 | if (hw->autoneg_advertised & ADVERTISE_100_FULL) |
103 | adv4 |= ADVERTISE_100FULL; |
104 | if (hw->autoneg_advertised & ADVERTISE_1000_FULL) |
105 | adv9 |= ADVERTISE_1000FULL; |
106 | |
107 | if (adv4 != hw->mii_autoneg_adv_reg || |
108 | adv9 != hw->mii_1000t_ctrl_reg) { |
109 | hw->mii_autoneg_adv_reg = adv4; |
110 | hw->mii_1000t_ctrl_reg = adv9; |
111 | hw->re_autoneg = true; |
112 | } |
113 | |
114 | } else { |
115 | clear_bit(__AT_RESETTING, addr: &adapter->flags); |
116 | return -EINVAL; |
117 | } |
118 | |
119 | /* reset the link */ |
120 | |
121 | if (netif_running(dev: adapter->netdev)) { |
122 | atl1e_down(adapter); |
123 | atl1e_up(adapter); |
124 | } else |
125 | atl1e_reset_hw(hw: &adapter->hw); |
126 | |
127 | clear_bit(__AT_RESETTING, addr: &adapter->flags); |
128 | return 0; |
129 | } |
130 | |
131 | static u32 atl1e_get_msglevel(struct net_device *netdev) |
132 | { |
133 | #ifdef DBG |
134 | return 1; |
135 | #else |
136 | return 0; |
137 | #endif |
138 | } |
139 | |
140 | static int atl1e_get_regs_len(struct net_device *netdev) |
141 | { |
142 | return AT_REGS_LEN * sizeof(u32); |
143 | } |
144 | |
145 | static void atl1e_get_regs(struct net_device *netdev, |
146 | struct ethtool_regs *regs, void *p) |
147 | { |
148 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
149 | struct atl1e_hw *hw = &adapter->hw; |
150 | u32 *regs_buff = p; |
151 | u16 phy_data; |
152 | |
153 | memset(p, 0, AT_REGS_LEN * sizeof(u32)); |
154 | |
155 | regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; |
156 | |
157 | regs_buff[0] = AT_READ_REG(hw, REG_VPD_CAP); |
158 | regs_buff[1] = AT_READ_REG(hw, REG_SPI_FLASH_CTRL); |
159 | regs_buff[2] = AT_READ_REG(hw, REG_SPI_FLASH_CONFIG); |
160 | regs_buff[3] = AT_READ_REG(hw, REG_TWSI_CTRL); |
161 | regs_buff[4] = AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL); |
162 | regs_buff[5] = AT_READ_REG(hw, REG_MASTER_CTRL); |
163 | regs_buff[6] = AT_READ_REG(hw, REG_MANUAL_TIMER_INIT); |
164 | regs_buff[7] = AT_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT); |
165 | regs_buff[8] = AT_READ_REG(hw, REG_GPHY_CTRL); |
166 | regs_buff[9] = AT_READ_REG(hw, REG_CMBDISDMA_TIMER); |
167 | regs_buff[10] = AT_READ_REG(hw, REG_IDLE_STATUS); |
168 | regs_buff[11] = AT_READ_REG(hw, REG_MDIO_CTRL); |
169 | regs_buff[12] = AT_READ_REG(hw, REG_SERDES_LOCK); |
170 | regs_buff[13] = AT_READ_REG(hw, REG_MAC_CTRL); |
171 | regs_buff[14] = AT_READ_REG(hw, REG_MAC_IPG_IFG); |
172 | regs_buff[15] = AT_READ_REG(hw, REG_MAC_STA_ADDR); |
173 | regs_buff[16] = AT_READ_REG(hw, REG_MAC_STA_ADDR+4); |
174 | regs_buff[17] = AT_READ_REG(hw, REG_RX_HASH_TABLE); |
175 | regs_buff[18] = AT_READ_REG(hw, REG_RX_HASH_TABLE+4); |
176 | regs_buff[19] = AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL); |
177 | regs_buff[20] = AT_READ_REG(hw, REG_MTU); |
178 | regs_buff[21] = AT_READ_REG(hw, REG_WOL_CTRL); |
179 | regs_buff[22] = AT_READ_REG(hw, REG_SRAM_TRD_ADDR); |
180 | regs_buff[23] = AT_READ_REG(hw, REG_SRAM_TRD_LEN); |
181 | regs_buff[24] = AT_READ_REG(hw, REG_SRAM_RXF_ADDR); |
182 | regs_buff[25] = AT_READ_REG(hw, REG_SRAM_RXF_LEN); |
183 | regs_buff[26] = AT_READ_REG(hw, REG_SRAM_TXF_ADDR); |
184 | regs_buff[27] = AT_READ_REG(hw, REG_SRAM_TXF_LEN); |
185 | regs_buff[28] = AT_READ_REG(hw, REG_SRAM_TCPH_ADDR); |
186 | regs_buff[29] = AT_READ_REG(hw, REG_SRAM_PKTH_ADDR); |
187 | |
188 | atl1e_read_phy_reg(hw, MII_BMCR, phy_data: &phy_data); |
189 | regs_buff[73] = (u32)phy_data; |
190 | atl1e_read_phy_reg(hw, MII_BMSR, phy_data: &phy_data); |
191 | regs_buff[74] = (u32)phy_data; |
192 | } |
193 | |
194 | static int atl1e_get_eeprom_len(struct net_device *netdev) |
195 | { |
196 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
197 | |
198 | if (!atl1e_check_eeprom_exist(hw: &adapter->hw)) |
199 | return AT_EEPROM_LEN; |
200 | else |
201 | return 0; |
202 | } |
203 | |
204 | static int atl1e_get_eeprom(struct net_device *netdev, |
205 | struct ethtool_eeprom *eeprom, u8 *bytes) |
206 | { |
207 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
208 | struct atl1e_hw *hw = &adapter->hw; |
209 | u32 *eeprom_buff; |
210 | int first_dword, last_dword; |
211 | int ret_val = 0; |
212 | int i; |
213 | |
214 | if (eeprom->len == 0) |
215 | return -EINVAL; |
216 | |
217 | if (atl1e_check_eeprom_exist(hw)) /* not exist */ |
218 | return -EINVAL; |
219 | |
220 | eeprom->magic = hw->vendor_id | (hw->device_id << 16); |
221 | |
222 | first_dword = eeprom->offset >> 2; |
223 | last_dword = (eeprom->offset + eeprom->len - 1) >> 2; |
224 | |
225 | eeprom_buff = kmalloc_array(n: last_dword - first_dword + 1, size: sizeof(u32), |
226 | GFP_KERNEL); |
227 | if (eeprom_buff == NULL) |
228 | return -ENOMEM; |
229 | |
230 | for (i = first_dword; i < last_dword; i++) { |
231 | if (!atl1e_read_eeprom(hw, offset: i * 4, p_value: &(eeprom_buff[i-first_dword]))) { |
232 | kfree(objp: eeprom_buff); |
233 | return -EIO; |
234 | } |
235 | } |
236 | |
237 | memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), |
238 | eeprom->len); |
239 | kfree(objp: eeprom_buff); |
240 | |
241 | return ret_val; |
242 | } |
243 | |
244 | static int atl1e_set_eeprom(struct net_device *netdev, |
245 | struct ethtool_eeprom *eeprom, u8 *bytes) |
246 | { |
247 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
248 | struct atl1e_hw *hw = &adapter->hw; |
249 | u32 *eeprom_buff; |
250 | u32 *ptr; |
251 | int first_dword, last_dword; |
252 | int ret_val = 0; |
253 | int i; |
254 | |
255 | if (eeprom->len == 0) |
256 | return -EOPNOTSUPP; |
257 | |
258 | if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) |
259 | return -EINVAL; |
260 | |
261 | first_dword = eeprom->offset >> 2; |
262 | last_dword = (eeprom->offset + eeprom->len - 1) >> 2; |
263 | eeprom_buff = kmalloc(AT_EEPROM_LEN, GFP_KERNEL); |
264 | if (eeprom_buff == NULL) |
265 | return -ENOMEM; |
266 | |
267 | ptr = eeprom_buff; |
268 | |
269 | if (eeprom->offset & 3) { |
270 | /* need read/modify/write of first changed EEPROM word */ |
271 | /* only the second byte of the word is being modified */ |
272 | if (!atl1e_read_eeprom(hw, offset: first_dword * 4, p_value: &(eeprom_buff[0]))) { |
273 | ret_val = -EIO; |
274 | goto out; |
275 | } |
276 | ptr++; |
277 | } |
278 | if (((eeprom->offset + eeprom->len) & 3)) { |
279 | /* need read/modify/write of last changed EEPROM word */ |
280 | /* only the first byte of the word is being modified */ |
281 | |
282 | if (!atl1e_read_eeprom(hw, offset: last_dword * 4, |
283 | p_value: &(eeprom_buff[last_dword - first_dword]))) { |
284 | ret_val = -EIO; |
285 | goto out; |
286 | } |
287 | } |
288 | |
289 | /* Device's eeprom is always little-endian, word addressable */ |
290 | memcpy(ptr, bytes, eeprom->len); |
291 | |
292 | for (i = 0; i < last_dword - first_dword + 1; i++) { |
293 | if (!atl1e_write_eeprom(hw, offset: ((first_dword + i) * 4), |
294 | value: eeprom_buff[i])) { |
295 | ret_val = -EIO; |
296 | goto out; |
297 | } |
298 | } |
299 | out: |
300 | kfree(objp: eeprom_buff); |
301 | return ret_val; |
302 | } |
303 | |
304 | static void atl1e_get_drvinfo(struct net_device *netdev, |
305 | struct ethtool_drvinfo *drvinfo) |
306 | { |
307 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
308 | |
309 | strscpy(drvinfo->driver, atl1e_driver_name, sizeof(drvinfo->driver)); |
310 | strscpy(drvinfo->fw_version, "L1e" , sizeof(drvinfo->fw_version)); |
311 | strscpy(drvinfo->bus_info, pci_name(adapter->pdev), |
312 | sizeof(drvinfo->bus_info)); |
313 | } |
314 | |
315 | static void atl1e_get_wol(struct net_device *netdev, |
316 | struct ethtool_wolinfo *wol) |
317 | { |
318 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
319 | |
320 | wol->supported = WAKE_MAGIC | WAKE_PHY; |
321 | wol->wolopts = 0; |
322 | |
323 | if (adapter->wol & AT_WUFC_EX) |
324 | wol->wolopts |= WAKE_UCAST; |
325 | if (adapter->wol & AT_WUFC_MC) |
326 | wol->wolopts |= WAKE_MCAST; |
327 | if (adapter->wol & AT_WUFC_BC) |
328 | wol->wolopts |= WAKE_BCAST; |
329 | if (adapter->wol & AT_WUFC_MAG) |
330 | wol->wolopts |= WAKE_MAGIC; |
331 | if (adapter->wol & AT_WUFC_LNKC) |
332 | wol->wolopts |= WAKE_PHY; |
333 | } |
334 | |
335 | static int atl1e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) |
336 | { |
337 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
338 | |
339 | if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | |
340 | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)) |
341 | return -EOPNOTSUPP; |
342 | /* these settings will always override what we currently have */ |
343 | adapter->wol = 0; |
344 | |
345 | if (wol->wolopts & WAKE_MAGIC) |
346 | adapter->wol |= AT_WUFC_MAG; |
347 | if (wol->wolopts & WAKE_PHY) |
348 | adapter->wol |= AT_WUFC_LNKC; |
349 | |
350 | device_set_wakeup_enable(dev: &adapter->pdev->dev, enable: adapter->wol); |
351 | |
352 | return 0; |
353 | } |
354 | |
355 | static int atl1e_nway_reset(struct net_device *netdev) |
356 | { |
357 | struct atl1e_adapter *adapter = netdev_priv(dev: netdev); |
358 | if (netif_running(dev: netdev)) |
359 | atl1e_reinit_locked(adapter); |
360 | return 0; |
361 | } |
362 | |
363 | static const struct ethtool_ops atl1e_ethtool_ops = { |
364 | .get_drvinfo = atl1e_get_drvinfo, |
365 | .get_regs_len = atl1e_get_regs_len, |
366 | .get_regs = atl1e_get_regs, |
367 | .get_wol = atl1e_get_wol, |
368 | .set_wol = atl1e_set_wol, |
369 | .get_msglevel = atl1e_get_msglevel, |
370 | .nway_reset = atl1e_nway_reset, |
371 | .get_link = ethtool_op_get_link, |
372 | .get_eeprom_len = atl1e_get_eeprom_len, |
373 | .get_eeprom = atl1e_get_eeprom, |
374 | .set_eeprom = atl1e_set_eeprom, |
375 | .get_link_ksettings = atl1e_get_link_ksettings, |
376 | .set_link_ksettings = atl1e_set_link_ksettings, |
377 | }; |
378 | |
379 | void atl1e_set_ethtool_ops(struct net_device *netdev) |
380 | { |
381 | netdev->ethtool_ops = &atl1e_ethtool_ops; |
382 | } |
383 | |