Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /******************************************************************************* |
2 | ||
3 | ||
4 | Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify it | |
7 | under the terms of the GNU General Public License as published by the Free | |
8 | Software Foundation; either version 2 of the License, or (at your option) | |
9 | any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License along with | |
17 | this program; if not, write to the Free Software Foundation, Inc., 59 | |
18 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
19 | ||
20 | The full GNU General Public License is included in this distribution in the | |
21 | file called LICENSE. | |
22 | ||
23 | Contact Information: | |
24 | Linux NICS <linux.nics@intel.com> | |
25 | Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
26 | ||
27 | *******************************************************************************/ | |
28 | ||
29 | /* ethtool support for ixgb */ | |
30 | ||
31 | #include "ixgb.h" | |
32 | ||
33 | #include <asm/uaccess.h> | |
34 | ||
35 | extern char ixgb_driver_name[]; | |
36 | extern char ixgb_driver_version[]; | |
37 | ||
38 | extern int ixgb_up(struct ixgb_adapter *adapter); | |
39 | extern void ixgb_down(struct ixgb_adapter *adapter, boolean_t kill_watchdog); | |
40 | extern void ixgb_reset(struct ixgb_adapter *adapter); | |
41 | extern int ixgb_setup_rx_resources(struct ixgb_adapter *adapter); | |
42 | extern int ixgb_setup_tx_resources(struct ixgb_adapter *adapter); | |
43 | extern void ixgb_free_rx_resources(struct ixgb_adapter *adapter); | |
44 | extern void ixgb_free_tx_resources(struct ixgb_adapter *adapter); | |
45 | extern void ixgb_update_stats(struct ixgb_adapter *adapter); | |
46 | ||
47 | struct ixgb_stats { | |
48 | char stat_string[ETH_GSTRING_LEN]; | |
49 | int sizeof_stat; | |
50 | int stat_offset; | |
51 | }; | |
52 | ||
53 | #define IXGB_STAT(m) sizeof(((struct ixgb_adapter *)0)->m), \ | |
54 | offsetof(struct ixgb_adapter, m) | |
55 | static struct ixgb_stats ixgb_gstrings_stats[] = { | |
56 | {"rx_packets", IXGB_STAT(net_stats.rx_packets)}, | |
57 | {"tx_packets", IXGB_STAT(net_stats.tx_packets)}, | |
58 | {"rx_bytes", IXGB_STAT(net_stats.rx_bytes)}, | |
59 | {"tx_bytes", IXGB_STAT(net_stats.tx_bytes)}, | |
60 | {"rx_errors", IXGB_STAT(net_stats.rx_errors)}, | |
61 | {"tx_errors", IXGB_STAT(net_stats.tx_errors)}, | |
62 | {"rx_dropped", IXGB_STAT(net_stats.rx_dropped)}, | |
63 | {"tx_dropped", IXGB_STAT(net_stats.tx_dropped)}, | |
64 | {"multicast", IXGB_STAT(net_stats.multicast)}, | |
65 | {"collisions", IXGB_STAT(net_stats.collisions)}, | |
66 | ||
67 | /* { "rx_length_errors", IXGB_STAT(net_stats.rx_length_errors) }, */ | |
68 | {"rx_over_errors", IXGB_STAT(net_stats.rx_over_errors)}, | |
69 | {"rx_crc_errors", IXGB_STAT(net_stats.rx_crc_errors)}, | |
70 | {"rx_frame_errors", IXGB_STAT(net_stats.rx_frame_errors)}, | |
71 | {"rx_fifo_errors", IXGB_STAT(net_stats.rx_fifo_errors)}, | |
72 | {"rx_missed_errors", IXGB_STAT(net_stats.rx_missed_errors)}, | |
73 | {"tx_aborted_errors", IXGB_STAT(net_stats.tx_aborted_errors)}, | |
74 | {"tx_carrier_errors", IXGB_STAT(net_stats.tx_carrier_errors)}, | |
75 | {"tx_fifo_errors", IXGB_STAT(net_stats.tx_fifo_errors)}, | |
76 | {"tx_heartbeat_errors", IXGB_STAT(net_stats.tx_heartbeat_errors)}, | |
77 | {"tx_window_errors", IXGB_STAT(net_stats.tx_window_errors)}, | |
78 | {"tx_deferred_ok", IXGB_STAT(stats.dc)}, | |
79 | {"rx_long_length_errors", IXGB_STAT(stats.roc)}, | |
80 | {"rx_short_length_errors", IXGB_STAT(stats.ruc)}, | |
81 | #ifdef NETIF_F_TSO | |
82 | {"tx_tcp_seg_good", IXGB_STAT(stats.tsctc)}, | |
83 | {"tx_tcp_seg_failed", IXGB_STAT(stats.tsctfc)}, | |
84 | #endif | |
85 | {"rx_flow_control_xon", IXGB_STAT(stats.xonrxc)}, | |
86 | {"rx_flow_control_xoff", IXGB_STAT(stats.xoffrxc)}, | |
87 | {"tx_flow_control_xon", IXGB_STAT(stats.xontxc)}, | |
88 | {"tx_flow_control_xoff", IXGB_STAT(stats.xofftxc)}, | |
89 | {"rx_csum_offload_good", IXGB_STAT(hw_csum_rx_good)}, | |
90 | {"rx_csum_offload_errors", IXGB_STAT(hw_csum_rx_error)}, | |
91 | {"tx_csum_offload_good", IXGB_STAT(hw_csum_tx_good)}, | |
92 | {"tx_csum_offload_errors", IXGB_STAT(hw_csum_tx_error)} | |
93 | }; | |
94 | ||
95 | #define IXGB_STATS_LEN \ | |
96 | sizeof(ixgb_gstrings_stats) / sizeof(struct ixgb_stats) | |
97 | ||
98 | static int | |
99 | ixgb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) | |
100 | { | |
101 | struct ixgb_adapter *adapter = netdev->priv; | |
102 | ||
103 | ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); | |
104 | ecmd->advertising = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); | |
105 | ecmd->port = PORT_FIBRE; | |
106 | ecmd->transceiver = XCVR_EXTERNAL; | |
107 | ||
108 | if(netif_carrier_ok(adapter->netdev)) { | |
109 | ecmd->speed = SPEED_10000; | |
110 | ecmd->duplex = DUPLEX_FULL; | |
111 | } else { | |
112 | ecmd->speed = -1; | |
113 | ecmd->duplex = -1; | |
114 | } | |
115 | ||
116 | ecmd->autoneg = AUTONEG_DISABLE; | |
117 | return 0; | |
118 | } | |
119 | ||
120 | static int | |
121 | ixgb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) | |
122 | { | |
123 | struct ixgb_adapter *adapter = netdev->priv; | |
124 | ||
125 | if(ecmd->autoneg == AUTONEG_ENABLE || | |
126 | ecmd->speed + ecmd->duplex != SPEED_10000 + DUPLEX_FULL) | |
127 | return -EINVAL; | |
128 | ||
129 | if(netif_running(adapter->netdev)) { | |
130 | ixgb_down(adapter, TRUE); | |
131 | ixgb_reset(adapter); | |
132 | ixgb_up(adapter); | |
133 | } else | |
134 | ixgb_reset(adapter); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | static void | |
140 | ixgb_get_pauseparam(struct net_device *netdev, | |
141 | struct ethtool_pauseparam *pause) | |
142 | { | |
143 | struct ixgb_adapter *adapter = netdev->priv; | |
144 | struct ixgb_hw *hw = &adapter->hw; | |
145 | ||
146 | pause->autoneg = AUTONEG_DISABLE; | |
147 | ||
148 | if(hw->fc.type == ixgb_fc_rx_pause) | |
149 | pause->rx_pause = 1; | |
150 | else if(hw->fc.type == ixgb_fc_tx_pause) | |
151 | pause->tx_pause = 1; | |
152 | else if(hw->fc.type == ixgb_fc_full) { | |
153 | pause->rx_pause = 1; | |
154 | pause->tx_pause = 1; | |
155 | } | |
156 | } | |
157 | ||
158 | static int | |
159 | ixgb_set_pauseparam(struct net_device *netdev, | |
160 | struct ethtool_pauseparam *pause) | |
161 | { | |
162 | struct ixgb_adapter *adapter = netdev->priv; | |
163 | struct ixgb_hw *hw = &adapter->hw; | |
164 | ||
165 | if(pause->autoneg == AUTONEG_ENABLE) | |
166 | return -EINVAL; | |
167 | ||
168 | if(pause->rx_pause && pause->tx_pause) | |
169 | hw->fc.type = ixgb_fc_full; | |
170 | else if(pause->rx_pause && !pause->tx_pause) | |
171 | hw->fc.type = ixgb_fc_rx_pause; | |
172 | else if(!pause->rx_pause && pause->tx_pause) | |
173 | hw->fc.type = ixgb_fc_tx_pause; | |
174 | else if(!pause->rx_pause && !pause->tx_pause) | |
175 | hw->fc.type = ixgb_fc_none; | |
176 | ||
177 | if(netif_running(adapter->netdev)) { | |
178 | ixgb_down(adapter, TRUE); | |
179 | ixgb_up(adapter); | |
180 | } else | |
181 | ixgb_reset(adapter); | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
186 | static uint32_t | |
187 | ixgb_get_rx_csum(struct net_device *netdev) | |
188 | { | |
189 | struct ixgb_adapter *adapter = netdev->priv; | |
190 | return adapter->rx_csum; | |
191 | } | |
192 | ||
193 | static int | |
194 | ixgb_set_rx_csum(struct net_device *netdev, uint32_t data) | |
195 | { | |
196 | struct ixgb_adapter *adapter = netdev->priv; | |
197 | adapter->rx_csum = data; | |
198 | ||
199 | if(netif_running(netdev)) { | |
200 | ixgb_down(adapter,TRUE); | |
201 | ixgb_up(adapter); | |
202 | } else | |
203 | ixgb_reset(adapter); | |
204 | return 0; | |
205 | } | |
206 | ||
207 | static uint32_t | |
208 | ixgb_get_tx_csum(struct net_device *netdev) | |
209 | { | |
210 | return (netdev->features & NETIF_F_HW_CSUM) != 0; | |
211 | } | |
212 | ||
213 | static int | |
214 | ixgb_set_tx_csum(struct net_device *netdev, uint32_t data) | |
215 | { | |
216 | if (data) | |
217 | netdev->features |= NETIF_F_HW_CSUM; | |
218 | else | |
219 | netdev->features &= ~NETIF_F_HW_CSUM; | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | #ifdef NETIF_F_TSO | |
225 | static int | |
226 | ixgb_set_tso(struct net_device *netdev, uint32_t data) | |
227 | { | |
228 | if(data) | |
229 | netdev->features |= NETIF_F_TSO; | |
230 | else | |
231 | netdev->features &= ~NETIF_F_TSO; | |
232 | return 0; | |
233 | } | |
234 | #endif /* NETIF_F_TSO */ | |
235 | ||
236 | #define IXGB_GET_STAT(_A_, _R_) _A_->stats._R_ | |
237 | ||
238 | static int | |
239 | ixgb_get_regs_len(struct net_device *netdev) | |
240 | { | |
241 | #define IXGB_REG_DUMP_LEN 136*sizeof(uint32_t) | |
242 | return IXGB_REG_DUMP_LEN; | |
243 | } | |
244 | ||
245 | static void | |
246 | ixgb_get_regs(struct net_device *netdev, | |
247 | struct ethtool_regs *regs, void *p) | |
248 | { | |
249 | struct ixgb_adapter *adapter = netdev->priv; | |
250 | struct ixgb_hw *hw = &adapter->hw; | |
251 | uint32_t *reg = p; | |
252 | uint32_t *reg_start = reg; | |
253 | uint8_t i; | |
254 | ||
255 | regs->version = (adapter->hw.device_id << 16) | adapter->hw.subsystem_id; | |
256 | ||
257 | /* General Registers */ | |
258 | *reg++ = IXGB_READ_REG(hw, CTRL0); /* 0 */ | |
259 | *reg++ = IXGB_READ_REG(hw, CTRL1); /* 1 */ | |
260 | *reg++ = IXGB_READ_REG(hw, STATUS); /* 2 */ | |
261 | *reg++ = IXGB_READ_REG(hw, EECD); /* 3 */ | |
262 | *reg++ = IXGB_READ_REG(hw, MFS); /* 4 */ | |
263 | ||
264 | /* Interrupt */ | |
265 | *reg++ = IXGB_READ_REG(hw, ICR); /* 5 */ | |
266 | *reg++ = IXGB_READ_REG(hw, ICS); /* 6 */ | |
267 | *reg++ = IXGB_READ_REG(hw, IMS); /* 7 */ | |
268 | *reg++ = IXGB_READ_REG(hw, IMC); /* 8 */ | |
269 | ||
270 | /* Receive */ | |
271 | *reg++ = IXGB_READ_REG(hw, RCTL); /* 9 */ | |
272 | *reg++ = IXGB_READ_REG(hw, FCRTL); /* 10 */ | |
273 | *reg++ = IXGB_READ_REG(hw, FCRTH); /* 11 */ | |
274 | *reg++ = IXGB_READ_REG(hw, RDBAL); /* 12 */ | |
275 | *reg++ = IXGB_READ_REG(hw, RDBAH); /* 13 */ | |
276 | *reg++ = IXGB_READ_REG(hw, RDLEN); /* 14 */ | |
277 | *reg++ = IXGB_READ_REG(hw, RDH); /* 15 */ | |
278 | *reg++ = IXGB_READ_REG(hw, RDT); /* 16 */ | |
279 | *reg++ = IXGB_READ_REG(hw, RDTR); /* 17 */ | |
280 | *reg++ = IXGB_READ_REG(hw, RXDCTL); /* 18 */ | |
281 | *reg++ = IXGB_READ_REG(hw, RAIDC); /* 19 */ | |
282 | *reg++ = IXGB_READ_REG(hw, RXCSUM); /* 20 */ | |
283 | ||
284 | for (i = 0; i < IXGB_RAR_ENTRIES; i++) { | |
285 | *reg++ = IXGB_READ_REG_ARRAY(hw, RAL, (i << 1)); /*21,...,51 */ | |
286 | *reg++ = IXGB_READ_REG_ARRAY(hw, RAH, (i << 1)); /*22,...,52 */ | |
287 | } | |
288 | ||
289 | /* Transmit */ | |
290 | *reg++ = IXGB_READ_REG(hw, TCTL); /* 53 */ | |
291 | *reg++ = IXGB_READ_REG(hw, TDBAL); /* 54 */ | |
292 | *reg++ = IXGB_READ_REG(hw, TDBAH); /* 55 */ | |
293 | *reg++ = IXGB_READ_REG(hw, TDLEN); /* 56 */ | |
294 | *reg++ = IXGB_READ_REG(hw, TDH); /* 57 */ | |
295 | *reg++ = IXGB_READ_REG(hw, TDT); /* 58 */ | |
296 | *reg++ = IXGB_READ_REG(hw, TIDV); /* 59 */ | |
297 | *reg++ = IXGB_READ_REG(hw, TXDCTL); /* 60 */ | |
298 | *reg++ = IXGB_READ_REG(hw, TSPMT); /* 61 */ | |
299 | *reg++ = IXGB_READ_REG(hw, PAP); /* 62 */ | |
300 | ||
301 | /* Physical */ | |
302 | *reg++ = IXGB_READ_REG(hw, PCSC1); /* 63 */ | |
303 | *reg++ = IXGB_READ_REG(hw, PCSC2); /* 64 */ | |
304 | *reg++ = IXGB_READ_REG(hw, PCSS1); /* 65 */ | |
305 | *reg++ = IXGB_READ_REG(hw, PCSS2); /* 66 */ | |
306 | *reg++ = IXGB_READ_REG(hw, XPCSS); /* 67 */ | |
307 | *reg++ = IXGB_READ_REG(hw, UCCR); /* 68 */ | |
308 | *reg++ = IXGB_READ_REG(hw, XPCSTC); /* 69 */ | |
309 | *reg++ = IXGB_READ_REG(hw, MACA); /* 70 */ | |
310 | *reg++ = IXGB_READ_REG(hw, APAE); /* 71 */ | |
311 | *reg++ = IXGB_READ_REG(hw, ARD); /* 72 */ | |
312 | *reg++ = IXGB_READ_REG(hw, AIS); /* 73 */ | |
313 | *reg++ = IXGB_READ_REG(hw, MSCA); /* 74 */ | |
314 | *reg++ = IXGB_READ_REG(hw, MSRWD); /* 75 */ | |
315 | ||
316 | /* Statistics */ | |
317 | *reg++ = IXGB_GET_STAT(adapter, tprl); /* 76 */ | |
318 | *reg++ = IXGB_GET_STAT(adapter, tprh); /* 77 */ | |
319 | *reg++ = IXGB_GET_STAT(adapter, gprcl); /* 78 */ | |
320 | *reg++ = IXGB_GET_STAT(adapter, gprch); /* 79 */ | |
321 | *reg++ = IXGB_GET_STAT(adapter, bprcl); /* 80 */ | |
322 | *reg++ = IXGB_GET_STAT(adapter, bprch); /* 81 */ | |
323 | *reg++ = IXGB_GET_STAT(adapter, mprcl); /* 82 */ | |
324 | *reg++ = IXGB_GET_STAT(adapter, mprch); /* 83 */ | |
325 | *reg++ = IXGB_GET_STAT(adapter, uprcl); /* 84 */ | |
326 | *reg++ = IXGB_GET_STAT(adapter, uprch); /* 85 */ | |
327 | *reg++ = IXGB_GET_STAT(adapter, vprcl); /* 86 */ | |
328 | *reg++ = IXGB_GET_STAT(adapter, vprch); /* 87 */ | |
329 | *reg++ = IXGB_GET_STAT(adapter, jprcl); /* 88 */ | |
330 | *reg++ = IXGB_GET_STAT(adapter, jprch); /* 89 */ | |
331 | *reg++ = IXGB_GET_STAT(adapter, gorcl); /* 90 */ | |
332 | *reg++ = IXGB_GET_STAT(adapter, gorch); /* 91 */ | |
333 | *reg++ = IXGB_GET_STAT(adapter, torl); /* 92 */ | |
334 | *reg++ = IXGB_GET_STAT(adapter, torh); /* 93 */ | |
335 | *reg++ = IXGB_GET_STAT(adapter, rnbc); /* 94 */ | |
336 | *reg++ = IXGB_GET_STAT(adapter, ruc); /* 95 */ | |
337 | *reg++ = IXGB_GET_STAT(adapter, roc); /* 96 */ | |
338 | *reg++ = IXGB_GET_STAT(adapter, rlec); /* 97 */ | |
339 | *reg++ = IXGB_GET_STAT(adapter, crcerrs); /* 98 */ | |
340 | *reg++ = IXGB_GET_STAT(adapter, icbc); /* 99 */ | |
341 | *reg++ = IXGB_GET_STAT(adapter, ecbc); /* 100 */ | |
342 | *reg++ = IXGB_GET_STAT(adapter, mpc); /* 101 */ | |
343 | *reg++ = IXGB_GET_STAT(adapter, tptl); /* 102 */ | |
344 | *reg++ = IXGB_GET_STAT(adapter, tpth); /* 103 */ | |
345 | *reg++ = IXGB_GET_STAT(adapter, gptcl); /* 104 */ | |
346 | *reg++ = IXGB_GET_STAT(adapter, gptch); /* 105 */ | |
347 | *reg++ = IXGB_GET_STAT(adapter, bptcl); /* 106 */ | |
348 | *reg++ = IXGB_GET_STAT(adapter, bptch); /* 107 */ | |
349 | *reg++ = IXGB_GET_STAT(adapter, mptcl); /* 108 */ | |
350 | *reg++ = IXGB_GET_STAT(adapter, mptch); /* 109 */ | |
351 | *reg++ = IXGB_GET_STAT(adapter, uptcl); /* 110 */ | |
352 | *reg++ = IXGB_GET_STAT(adapter, uptch); /* 111 */ | |
353 | *reg++ = IXGB_GET_STAT(adapter, vptcl); /* 112 */ | |
354 | *reg++ = IXGB_GET_STAT(adapter, vptch); /* 113 */ | |
355 | *reg++ = IXGB_GET_STAT(adapter, jptcl); /* 114 */ | |
356 | *reg++ = IXGB_GET_STAT(adapter, jptch); /* 115 */ | |
357 | *reg++ = IXGB_GET_STAT(adapter, gotcl); /* 116 */ | |
358 | *reg++ = IXGB_GET_STAT(adapter, gotch); /* 117 */ | |
359 | *reg++ = IXGB_GET_STAT(adapter, totl); /* 118 */ | |
360 | *reg++ = IXGB_GET_STAT(adapter, toth); /* 119 */ | |
361 | *reg++ = IXGB_GET_STAT(adapter, dc); /* 120 */ | |
362 | *reg++ = IXGB_GET_STAT(adapter, plt64c); /* 121 */ | |
363 | *reg++ = IXGB_GET_STAT(adapter, tsctc); /* 122 */ | |
364 | *reg++ = IXGB_GET_STAT(adapter, tsctfc); /* 123 */ | |
365 | *reg++ = IXGB_GET_STAT(adapter, ibic); /* 124 */ | |
366 | *reg++ = IXGB_GET_STAT(adapter, rfc); /* 125 */ | |
367 | *reg++ = IXGB_GET_STAT(adapter, lfc); /* 126 */ | |
368 | *reg++ = IXGB_GET_STAT(adapter, pfrc); /* 127 */ | |
369 | *reg++ = IXGB_GET_STAT(adapter, pftc); /* 128 */ | |
370 | *reg++ = IXGB_GET_STAT(adapter, mcfrc); /* 129 */ | |
371 | *reg++ = IXGB_GET_STAT(adapter, mcftc); /* 130 */ | |
372 | *reg++ = IXGB_GET_STAT(adapter, xonrxc); /* 131 */ | |
373 | *reg++ = IXGB_GET_STAT(adapter, xontxc); /* 132 */ | |
374 | *reg++ = IXGB_GET_STAT(adapter, xoffrxc); /* 133 */ | |
375 | *reg++ = IXGB_GET_STAT(adapter, xofftxc); /* 134 */ | |
376 | *reg++ = IXGB_GET_STAT(adapter, rjc); /* 135 */ | |
377 | ||
378 | regs->len = (reg - reg_start) * sizeof(uint32_t); | |
379 | } | |
380 | ||
381 | static int | |
382 | ixgb_get_eeprom_len(struct net_device *netdev) | |
383 | { | |
384 | /* return size in bytes */ | |
385 | return (IXGB_EEPROM_SIZE << 1); | |
386 | } | |
387 | ||
388 | static int | |
389 | ixgb_get_eeprom(struct net_device *netdev, | |
390 | struct ethtool_eeprom *eeprom, uint8_t *bytes) | |
391 | { | |
392 | struct ixgb_adapter *adapter = netdev->priv; | |
393 | struct ixgb_hw *hw = &adapter->hw; | |
394 | uint16_t *eeprom_buff; | |
395 | int i, max_len, first_word, last_word; | |
396 | int ret_val = 0; | |
397 | ||
398 | if(eeprom->len == 0) { | |
399 | ret_val = -EINVAL; | |
400 | goto geeprom_error; | |
401 | } | |
402 | ||
403 | eeprom->magic = hw->vendor_id | (hw->device_id << 16); | |
404 | ||
405 | max_len = ixgb_get_eeprom_len(netdev); | |
406 | ||
407 | if(eeprom->offset > eeprom->offset + eeprom->len) { | |
408 | ret_val = -EINVAL; | |
409 | goto geeprom_error; | |
410 | } | |
411 | ||
412 | if((eeprom->offset + eeprom->len) > max_len) | |
413 | eeprom->len = (max_len - eeprom->offset); | |
414 | ||
415 | first_word = eeprom->offset >> 1; | |
416 | last_word = (eeprom->offset + eeprom->len - 1) >> 1; | |
417 | ||
418 | eeprom_buff = kmalloc(sizeof(uint16_t) * | |
419 | (last_word - first_word + 1), GFP_KERNEL); | |
420 | if(!eeprom_buff) | |
421 | return -ENOMEM; | |
422 | ||
423 | /* note the eeprom was good because the driver loaded */ | |
424 | for(i = 0; i <= (last_word - first_word); i++) { | |
425 | eeprom_buff[i] = ixgb_get_eeprom_word(hw, (first_word + i)); | |
426 | } | |
427 | ||
428 | memcpy(bytes, (uint8_t *)eeprom_buff + (eeprom->offset & 1), | |
429 | eeprom->len); | |
430 | kfree(eeprom_buff); | |
431 | ||
432 | geeprom_error: | |
433 | return ret_val; | |
434 | } | |
435 | ||
436 | static int | |
437 | ixgb_set_eeprom(struct net_device *netdev, | |
438 | struct ethtool_eeprom *eeprom, uint8_t *bytes) | |
439 | { | |
440 | struct ixgb_adapter *adapter = netdev->priv; | |
441 | struct ixgb_hw *hw = &adapter->hw; | |
442 | uint16_t *eeprom_buff; | |
443 | void *ptr; | |
444 | int max_len, first_word, last_word; | |
445 | uint16_t i; | |
446 | ||
447 | if(eeprom->len == 0) | |
448 | return -EINVAL; | |
449 | ||
450 | if(eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) | |
451 | return -EFAULT; | |
452 | ||
453 | max_len = ixgb_get_eeprom_len(netdev); | |
454 | ||
455 | if(eeprom->offset > eeprom->offset + eeprom->len) | |
456 | return -EINVAL; | |
457 | ||
458 | if((eeprom->offset + eeprom->len) > max_len) | |
459 | eeprom->len = (max_len - eeprom->offset); | |
460 | ||
461 | first_word = eeprom->offset >> 1; | |
462 | last_word = (eeprom->offset + eeprom->len - 1) >> 1; | |
463 | eeprom_buff = kmalloc(max_len, GFP_KERNEL); | |
464 | if(!eeprom_buff) | |
465 | return -ENOMEM; | |
466 | ||
467 | ptr = (void *)eeprom_buff; | |
468 | ||
469 | if(eeprom->offset & 1) { | |
470 | /* need read/modify/write of first changed EEPROM word */ | |
471 | /* only the second byte of the word is being modified */ | |
472 | eeprom_buff[0] = ixgb_read_eeprom(hw, first_word); | |
473 | ptr++; | |
474 | } | |
475 | if((eeprom->offset + eeprom->len) & 1) { | |
476 | /* need read/modify/write of last changed EEPROM word */ | |
477 | /* only the first byte of the word is being modified */ | |
478 | eeprom_buff[last_word - first_word] | |
479 | = ixgb_read_eeprom(hw, last_word); | |
480 | } | |
481 | ||
482 | memcpy(ptr, bytes, eeprom->len); | |
483 | for(i = 0; i <= (last_word - first_word); i++) | |
484 | ixgb_write_eeprom(hw, first_word + i, eeprom_buff[i]); | |
485 | ||
486 | /* Update the checksum over the first part of the EEPROM if needed */ | |
487 | if(first_word <= EEPROM_CHECKSUM_REG) | |
488 | ixgb_update_eeprom_checksum(hw); | |
489 | ||
490 | kfree(eeprom_buff); | |
491 | return 0; | |
492 | } | |
493 | ||
494 | static void | |
495 | ixgb_get_drvinfo(struct net_device *netdev, | |
496 | struct ethtool_drvinfo *drvinfo) | |
497 | { | |
498 | struct ixgb_adapter *adapter = netdev->priv; | |
499 | ||
500 | strncpy(drvinfo->driver, ixgb_driver_name, 32); | |
501 | strncpy(drvinfo->version, ixgb_driver_version, 32); | |
502 | strncpy(drvinfo->fw_version, "N/A", 32); | |
503 | strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); | |
504 | drvinfo->n_stats = IXGB_STATS_LEN; | |
505 | drvinfo->regdump_len = ixgb_get_regs_len(netdev); | |
506 | drvinfo->eedump_len = ixgb_get_eeprom_len(netdev); | |
507 | } | |
508 | ||
509 | static void | |
510 | ixgb_get_ringparam(struct net_device *netdev, | |
511 | struct ethtool_ringparam *ring) | |
512 | { | |
513 | struct ixgb_adapter *adapter = netdev->priv; | |
514 | struct ixgb_desc_ring *txdr = &adapter->tx_ring; | |
515 | struct ixgb_desc_ring *rxdr = &adapter->rx_ring; | |
516 | ||
517 | ring->rx_max_pending = MAX_RXD; | |
518 | ring->tx_max_pending = MAX_TXD; | |
519 | ring->rx_mini_max_pending = 0; | |
520 | ring->rx_jumbo_max_pending = 0; | |
521 | ring->rx_pending = rxdr->count; | |
522 | ring->tx_pending = txdr->count; | |
523 | ring->rx_mini_pending = 0; | |
524 | ring->rx_jumbo_pending = 0; | |
525 | } | |
526 | ||
527 | static int | |
528 | ixgb_set_ringparam(struct net_device *netdev, | |
529 | struct ethtool_ringparam *ring) | |
530 | { | |
531 | struct ixgb_adapter *adapter = netdev->priv; | |
532 | struct ixgb_desc_ring *txdr = &adapter->tx_ring; | |
533 | struct ixgb_desc_ring *rxdr = &adapter->rx_ring; | |
534 | struct ixgb_desc_ring tx_old, tx_new, rx_old, rx_new; | |
535 | int err; | |
536 | ||
537 | tx_old = adapter->tx_ring; | |
538 | rx_old = adapter->rx_ring; | |
539 | ||
540 | if((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) | |
541 | return -EINVAL; | |
542 | ||
543 | if(netif_running(adapter->netdev)) | |
544 | ixgb_down(adapter,TRUE); | |
545 | ||
546 | rxdr->count = max(ring->rx_pending,(uint32_t)MIN_RXD); | |
547 | rxdr->count = min(rxdr->count,(uint32_t)MAX_RXD); | |
548 | IXGB_ROUNDUP(rxdr->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); | |
549 | ||
550 | txdr->count = max(ring->tx_pending,(uint32_t)MIN_TXD); | |
551 | txdr->count = min(txdr->count,(uint32_t)MAX_TXD); | |
552 | IXGB_ROUNDUP(txdr->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); | |
553 | ||
554 | if(netif_running(adapter->netdev)) { | |
555 | /* Try to get new resources before deleting old */ | |
556 | if((err = ixgb_setup_rx_resources(adapter))) | |
557 | goto err_setup_rx; | |
558 | if((err = ixgb_setup_tx_resources(adapter))) | |
559 | goto err_setup_tx; | |
560 | ||
561 | /* save the new, restore the old in order to free it, | |
562 | * then restore the new back again */ | |
563 | ||
564 | rx_new = adapter->rx_ring; | |
565 | tx_new = adapter->tx_ring; | |
566 | adapter->rx_ring = rx_old; | |
567 | adapter->tx_ring = tx_old; | |
568 | ixgb_free_rx_resources(adapter); | |
569 | ixgb_free_tx_resources(adapter); | |
570 | adapter->rx_ring = rx_new; | |
571 | adapter->tx_ring = tx_new; | |
572 | if((err = ixgb_up(adapter))) | |
573 | return err; | |
574 | } | |
575 | ||
576 | return 0; | |
577 | err_setup_tx: | |
578 | ixgb_free_rx_resources(adapter); | |
579 | err_setup_rx: | |
580 | adapter->rx_ring = rx_old; | |
581 | adapter->tx_ring = tx_old; | |
582 | ixgb_up(adapter); | |
583 | return err; | |
584 | } | |
585 | ||
586 | /* toggle LED 4 times per second = 2 "blinks" per second */ | |
587 | #define IXGB_ID_INTERVAL (HZ/4) | |
588 | ||
589 | /* bit defines for adapter->led_status */ | |
590 | #define IXGB_LED_ON 0 | |
591 | ||
592 | static void | |
593 | ixgb_led_blink_callback(unsigned long data) | |
594 | { | |
595 | struct ixgb_adapter *adapter = (struct ixgb_adapter *)data; | |
596 | ||
597 | if(test_and_change_bit(IXGB_LED_ON, &adapter->led_status)) | |
598 | ixgb_led_off(&adapter->hw); | |
599 | else | |
600 | ixgb_led_on(&adapter->hw); | |
601 | ||
602 | mod_timer(&adapter->blink_timer, jiffies + IXGB_ID_INTERVAL); | |
603 | } | |
604 | ||
605 | static int | |
606 | ixgb_phys_id(struct net_device *netdev, uint32_t data) | |
607 | { | |
608 | struct ixgb_adapter *adapter = netdev->priv; | |
609 | ||
610 | if(!data || data > (uint32_t)(MAX_SCHEDULE_TIMEOUT / HZ)) | |
611 | data = (uint32_t)(MAX_SCHEDULE_TIMEOUT / HZ); | |
612 | ||
613 | if(!adapter->blink_timer.function) { | |
614 | init_timer(&adapter->blink_timer); | |
615 | adapter->blink_timer.function = ixgb_led_blink_callback; | |
616 | adapter->blink_timer.data = (unsigned long)adapter; | |
617 | } | |
618 | ||
619 | mod_timer(&adapter->blink_timer, jiffies); | |
620 | ||
621 | set_current_state(TASK_INTERRUPTIBLE); | |
622 | if(data) | |
623 | schedule_timeout(data * HZ); | |
624 | else | |
625 | schedule_timeout(MAX_SCHEDULE_TIMEOUT); | |
626 | ||
627 | del_timer_sync(&adapter->blink_timer); | |
628 | ixgb_led_off(&adapter->hw); | |
629 | clear_bit(IXGB_LED_ON, &adapter->led_status); | |
630 | ||
631 | return 0; | |
632 | } | |
633 | ||
634 | static int | |
635 | ixgb_get_stats_count(struct net_device *netdev) | |
636 | { | |
637 | return IXGB_STATS_LEN; | |
638 | } | |
639 | ||
640 | static void | |
641 | ixgb_get_ethtool_stats(struct net_device *netdev, | |
642 | struct ethtool_stats *stats, uint64_t *data) | |
643 | { | |
644 | struct ixgb_adapter *adapter = netdev->priv; | |
645 | int i; | |
646 | ||
647 | ixgb_update_stats(adapter); | |
648 | for(i = 0; i < IXGB_STATS_LEN; i++) { | |
649 | char *p = (char *)adapter+ixgb_gstrings_stats[i].stat_offset; | |
650 | data[i] = (ixgb_gstrings_stats[i].sizeof_stat == | |
651 | sizeof(uint64_t)) ? *(uint64_t *)p : *(uint32_t *)p; | |
652 | } | |
653 | } | |
654 | ||
655 | static void | |
656 | ixgb_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data) | |
657 | { | |
658 | int i; | |
659 | ||
660 | switch(stringset) { | |
661 | case ETH_SS_STATS: | |
662 | for(i=0; i < IXGB_STATS_LEN; i++) { | |
663 | memcpy(data + i * ETH_GSTRING_LEN, | |
664 | ixgb_gstrings_stats[i].stat_string, | |
665 | ETH_GSTRING_LEN); | |
666 | } | |
667 | break; | |
668 | } | |
669 | } | |
670 | ||
671 | struct ethtool_ops ixgb_ethtool_ops = { | |
672 | .get_settings = ixgb_get_settings, | |
673 | .set_settings = ixgb_set_settings, | |
674 | .get_drvinfo = ixgb_get_drvinfo, | |
675 | .get_regs_len = ixgb_get_regs_len, | |
676 | .get_regs = ixgb_get_regs, | |
677 | .get_link = ethtool_op_get_link, | |
678 | .get_eeprom_len = ixgb_get_eeprom_len, | |
679 | .get_eeprom = ixgb_get_eeprom, | |
680 | .set_eeprom = ixgb_set_eeprom, | |
681 | .get_ringparam = ixgb_get_ringparam, | |
682 | .set_ringparam = ixgb_set_ringparam, | |
683 | .get_pauseparam = ixgb_get_pauseparam, | |
684 | .set_pauseparam = ixgb_set_pauseparam, | |
685 | .get_rx_csum = ixgb_get_rx_csum, | |
686 | .set_rx_csum = ixgb_set_rx_csum, | |
687 | .get_tx_csum = ixgb_get_tx_csum, | |
688 | .set_tx_csum = ixgb_set_tx_csum, | |
689 | .get_sg = ethtool_op_get_sg, | |
690 | .set_sg = ethtool_op_set_sg, | |
691 | #ifdef NETIF_F_TSO | |
692 | .get_tso = ethtool_op_get_tso, | |
693 | .set_tso = ixgb_set_tso, | |
694 | #endif | |
695 | .get_strings = ixgb_get_strings, | |
696 | .phys_id = ixgb_phys_id, | |
697 | .get_stats_count = ixgb_get_stats_count, | |
698 | .get_ethtool_stats = ixgb_get_ethtool_stats, | |
699 | }; | |
700 | ||
701 | void ixgb_set_ethtool_ops(struct net_device *netdev) | |
702 | { | |
703 | SET_ETHTOOL_OPS(netdev, &ixgb_ethtool_ops); | |
704 | } |