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