diff options
Diffstat (limited to 'drivers/net/atl1e/atl1e_ethtool.c')
-rw-r--r-- | drivers/net/atl1e/atl1e_ethtool.c | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/drivers/net/atl1e/atl1e_ethtool.c b/drivers/net/atl1e/atl1e_ethtool.c new file mode 100644 index 00000000000..6269438d365 --- /dev/null +++ b/drivers/net/atl1e/atl1e_ethtool.c | |||
@@ -0,0 +1,390 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2007 Atheros Corporation. All rights reserved. | ||
3 | * | ||
4 | * Derived from Intel e1000 driver | ||
5 | * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the Free | ||
9 | * Software Foundation; either version 2 of the License, or (at your option) | ||
10 | * any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along with | ||
18 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
19 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/netdevice.h> | ||
24 | #include <linux/ethtool.h> | ||
25 | #include <linux/slab.h> | ||
26 | |||
27 | #include "atl1e.h" | ||
28 | |||
29 | static int atl1e_get_settings(struct net_device *netdev, | ||
30 | struct ethtool_cmd *ecmd) | ||
31 | { | ||
32 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
33 | struct atl1e_hw *hw = &adapter->hw; | ||
34 | |||
35 | ecmd->supported = (SUPPORTED_10baseT_Half | | ||
36 | SUPPORTED_10baseT_Full | | ||
37 | SUPPORTED_100baseT_Half | | ||
38 | SUPPORTED_100baseT_Full | | ||
39 | SUPPORTED_Autoneg | | ||
40 | SUPPORTED_TP); | ||
41 | if (hw->nic_type == athr_l1e) | ||
42 | ecmd->supported |= SUPPORTED_1000baseT_Full; | ||
43 | |||
44 | ecmd->advertising = ADVERTISED_TP; | ||
45 | |||
46 | ecmd->advertising |= ADVERTISED_Autoneg; | ||
47 | ecmd->advertising |= hw->autoneg_advertised; | ||
48 | |||
49 | ecmd->port = PORT_TP; | ||
50 | ecmd->phy_address = 0; | ||
51 | ecmd->transceiver = XCVR_INTERNAL; | ||
52 | |||
53 | if (adapter->link_speed != SPEED_0) { | ||
54 | ethtool_cmd_speed_set(ecmd, adapter->link_speed); | ||
55 | if (adapter->link_duplex == FULL_DUPLEX) | ||
56 | ecmd->duplex = DUPLEX_FULL; | ||
57 | else | ||
58 | ecmd->duplex = DUPLEX_HALF; | ||
59 | } else { | ||
60 | ethtool_cmd_speed_set(ecmd, -1); | ||
61 | ecmd->duplex = -1; | ||
62 | } | ||
63 | |||
64 | ecmd->autoneg = AUTONEG_ENABLE; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int atl1e_set_settings(struct net_device *netdev, | ||
69 | struct ethtool_cmd *ecmd) | ||
70 | { | ||
71 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
72 | struct atl1e_hw *hw = &adapter->hw; | ||
73 | |||
74 | while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) | ||
75 | msleep(1); | ||
76 | |||
77 | if (ecmd->autoneg == AUTONEG_ENABLE) { | ||
78 | u16 adv4, adv9; | ||
79 | |||
80 | if ((ecmd->advertising&ADVERTISE_1000_FULL)) { | ||
81 | if (hw->nic_type == athr_l1e) { | ||
82 | hw->autoneg_advertised = | ||
83 | ecmd->advertising & AT_ADV_MASK; | ||
84 | } else { | ||
85 | clear_bit(__AT_RESETTING, &adapter->flags); | ||
86 | return -EINVAL; | ||
87 | } | ||
88 | } else if (ecmd->advertising&ADVERTISE_1000_HALF) { | ||
89 | clear_bit(__AT_RESETTING, &adapter->flags); | ||
90 | return -EINVAL; | ||
91 | } else { | ||
92 | hw->autoneg_advertised = | ||
93 | ecmd->advertising & AT_ADV_MASK; | ||
94 | } | ||
95 | ecmd->advertising = hw->autoneg_advertised | | ||
96 | ADVERTISED_TP | ADVERTISED_Autoneg; | ||
97 | |||
98 | adv4 = hw->mii_autoneg_adv_reg & ~ADVERTISE_ALL; | ||
99 | adv9 = hw->mii_1000t_ctrl_reg & ~MII_AT001_CR_1000T_SPEED_MASK; | ||
100 | if (hw->autoneg_advertised & ADVERTISE_10_HALF) | ||
101 | adv4 |= ADVERTISE_10HALF; | ||
102 | if (hw->autoneg_advertised & ADVERTISE_10_FULL) | ||
103 | adv4 |= ADVERTISE_10FULL; | ||
104 | if (hw->autoneg_advertised & ADVERTISE_100_HALF) | ||
105 | adv4 |= ADVERTISE_100HALF; | ||
106 | if (hw->autoneg_advertised & ADVERTISE_100_FULL) | ||
107 | adv4 |= ADVERTISE_100FULL; | ||
108 | if (hw->autoneg_advertised & ADVERTISE_1000_FULL) | ||
109 | adv9 |= ADVERTISE_1000FULL; | ||
110 | |||
111 | if (adv4 != hw->mii_autoneg_adv_reg || | ||
112 | adv9 != hw->mii_1000t_ctrl_reg) { | ||
113 | hw->mii_autoneg_adv_reg = adv4; | ||
114 | hw->mii_1000t_ctrl_reg = adv9; | ||
115 | hw->re_autoneg = true; | ||
116 | } | ||
117 | |||
118 | } else { | ||
119 | clear_bit(__AT_RESETTING, &adapter->flags); | ||
120 | return -EINVAL; | ||
121 | } | ||
122 | |||
123 | /* reset the link */ | ||
124 | |||
125 | if (netif_running(adapter->netdev)) { | ||
126 | atl1e_down(adapter); | ||
127 | atl1e_up(adapter); | ||
128 | } else | ||
129 | atl1e_reset_hw(&adapter->hw); | ||
130 | |||
131 | clear_bit(__AT_RESETTING, &adapter->flags); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static u32 atl1e_get_msglevel(struct net_device *netdev) | ||
136 | { | ||
137 | #ifdef DBG | ||
138 | return 1; | ||
139 | #else | ||
140 | return 0; | ||
141 | #endif | ||
142 | } | ||
143 | |||
144 | static int atl1e_get_regs_len(struct net_device *netdev) | ||
145 | { | ||
146 | return AT_REGS_LEN * sizeof(u32); | ||
147 | } | ||
148 | |||
149 | static void atl1e_get_regs(struct net_device *netdev, | ||
150 | struct ethtool_regs *regs, void *p) | ||
151 | { | ||
152 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
153 | struct atl1e_hw *hw = &adapter->hw; | ||
154 | u32 *regs_buff = p; | ||
155 | u16 phy_data; | ||
156 | |||
157 | memset(p, 0, AT_REGS_LEN * sizeof(u32)); | ||
158 | |||
159 | regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; | ||
160 | |||
161 | regs_buff[0] = AT_READ_REG(hw, REG_VPD_CAP); | ||
162 | regs_buff[1] = AT_READ_REG(hw, REG_SPI_FLASH_CTRL); | ||
163 | regs_buff[2] = AT_READ_REG(hw, REG_SPI_FLASH_CONFIG); | ||
164 | regs_buff[3] = AT_READ_REG(hw, REG_TWSI_CTRL); | ||
165 | regs_buff[4] = AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL); | ||
166 | regs_buff[5] = AT_READ_REG(hw, REG_MASTER_CTRL); | ||
167 | regs_buff[6] = AT_READ_REG(hw, REG_MANUAL_TIMER_INIT); | ||
168 | regs_buff[7] = AT_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT); | ||
169 | regs_buff[8] = AT_READ_REG(hw, REG_GPHY_CTRL); | ||
170 | regs_buff[9] = AT_READ_REG(hw, REG_CMBDISDMA_TIMER); | ||
171 | regs_buff[10] = AT_READ_REG(hw, REG_IDLE_STATUS); | ||
172 | regs_buff[11] = AT_READ_REG(hw, REG_MDIO_CTRL); | ||
173 | regs_buff[12] = AT_READ_REG(hw, REG_SERDES_LOCK); | ||
174 | regs_buff[13] = AT_READ_REG(hw, REG_MAC_CTRL); | ||
175 | regs_buff[14] = AT_READ_REG(hw, REG_MAC_IPG_IFG); | ||
176 | regs_buff[15] = AT_READ_REG(hw, REG_MAC_STA_ADDR); | ||
177 | regs_buff[16] = AT_READ_REG(hw, REG_MAC_STA_ADDR+4); | ||
178 | regs_buff[17] = AT_READ_REG(hw, REG_RX_HASH_TABLE); | ||
179 | regs_buff[18] = AT_READ_REG(hw, REG_RX_HASH_TABLE+4); | ||
180 | regs_buff[19] = AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL); | ||
181 | regs_buff[20] = AT_READ_REG(hw, REG_MTU); | ||
182 | regs_buff[21] = AT_READ_REG(hw, REG_WOL_CTRL); | ||
183 | regs_buff[22] = AT_READ_REG(hw, REG_SRAM_TRD_ADDR); | ||
184 | regs_buff[23] = AT_READ_REG(hw, REG_SRAM_TRD_LEN); | ||
185 | regs_buff[24] = AT_READ_REG(hw, REG_SRAM_RXF_ADDR); | ||
186 | regs_buff[25] = AT_READ_REG(hw, REG_SRAM_RXF_LEN); | ||
187 | regs_buff[26] = AT_READ_REG(hw, REG_SRAM_TXF_ADDR); | ||
188 | regs_buff[27] = AT_READ_REG(hw, REG_SRAM_TXF_LEN); | ||
189 | regs_buff[28] = AT_READ_REG(hw, REG_SRAM_TCPH_ADDR); | ||
190 | regs_buff[29] = AT_READ_REG(hw, REG_SRAM_PKTH_ADDR); | ||
191 | |||
192 | atl1e_read_phy_reg(hw, MII_BMCR, &phy_data); | ||
193 | regs_buff[73] = (u32)phy_data; | ||
194 | atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); | ||
195 | regs_buff[74] = (u32)phy_data; | ||
196 | } | ||
197 | |||
198 | static int atl1e_get_eeprom_len(struct net_device *netdev) | ||
199 | { | ||
200 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
201 | |||
202 | if (!atl1e_check_eeprom_exist(&adapter->hw)) | ||
203 | return AT_EEPROM_LEN; | ||
204 | else | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int atl1e_get_eeprom(struct net_device *netdev, | ||
209 | struct ethtool_eeprom *eeprom, u8 *bytes) | ||
210 | { | ||
211 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
212 | struct atl1e_hw *hw = &adapter->hw; | ||
213 | u32 *eeprom_buff; | ||
214 | int first_dword, last_dword; | ||
215 | int ret_val = 0; | ||
216 | int i; | ||
217 | |||
218 | if (eeprom->len == 0) | ||
219 | return -EINVAL; | ||
220 | |||
221 | if (atl1e_check_eeprom_exist(hw)) /* not exist */ | ||
222 | return -EINVAL; | ||
223 | |||
224 | eeprom->magic = hw->vendor_id | (hw->device_id << 16); | ||
225 | |||
226 | first_dword = eeprom->offset >> 2; | ||
227 | last_dword = (eeprom->offset + eeprom->len - 1) >> 2; | ||
228 | |||
229 | eeprom_buff = kmalloc(sizeof(u32) * | ||
230 | (last_dword - first_dword + 1), GFP_KERNEL); | ||
231 | if (eeprom_buff == NULL) | ||
232 | return -ENOMEM; | ||
233 | |||
234 | for (i = first_dword; i < last_dword; i++) { | ||
235 | if (!atl1e_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { | ||
236 | kfree(eeprom_buff); | ||
237 | return -EIO; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), | ||
242 | eeprom->len); | ||
243 | kfree(eeprom_buff); | ||
244 | |||
245 | return ret_val; | ||
246 | } | ||
247 | |||
248 | static int atl1e_set_eeprom(struct net_device *netdev, | ||
249 | struct ethtool_eeprom *eeprom, u8 *bytes) | ||
250 | { | ||
251 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
252 | struct atl1e_hw *hw = &adapter->hw; | ||
253 | u32 *eeprom_buff; | ||
254 | u32 *ptr; | ||
255 | int first_dword, last_dword; | ||
256 | int ret_val = 0; | ||
257 | int i; | ||
258 | |||
259 | if (eeprom->len == 0) | ||
260 | return -EOPNOTSUPP; | ||
261 | |||
262 | if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) | ||
263 | return -EINVAL; | ||
264 | |||
265 | first_dword = eeprom->offset >> 2; | ||
266 | last_dword = (eeprom->offset + eeprom->len - 1) >> 2; | ||
267 | eeprom_buff = kmalloc(AT_EEPROM_LEN, GFP_KERNEL); | ||
268 | if (eeprom_buff == NULL) | ||
269 | return -ENOMEM; | ||
270 | |||
271 | ptr = (u32 *)eeprom_buff; | ||
272 | |||
273 | if (eeprom->offset & 3) { | ||
274 | /* need read/modify/write of first changed EEPROM word */ | ||
275 | /* only the second byte of the word is being modified */ | ||
276 | if (!atl1e_read_eeprom(hw, first_dword * 4, &(eeprom_buff[0]))) { | ||
277 | ret_val = -EIO; | ||
278 | goto out; | ||
279 | } | ||
280 | ptr++; | ||
281 | } | ||
282 | if (((eeprom->offset + eeprom->len) & 3)) { | ||
283 | /* need read/modify/write of last changed EEPROM word */ | ||
284 | /* only the first byte of the word is being modified */ | ||
285 | |||
286 | if (!atl1e_read_eeprom(hw, last_dword * 4, | ||
287 | &(eeprom_buff[last_dword - first_dword]))) { | ||
288 | ret_val = -EIO; | ||
289 | goto out; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /* Device's eeprom is always little-endian, word addressable */ | ||
294 | memcpy(ptr, bytes, eeprom->len); | ||
295 | |||
296 | for (i = 0; i < last_dword - first_dword + 1; i++) { | ||
297 | if (!atl1e_write_eeprom(hw, ((first_dword + i) * 4), | ||
298 | eeprom_buff[i])) { | ||
299 | ret_val = -EIO; | ||
300 | goto out; | ||
301 | } | ||
302 | } | ||
303 | out: | ||
304 | kfree(eeprom_buff); | ||
305 | return ret_val; | ||
306 | } | ||
307 | |||
308 | static void atl1e_get_drvinfo(struct net_device *netdev, | ||
309 | struct ethtool_drvinfo *drvinfo) | ||
310 | { | ||
311 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
312 | |||
313 | strncpy(drvinfo->driver, atl1e_driver_name, 32); | ||
314 | strncpy(drvinfo->version, atl1e_driver_version, 32); | ||
315 | strncpy(drvinfo->fw_version, "L1e", 32); | ||
316 | strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); | ||
317 | drvinfo->n_stats = 0; | ||
318 | drvinfo->testinfo_len = 0; | ||
319 | drvinfo->regdump_len = atl1e_get_regs_len(netdev); | ||
320 | drvinfo->eedump_len = atl1e_get_eeprom_len(netdev); | ||
321 | } | ||
322 | |||
323 | static void atl1e_get_wol(struct net_device *netdev, | ||
324 | struct ethtool_wolinfo *wol) | ||
325 | { | ||
326 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
327 | |||
328 | wol->supported = WAKE_MAGIC | WAKE_PHY; | ||
329 | wol->wolopts = 0; | ||
330 | |||
331 | if (adapter->wol & AT_WUFC_EX) | ||
332 | wol->wolopts |= WAKE_UCAST; | ||
333 | if (adapter->wol & AT_WUFC_MC) | ||
334 | wol->wolopts |= WAKE_MCAST; | ||
335 | if (adapter->wol & AT_WUFC_BC) | ||
336 | wol->wolopts |= WAKE_BCAST; | ||
337 | if (adapter->wol & AT_WUFC_MAG) | ||
338 | wol->wolopts |= WAKE_MAGIC; | ||
339 | if (adapter->wol & AT_WUFC_LNKC) | ||
340 | wol->wolopts |= WAKE_PHY; | ||
341 | } | ||
342 | |||
343 | static int atl1e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) | ||
344 | { | ||
345 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
346 | |||
347 | if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | | ||
348 | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)) | ||
349 | return -EOPNOTSUPP; | ||
350 | /* these settings will always override what we currently have */ | ||
351 | adapter->wol = 0; | ||
352 | |||
353 | if (wol->wolopts & WAKE_MAGIC) | ||
354 | adapter->wol |= AT_WUFC_MAG; | ||
355 | if (wol->wolopts & WAKE_PHY) | ||
356 | adapter->wol |= AT_WUFC_LNKC; | ||
357 | |||
358 | device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); | ||
359 | |||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int atl1e_nway_reset(struct net_device *netdev) | ||
364 | { | ||
365 | struct atl1e_adapter *adapter = netdev_priv(netdev); | ||
366 | if (netif_running(netdev)) | ||
367 | atl1e_reinit_locked(adapter); | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static const struct ethtool_ops atl1e_ethtool_ops = { | ||
372 | .get_settings = atl1e_get_settings, | ||
373 | .set_settings = atl1e_set_settings, | ||
374 | .get_drvinfo = atl1e_get_drvinfo, | ||
375 | .get_regs_len = atl1e_get_regs_len, | ||
376 | .get_regs = atl1e_get_regs, | ||
377 | .get_wol = atl1e_get_wol, | ||
378 | .set_wol = atl1e_set_wol, | ||
379 | .get_msglevel = atl1e_get_msglevel, | ||
380 | .nway_reset = atl1e_nway_reset, | ||
381 | .get_link = ethtool_op_get_link, | ||
382 | .get_eeprom_len = atl1e_get_eeprom_len, | ||
383 | .get_eeprom = atl1e_get_eeprom, | ||
384 | .set_eeprom = atl1e_set_eeprom, | ||
385 | }; | ||
386 | |||
387 | void atl1e_set_ethtool_ops(struct net_device *netdev) | ||
388 | { | ||
389 | SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops); | ||
390 | } | ||