aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/sfc/ethtool.c')
-rw-r--r--drivers/net/sfc/ethtool.c460
1 files changed, 460 insertions, 0 deletions
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
new file mode 100644
index 000000000000..ad541badbd98
--- /dev/null
+++ b/drivers/net/sfc/ethtool.c
@@ -0,0 +1,460 @@
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
21static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable);
22
23struct ethtool_string {
24 char name[ETH_GSTRING_LEN];
25};
26
27struct 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
50static u64 efx_get_uint_stat(void *field)
51{
52 return *(unsigned int *)field;
53}
54
55static u64 efx_get_ulong_stat(void *field)
56{
57 return *(unsigned long *)field;
58}
59
60static u64 efx_get_u64_stat(void *field)
61{
62 return *(u64 *) field;
63}
64
65static 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
90static 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 */
170static 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. */
181int 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. */
195int 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
210static 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
220static int efx_ethtool_get_stats_count(struct net_device *net_dev)
221{
222 return EFX_ETHTOOL_NUM_STATS;
223}
224
225static 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
239static 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
275static 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
289static 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
301static 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 */
309static 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
316static 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
323static 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 */
361static 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
404static 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
427static 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
438struct 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};