Commit | Line | Data |
---|---|---|
8ceee660 BH |
1 | /**************************************************************************** |
2 | * Driver for Solarflare Solarstorm network controllers and boards | |
3 | * Copyright 2005-2006 Fen Systems Ltd. | |
4 | * Copyright 2006-2008 Solarflare Communications Inc. | |
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 version 2 as published | |
8 | * by the Free Software Foundation, incorporated herein by reference. | |
9 | */ | |
10 | ||
11 | #include <linux/netdevice.h> | |
12 | #include <linux/ethtool.h> | |
13 | #include <linux/rtnetlink.h> | |
14 | #include "net_driver.h" | |
15 | #include "efx.h" | |
16 | #include "ethtool.h" | |
17 | #include "falcon.h" | |
18 | #include "gmii.h" | |
19 | #include "mac.h" | |
20 | ||
21 | static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable); | |
22 | ||
23 | struct ethtool_string { | |
24 | char name[ETH_GSTRING_LEN]; | |
25 | }; | |
26 | ||
27 | struct efx_ethtool_stat { | |
28 | const char *name; | |
29 | enum { | |
30 | EFX_ETHTOOL_STAT_SOURCE_mac_stats, | |
31 | EFX_ETHTOOL_STAT_SOURCE_nic, | |
32 | EFX_ETHTOOL_STAT_SOURCE_channel | |
33 | } source; | |
34 | unsigned offset; | |
35 | u64(*get_stat) (void *field); /* Reader function */ | |
36 | }; | |
37 | ||
38 | /* Initialiser for a struct #efx_ethtool_stat with type-checking */ | |
39 | #define EFX_ETHTOOL_STAT(stat_name, source_name, field, field_type, \ | |
40 | get_stat_function) { \ | |
41 | .name = #stat_name, \ | |
42 | .source = EFX_ETHTOOL_STAT_SOURCE_##source_name, \ | |
43 | .offset = ((((field_type *) 0) == \ | |
44 | &((struct efx_##source_name *)0)->field) ? \ | |
45 | offsetof(struct efx_##source_name, field) : \ | |
46 | offsetof(struct efx_##source_name, field)), \ | |
47 | .get_stat = get_stat_function, \ | |
48 | } | |
49 | ||
50 | static u64 efx_get_uint_stat(void *field) | |
51 | { | |
52 | return *(unsigned int *)field; | |
53 | } | |
54 | ||
55 | static u64 efx_get_ulong_stat(void *field) | |
56 | { | |
57 | return *(unsigned long *)field; | |
58 | } | |
59 | ||
60 | static u64 efx_get_u64_stat(void *field) | |
61 | { | |
62 | return *(u64 *) field; | |
63 | } | |
64 | ||
65 | static u64 efx_get_atomic_stat(void *field) | |
66 | { | |
67 | return atomic_read((atomic_t *) field); | |
68 | } | |
69 | ||
70 | #define EFX_ETHTOOL_ULONG_MAC_STAT(field) \ | |
71 | EFX_ETHTOOL_STAT(field, mac_stats, field, \ | |
72 | unsigned long, efx_get_ulong_stat) | |
73 | ||
74 | #define EFX_ETHTOOL_U64_MAC_STAT(field) \ | |
75 | EFX_ETHTOOL_STAT(field, mac_stats, field, \ | |
76 | u64, efx_get_u64_stat) | |
77 | ||
78 | #define EFX_ETHTOOL_UINT_NIC_STAT(name) \ | |
79 | EFX_ETHTOOL_STAT(name, nic, n_##name, \ | |
80 | unsigned int, efx_get_uint_stat) | |
81 | ||
82 | #define EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \ | |
83 | EFX_ETHTOOL_STAT(field, nic, field, \ | |
84 | atomic_t, efx_get_atomic_stat) | |
85 | ||
86 | #define EFX_ETHTOOL_UINT_CHANNEL_STAT(field) \ | |
87 | EFX_ETHTOOL_STAT(field, channel, n_##field, \ | |
88 | unsigned int, efx_get_uint_stat) | |
89 | ||
90 | static struct efx_ethtool_stat efx_ethtool_stats[] = { | |
91 | EFX_ETHTOOL_U64_MAC_STAT(tx_bytes), | |
92 | EFX_ETHTOOL_U64_MAC_STAT(tx_good_bytes), | |
93 | EFX_ETHTOOL_U64_MAC_STAT(tx_bad_bytes), | |
94 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_packets), | |
95 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_bad), | |
96 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_pause), | |
97 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_control), | |
98 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_unicast), | |
99 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_multicast), | |
100 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_broadcast), | |
101 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_lt64), | |
102 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_64), | |
103 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_65_to_127), | |
104 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_128_to_255), | |
105 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_256_to_511), | |
106 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_512_to_1023), | |
107 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_1024_to_15xx), | |
108 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_15xx_to_jumbo), | |
109 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_gtjumbo), | |
110 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_collision), | |
111 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_single_collision), | |
112 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_multiple_collision), | |
113 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_collision), | |
114 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_deferred), | |
115 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_late_collision), | |
116 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_deferred), | |
117 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_non_tcpudp), | |
118 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_mac_src_error), | |
119 | EFX_ETHTOOL_ULONG_MAC_STAT(tx_ip_src_error), | |
120 | EFX_ETHTOOL_U64_MAC_STAT(rx_bytes), | |
121 | EFX_ETHTOOL_U64_MAC_STAT(rx_good_bytes), | |
122 | EFX_ETHTOOL_U64_MAC_STAT(rx_bad_bytes), | |
123 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_packets), | |
124 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_good), | |
125 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad), | |
126 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_pause), | |
127 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_control), | |
128 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_unicast), | |
129 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_multicast), | |
130 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_broadcast), | |
131 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_lt64), | |
132 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_64), | |
133 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_65_to_127), | |
134 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_128_to_255), | |
135 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_256_to_511), | |
136 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_512_to_1023), | |
137 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_1024_to_15xx), | |
138 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_15xx_to_jumbo), | |
139 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_gtjumbo), | |
140 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_lt64), | |
141 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_64_to_15xx), | |
142 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_15xx_to_jumbo), | |
143 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_gtjumbo), | |
144 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_overflow), | |
145 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_missed), | |
146 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_false_carrier), | |
147 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_symbol_error), | |
148 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_align_error), | |
149 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_length_error), | |
150 | EFX_ETHTOOL_ULONG_MAC_STAT(rx_internal_error), | |
151 | EFX_ETHTOOL_UINT_NIC_STAT(rx_nodesc_drop_cnt), | |
152 | EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset), | |
153 | EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), | |
154 | EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), | |
155 | EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err), | |
156 | EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc), | |
157 | }; | |
158 | ||
159 | /* Number of ethtool statistics */ | |
160 | #define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats) | |
161 | ||
162 | /************************************************************************** | |
163 | * | |
164 | * Ethtool operations | |
165 | * | |
166 | ************************************************************************** | |
167 | */ | |
168 | ||
169 | /* Identify device by flashing LEDs */ | |
170 | static int efx_ethtool_phys_id(struct net_device *net_dev, u32 seconds) | |
171 | { | |
172 | struct efx_nic *efx = net_dev->priv; | |
173 | ||
174 | efx->board_info.blink(efx, 1); | |
175 | schedule_timeout_interruptible(seconds * HZ); | |
176 | efx->board_info.blink(efx, 0); | |
177 | return 0; | |
178 | } | |
179 | ||
180 | /* This must be called with rtnl_lock held. */ | |
181 | int efx_ethtool_get_settings(struct net_device *net_dev, | |
182 | struct ethtool_cmd *ecmd) | |
183 | { | |
184 | struct efx_nic *efx = net_dev->priv; | |
185 | int rc; | |
186 | ||
187 | mutex_lock(&efx->mac_lock); | |
188 | rc = falcon_xmac_get_settings(efx, ecmd); | |
189 | mutex_unlock(&efx->mac_lock); | |
190 | ||
191 | return rc; | |
192 | } | |
193 | ||
194 | /* This must be called with rtnl_lock held. */ | |
195 | int efx_ethtool_set_settings(struct net_device *net_dev, | |
196 | struct ethtool_cmd *ecmd) | |
197 | { | |
198 | struct efx_nic *efx = net_dev->priv; | |
199 | int rc; | |
200 | ||
201 | mutex_lock(&efx->mac_lock); | |
202 | rc = falcon_xmac_set_settings(efx, ecmd); | |
203 | mutex_unlock(&efx->mac_lock); | |
204 | if (!rc) | |
205 | efx_reconfigure_port(efx); | |
206 | ||
207 | return rc; | |
208 | } | |
209 | ||
210 | static void efx_ethtool_get_drvinfo(struct net_device *net_dev, | |
211 | struct ethtool_drvinfo *info) | |
212 | { | |
213 | struct efx_nic *efx = net_dev->priv; | |
214 | ||
215 | strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver)); | |
216 | strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); | |
217 | strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); | |
218 | } | |
219 | ||
220 | static int efx_ethtool_get_stats_count(struct net_device *net_dev) | |
221 | { | |
222 | return EFX_ETHTOOL_NUM_STATS; | |
223 | } | |
224 | ||
225 | static void efx_ethtool_get_strings(struct net_device *net_dev, | |
226 | u32 string_set, u8 *strings) | |
227 | { | |
228 | struct ethtool_string *ethtool_strings = | |
229 | (struct ethtool_string *)strings; | |
230 | int i; | |
231 | ||
232 | if (string_set == ETH_SS_STATS) | |
233 | for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) | |
234 | strncpy(ethtool_strings[i].name, | |
235 | efx_ethtool_stats[i].name, | |
236 | sizeof(ethtool_strings[i].name)); | |
237 | } | |
238 | ||
239 | static void efx_ethtool_get_stats(struct net_device *net_dev, | |
240 | struct ethtool_stats *stats, | |
241 | u64 *data) | |
242 | { | |
243 | struct efx_nic *efx = net_dev->priv; | |
244 | struct efx_mac_stats *mac_stats = &efx->mac_stats; | |
245 | struct efx_ethtool_stat *stat; | |
246 | struct efx_channel *channel; | |
247 | int i; | |
248 | ||
249 | EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS); | |
250 | ||
251 | /* Update MAC and NIC statistics */ | |
252 | net_dev->get_stats(net_dev); | |
253 | ||
254 | /* Fill detailed statistics buffer */ | |
255 | for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) { | |
256 | stat = &efx_ethtool_stats[i]; | |
257 | switch (stat->source) { | |
258 | case EFX_ETHTOOL_STAT_SOURCE_mac_stats: | |
259 | data[i] = stat->get_stat((void *)mac_stats + | |
260 | stat->offset); | |
261 | break; | |
262 | case EFX_ETHTOOL_STAT_SOURCE_nic: | |
263 | data[i] = stat->get_stat((void *)efx + stat->offset); | |
264 | break; | |
265 | case EFX_ETHTOOL_STAT_SOURCE_channel: | |
266 | data[i] = 0; | |
267 | efx_for_each_channel(channel, efx) | |
268 | data[i] += stat->get_stat((void *)channel + | |
269 | stat->offset); | |
270 | break; | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) | |
276 | { | |
277 | struct efx_nic *efx = net_dev->priv; | |
278 | int rc; | |
279 | ||
280 | rc = ethtool_op_set_tx_csum(net_dev, enable); | |
281 | if (rc) | |
282 | return rc; | |
283 | ||
284 | efx_flush_queues(efx); | |
285 | ||
286 | return 0; | |
287 | } | |
288 | ||
289 | static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable) | |
290 | { | |
291 | struct efx_nic *efx = net_dev->priv; | |
292 | ||
293 | /* No way to stop the hardware doing the checks; we just | |
294 | * ignore the result. | |
295 | */ | |
296 | efx->rx_checksum_enabled = (enable ? 1 : 0); | |
297 | ||
298 | return 0; | |
299 | } | |
300 | ||
301 | static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev) | |
302 | { | |
303 | struct efx_nic *efx = net_dev->priv; | |
304 | ||
305 | return efx->rx_checksum_enabled; | |
306 | } | |
307 | ||
308 | /* Restart autonegotiation */ | |
309 | static int efx_ethtool_nway_reset(struct net_device *net_dev) | |
310 | { | |
311 | struct efx_nic *efx = net_dev->priv; | |
312 | ||
313 | return mii_nway_restart(&efx->mii); | |
314 | } | |
315 | ||
316 | static u32 efx_ethtool_get_link(struct net_device *net_dev) | |
317 | { | |
318 | struct efx_nic *efx = net_dev->priv; | |
319 | ||
320 | return efx->link_up; | |
321 | } | |
322 | ||
323 | static int efx_ethtool_get_coalesce(struct net_device *net_dev, | |
324 | struct ethtool_coalesce *coalesce) | |
325 | { | |
326 | struct efx_nic *efx = net_dev->priv; | |
327 | struct efx_tx_queue *tx_queue; | |
328 | struct efx_rx_queue *rx_queue; | |
329 | struct efx_channel *channel; | |
330 | ||
331 | memset(coalesce, 0, sizeof(*coalesce)); | |
332 | ||
333 | /* Find lowest IRQ moderation across all used TX queues */ | |
334 | coalesce->tx_coalesce_usecs_irq = ~((u32) 0); | |
335 | efx_for_each_tx_queue(tx_queue, efx) { | |
336 | channel = tx_queue->channel; | |
337 | if (channel->irq_moderation < coalesce->tx_coalesce_usecs_irq) { | |
338 | if (channel->used_flags != EFX_USED_BY_RX_TX) | |
339 | coalesce->tx_coalesce_usecs_irq = | |
340 | channel->irq_moderation; | |
341 | else | |
342 | coalesce->tx_coalesce_usecs_irq = 0; | |
343 | } | |
344 | } | |
345 | ||
346 | /* Find lowest IRQ moderation across all used RX queues */ | |
347 | coalesce->rx_coalesce_usecs_irq = ~((u32) 0); | |
348 | efx_for_each_rx_queue(rx_queue, efx) { | |
349 | channel = rx_queue->channel; | |
350 | if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq) | |
351 | coalesce->rx_coalesce_usecs_irq = | |
352 | channel->irq_moderation; | |
353 | } | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
358 | /* Set coalescing parameters | |
359 | * The difficulties occur for shared channels | |
360 | */ | |
361 | static int efx_ethtool_set_coalesce(struct net_device *net_dev, | |
362 | struct ethtool_coalesce *coalesce) | |
363 | { | |
364 | struct efx_nic *efx = net_dev->priv; | |
365 | struct efx_channel *channel; | |
366 | struct efx_tx_queue *tx_queue; | |
367 | unsigned tx_usecs, rx_usecs; | |
368 | ||
369 | if (coalesce->use_adaptive_rx_coalesce || | |
370 | coalesce->use_adaptive_tx_coalesce) | |
371 | return -EOPNOTSUPP; | |
372 | ||
373 | if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) { | |
374 | EFX_ERR(efx, "invalid coalescing setting. " | |
375 | "Only rx/tx_coalesce_usecs_irq are supported\n"); | |
376 | return -EOPNOTSUPP; | |
377 | } | |
378 | ||
379 | rx_usecs = coalesce->rx_coalesce_usecs_irq; | |
380 | tx_usecs = coalesce->tx_coalesce_usecs_irq; | |
381 | ||
382 | /* If the channel is shared only allow RX parameters to be set */ | |
383 | efx_for_each_tx_queue(tx_queue, efx) { | |
384 | if ((tx_queue->channel->used_flags == EFX_USED_BY_RX_TX) && | |
385 | tx_usecs) { | |
386 | EFX_ERR(efx, "Channel is shared. " | |
387 | "Only RX coalescing may be set\n"); | |
388 | return -EOPNOTSUPP; | |
389 | } | |
390 | } | |
391 | ||
392 | efx_init_irq_moderation(efx, tx_usecs, rx_usecs); | |
393 | ||
394 | /* Reset channel to pick up new moderation value. Note that | |
395 | * this may change the value of the irq_moderation field | |
396 | * (e.g. to allow for hardware timer granularity). | |
397 | */ | |
398 | efx_for_each_channel(channel, efx) | |
399 | falcon_set_int_moderation(channel); | |
400 | ||
401 | return 0; | |
402 | } | |
403 | ||
404 | static int efx_ethtool_set_pauseparam(struct net_device *net_dev, | |
405 | struct ethtool_pauseparam *pause) | |
406 | { | |
407 | struct efx_nic *efx = net_dev->priv; | |
408 | enum efx_fc_type flow_control = efx->flow_control; | |
409 | int rc; | |
410 | ||
411 | flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO); | |
412 | flow_control |= pause->rx_pause ? EFX_FC_RX : 0; | |
413 | flow_control |= pause->tx_pause ? EFX_FC_TX : 0; | |
414 | flow_control |= pause->autoneg ? EFX_FC_AUTO : 0; | |
415 | ||
416 | /* Try to push the pause parameters */ | |
417 | mutex_lock(&efx->mac_lock); | |
418 | rc = falcon_xmac_set_pause(efx, flow_control); | |
419 | mutex_unlock(&efx->mac_lock); | |
420 | ||
421 | if (!rc) | |
422 | efx_reconfigure_port(efx); | |
423 | ||
424 | return rc; | |
425 | } | |
426 | ||
427 | static void efx_ethtool_get_pauseparam(struct net_device *net_dev, | |
428 | struct ethtool_pauseparam *pause) | |
429 | { | |
430 | struct efx_nic *efx = net_dev->priv; | |
431 | ||
432 | pause->rx_pause = (efx->flow_control & EFX_FC_RX) ? 1 : 0; | |
433 | pause->tx_pause = (efx->flow_control & EFX_FC_TX) ? 1 : 0; | |
434 | pause->autoneg = (efx->flow_control & EFX_FC_AUTO) ? 1 : 0; | |
435 | } | |
436 | ||
437 | ||
438 | struct ethtool_ops efx_ethtool_ops = { | |
439 | .get_settings = efx_ethtool_get_settings, | |
440 | .set_settings = efx_ethtool_set_settings, | |
441 | .get_drvinfo = efx_ethtool_get_drvinfo, | |
442 | .nway_reset = efx_ethtool_nway_reset, | |
443 | .get_link = efx_ethtool_get_link, | |
444 | .get_coalesce = efx_ethtool_get_coalesce, | |
445 | .set_coalesce = efx_ethtool_set_coalesce, | |
446 | .get_pauseparam = efx_ethtool_get_pauseparam, | |
447 | .set_pauseparam = efx_ethtool_set_pauseparam, | |
448 | .get_rx_csum = efx_ethtool_get_rx_csum, | |
449 | .set_rx_csum = efx_ethtool_set_rx_csum, | |
450 | .get_tx_csum = ethtool_op_get_tx_csum, | |
451 | .set_tx_csum = efx_ethtool_set_tx_csum, | |
452 | .get_sg = ethtool_op_get_sg, | |
453 | .set_sg = ethtool_op_set_sg, | |
454 | .get_flags = ethtool_op_get_flags, | |
455 | .set_flags = ethtool_op_set_flags, | |
456 | .get_strings = efx_ethtool_get_strings, | |
457 | .phys_id = efx_ethtool_phys_id, | |
458 | .get_stats_count = efx_ethtool_get_stats_count, | |
459 | .get_ethtool_stats = efx_ethtool_get_stats, | |
460 | }; |