diff options
Diffstat (limited to 'drivers/net/benet/be_ethtool.c')
-rw-r--r-- | drivers/net/benet/be_ethtool.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/drivers/net/benet/be_ethtool.c b/drivers/net/benet/be_ethtool.c new file mode 100644 index 000000000000..04f4b73fa8d8 --- /dev/null +++ b/drivers/net/benet/be_ethtool.c | |||
@@ -0,0 +1,362 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005 - 2009 ServerEngines | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License version 2 | ||
7 | * as published by the Free Software Foundation. The full GNU General | ||
8 | * Public License is included in this distribution in the file called COPYING. | ||
9 | * | ||
10 | * Contact Information: | ||
11 | * linux-drivers@serverengines.com | ||
12 | * | ||
13 | * ServerEngines | ||
14 | * 209 N. Fair Oaks Ave | ||
15 | * Sunnyvale, CA 94085 | ||
16 | */ | ||
17 | |||
18 | #include "be.h" | ||
19 | #include <linux/ethtool.h> | ||
20 | |||
21 | struct be_ethtool_stat { | ||
22 | char desc[ETH_GSTRING_LEN]; | ||
23 | int type; | ||
24 | int size; | ||
25 | int offset; | ||
26 | }; | ||
27 | |||
28 | enum {NETSTAT, PORTSTAT, MISCSTAT, DRVSTAT, ERXSTAT}; | ||
29 | #define FIELDINFO(_struct, field) FIELD_SIZEOF(_struct, field), \ | ||
30 | offsetof(_struct, field) | ||
31 | #define NETSTAT_INFO(field) #field, NETSTAT,\ | ||
32 | FIELDINFO(struct net_device_stats,\ | ||
33 | field) | ||
34 | #define DRVSTAT_INFO(field) #field, DRVSTAT,\ | ||
35 | FIELDINFO(struct be_drvr_stats, field) | ||
36 | #define MISCSTAT_INFO(field) #field, MISCSTAT,\ | ||
37 | FIELDINFO(struct be_rxf_stats, field) | ||
38 | #define PORTSTAT_INFO(field) #field, PORTSTAT,\ | ||
39 | FIELDINFO(struct be_port_rxf_stats, \ | ||
40 | field) | ||
41 | #define ERXSTAT_INFO(field) #field, ERXSTAT,\ | ||
42 | FIELDINFO(struct be_erx_stats, field) | ||
43 | |||
44 | static const struct be_ethtool_stat et_stats[] = { | ||
45 | {NETSTAT_INFO(rx_packets)}, | ||
46 | {NETSTAT_INFO(tx_packets)}, | ||
47 | {NETSTAT_INFO(rx_bytes)}, | ||
48 | {NETSTAT_INFO(tx_bytes)}, | ||
49 | {NETSTAT_INFO(rx_errors)}, | ||
50 | {NETSTAT_INFO(tx_errors)}, | ||
51 | {NETSTAT_INFO(rx_dropped)}, | ||
52 | {NETSTAT_INFO(tx_dropped)}, | ||
53 | {DRVSTAT_INFO(be_tx_reqs)}, | ||
54 | {DRVSTAT_INFO(be_tx_stops)}, | ||
55 | {DRVSTAT_INFO(be_fwd_reqs)}, | ||
56 | {DRVSTAT_INFO(be_tx_wrbs)}, | ||
57 | {DRVSTAT_INFO(be_polls)}, | ||
58 | {DRVSTAT_INFO(be_tx_events)}, | ||
59 | {DRVSTAT_INFO(be_rx_events)}, | ||
60 | {DRVSTAT_INFO(be_tx_compl)}, | ||
61 | {DRVSTAT_INFO(be_rx_compl)}, | ||
62 | {DRVSTAT_INFO(be_ethrx_post_fail)}, | ||
63 | {DRVSTAT_INFO(be_802_3_dropped_frames)}, | ||
64 | {DRVSTAT_INFO(be_802_3_malformed_frames)}, | ||
65 | {DRVSTAT_INFO(be_tx_rate)}, | ||
66 | {DRVSTAT_INFO(be_rx_rate)}, | ||
67 | {PORTSTAT_INFO(rx_unicast_frames)}, | ||
68 | {PORTSTAT_INFO(rx_multicast_frames)}, | ||
69 | {PORTSTAT_INFO(rx_broadcast_frames)}, | ||
70 | {PORTSTAT_INFO(rx_crc_errors)}, | ||
71 | {PORTSTAT_INFO(rx_alignment_symbol_errors)}, | ||
72 | {PORTSTAT_INFO(rx_pause_frames)}, | ||
73 | {PORTSTAT_INFO(rx_control_frames)}, | ||
74 | {PORTSTAT_INFO(rx_in_range_errors)}, | ||
75 | {PORTSTAT_INFO(rx_out_range_errors)}, | ||
76 | {PORTSTAT_INFO(rx_frame_too_long)}, | ||
77 | {PORTSTAT_INFO(rx_address_match_errors)}, | ||
78 | {PORTSTAT_INFO(rx_vlan_mismatch)}, | ||
79 | {PORTSTAT_INFO(rx_dropped_too_small)}, | ||
80 | {PORTSTAT_INFO(rx_dropped_too_short)}, | ||
81 | {PORTSTAT_INFO(rx_dropped_header_too_small)}, | ||
82 | {PORTSTAT_INFO(rx_dropped_tcp_length)}, | ||
83 | {PORTSTAT_INFO(rx_dropped_runt)}, | ||
84 | {PORTSTAT_INFO(rx_fifo_overflow)}, | ||
85 | {PORTSTAT_INFO(rx_input_fifo_overflow)}, | ||
86 | {PORTSTAT_INFO(rx_ip_checksum_errs)}, | ||
87 | {PORTSTAT_INFO(rx_tcp_checksum_errs)}, | ||
88 | {PORTSTAT_INFO(rx_udp_checksum_errs)}, | ||
89 | {PORTSTAT_INFO(rx_non_rss_packets)}, | ||
90 | {PORTSTAT_INFO(rx_ipv4_packets)}, | ||
91 | {PORTSTAT_INFO(rx_ipv6_packets)}, | ||
92 | {PORTSTAT_INFO(tx_unicastframes)}, | ||
93 | {PORTSTAT_INFO(tx_multicastframes)}, | ||
94 | {PORTSTAT_INFO(tx_broadcastframes)}, | ||
95 | {PORTSTAT_INFO(tx_pauseframes)}, | ||
96 | {PORTSTAT_INFO(tx_controlframes)}, | ||
97 | {MISCSTAT_INFO(rx_drops_no_pbuf)}, | ||
98 | {MISCSTAT_INFO(rx_drops_no_txpb)}, | ||
99 | {MISCSTAT_INFO(rx_drops_no_erx_descr)}, | ||
100 | {MISCSTAT_INFO(rx_drops_no_tpre_descr)}, | ||
101 | {MISCSTAT_INFO(rx_drops_too_many_frags)}, | ||
102 | {MISCSTAT_INFO(rx_drops_invalid_ring)}, | ||
103 | {MISCSTAT_INFO(forwarded_packets)}, | ||
104 | {MISCSTAT_INFO(rx_drops_mtu)}, | ||
105 | {ERXSTAT_INFO(rx_drops_no_fragments)}, | ||
106 | }; | ||
107 | #define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats) | ||
108 | |||
109 | static void | ||
110 | be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) | ||
111 | { | ||
112 | struct be_adapter *adapter = netdev_priv(netdev); | ||
113 | |||
114 | strcpy(drvinfo->driver, DRV_NAME); | ||
115 | strcpy(drvinfo->version, DRV_VER); | ||
116 | strncpy(drvinfo->fw_version, adapter->fw_ver, FW_VER_LEN); | ||
117 | strcpy(drvinfo->bus_info, pci_name(adapter->pdev)); | ||
118 | drvinfo->testinfo_len = 0; | ||
119 | drvinfo->regdump_len = 0; | ||
120 | drvinfo->eedump_len = 0; | ||
121 | } | ||
122 | |||
123 | static int | ||
124 | be_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) | ||
125 | { | ||
126 | struct be_adapter *adapter = netdev_priv(netdev); | ||
127 | struct be_eq_obj *rx_eq = &adapter->rx_eq; | ||
128 | struct be_eq_obj *tx_eq = &adapter->tx_eq; | ||
129 | |||
130 | coalesce->rx_max_coalesced_frames = adapter->max_rx_coal; | ||
131 | |||
132 | coalesce->rx_coalesce_usecs = rx_eq->cur_eqd; | ||
133 | coalesce->rx_coalesce_usecs_high = rx_eq->max_eqd; | ||
134 | coalesce->rx_coalesce_usecs_low = rx_eq->min_eqd; | ||
135 | |||
136 | coalesce->tx_coalesce_usecs = tx_eq->cur_eqd; | ||
137 | coalesce->tx_coalesce_usecs_high = tx_eq->max_eqd; | ||
138 | coalesce->tx_coalesce_usecs_low = tx_eq->min_eqd; | ||
139 | |||
140 | coalesce->use_adaptive_rx_coalesce = rx_eq->enable_aic; | ||
141 | coalesce->use_adaptive_tx_coalesce = tx_eq->enable_aic; | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * This routine is used to set interrup coalescing delay *as well as* | ||
148 | * the number of pkts to coalesce for LRO. | ||
149 | */ | ||
150 | static int | ||
151 | be_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) | ||
152 | { | ||
153 | struct be_adapter *adapter = netdev_priv(netdev); | ||
154 | struct be_ctrl_info *ctrl = &adapter->ctrl; | ||
155 | struct be_eq_obj *rx_eq = &adapter->rx_eq; | ||
156 | struct be_eq_obj *tx_eq = &adapter->tx_eq; | ||
157 | u32 tx_max, tx_min, tx_cur; | ||
158 | u32 rx_max, rx_min, rx_cur; | ||
159 | int status = 0; | ||
160 | |||
161 | if (coalesce->use_adaptive_tx_coalesce == 1) | ||
162 | return -EINVAL; | ||
163 | |||
164 | adapter->max_rx_coal = coalesce->rx_max_coalesced_frames; | ||
165 | if (adapter->max_rx_coal > MAX_SKB_FRAGS) | ||
166 | adapter->max_rx_coal = MAX_SKB_FRAGS - 1; | ||
167 | |||
168 | /* if AIC is being turned on now, start with an EQD of 0 */ | ||
169 | if (rx_eq->enable_aic == 0 && | ||
170 | coalesce->use_adaptive_rx_coalesce == 1) { | ||
171 | rx_eq->cur_eqd = 0; | ||
172 | } | ||
173 | rx_eq->enable_aic = coalesce->use_adaptive_rx_coalesce; | ||
174 | |||
175 | rx_max = coalesce->rx_coalesce_usecs_high; | ||
176 | rx_min = coalesce->rx_coalesce_usecs_low; | ||
177 | rx_cur = coalesce->rx_coalesce_usecs; | ||
178 | |||
179 | tx_max = coalesce->tx_coalesce_usecs_high; | ||
180 | tx_min = coalesce->tx_coalesce_usecs_low; | ||
181 | tx_cur = coalesce->tx_coalesce_usecs; | ||
182 | |||
183 | if (tx_cur > BE_MAX_EQD) | ||
184 | tx_cur = BE_MAX_EQD; | ||
185 | if (tx_eq->cur_eqd != tx_cur) { | ||
186 | status = be_cmd_modify_eqd(ctrl, tx_eq->q.id, tx_cur); | ||
187 | if (!status) | ||
188 | tx_eq->cur_eqd = tx_cur; | ||
189 | } | ||
190 | |||
191 | if (rx_eq->enable_aic) { | ||
192 | if (rx_max > BE_MAX_EQD) | ||
193 | rx_max = BE_MAX_EQD; | ||
194 | if (rx_min > rx_max) | ||
195 | rx_min = rx_max; | ||
196 | rx_eq->max_eqd = rx_max; | ||
197 | rx_eq->min_eqd = rx_min; | ||
198 | if (rx_eq->cur_eqd > rx_max) | ||
199 | rx_eq->cur_eqd = rx_max; | ||
200 | if (rx_eq->cur_eqd < rx_min) | ||
201 | rx_eq->cur_eqd = rx_min; | ||
202 | } else { | ||
203 | if (rx_cur > BE_MAX_EQD) | ||
204 | rx_cur = BE_MAX_EQD; | ||
205 | if (rx_eq->cur_eqd != rx_cur) { | ||
206 | status = be_cmd_modify_eqd(ctrl, rx_eq->q.id, rx_cur); | ||
207 | if (!status) | ||
208 | rx_eq->cur_eqd = rx_cur; | ||
209 | } | ||
210 | } | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static u32 be_get_rx_csum(struct net_device *netdev) | ||
215 | { | ||
216 | struct be_adapter *adapter = netdev_priv(netdev); | ||
217 | |||
218 | return adapter->rx_csum; | ||
219 | } | ||
220 | |||
221 | static int be_set_rx_csum(struct net_device *netdev, uint32_t data) | ||
222 | { | ||
223 | struct be_adapter *adapter = netdev_priv(netdev); | ||
224 | |||
225 | if (data) | ||
226 | adapter->rx_csum = true; | ||
227 | else | ||
228 | adapter->rx_csum = false; | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static void | ||
234 | be_get_ethtool_stats(struct net_device *netdev, | ||
235 | struct ethtool_stats *stats, uint64_t *data) | ||
236 | { | ||
237 | struct be_adapter *adapter = netdev_priv(netdev); | ||
238 | struct be_drvr_stats *drvr_stats = &adapter->stats.drvr_stats; | ||
239 | struct be_hw_stats *hw_stats = hw_stats_from_cmd(adapter->stats.cmd.va); | ||
240 | struct be_rxf_stats *rxf_stats = &hw_stats->rxf; | ||
241 | struct be_port_rxf_stats *port_stats = | ||
242 | &rxf_stats->port[adapter->port_num]; | ||
243 | struct net_device_stats *net_stats = &adapter->stats.net_stats; | ||
244 | struct be_erx_stats *erx_stats = &hw_stats->erx; | ||
245 | void *p = NULL; | ||
246 | int i; | ||
247 | |||
248 | for (i = 0; i < ETHTOOL_STATS_NUM; i++) { | ||
249 | switch (et_stats[i].type) { | ||
250 | case NETSTAT: | ||
251 | p = net_stats; | ||
252 | break; | ||
253 | case DRVSTAT: | ||
254 | p = drvr_stats; | ||
255 | break; | ||
256 | case PORTSTAT: | ||
257 | p = port_stats; | ||
258 | break; | ||
259 | case MISCSTAT: | ||
260 | p = rxf_stats; | ||
261 | break; | ||
262 | case ERXSTAT: /* Currently only one ERX stat is provided */ | ||
263 | p = (u32 *)erx_stats + adapter->rx_obj.q.id; | ||
264 | break; | ||
265 | } | ||
266 | |||
267 | p = (u8 *)p + et_stats[i].offset; | ||
268 | data[i] = (et_stats[i].size == sizeof(u64)) ? | ||
269 | *(u64 *)p: *(u32 *)p; | ||
270 | } | ||
271 | |||
272 | return; | ||
273 | } | ||
274 | |||
275 | static void | ||
276 | be_get_stat_strings(struct net_device *netdev, uint32_t stringset, | ||
277 | uint8_t *data) | ||
278 | { | ||
279 | int i; | ||
280 | switch (stringset) { | ||
281 | case ETH_SS_STATS: | ||
282 | for (i = 0; i < ETHTOOL_STATS_NUM; i++) { | ||
283 | memcpy(data, et_stats[i].desc, ETH_GSTRING_LEN); | ||
284 | data += ETH_GSTRING_LEN; | ||
285 | } | ||
286 | break; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | static int be_get_stats_count(struct net_device *netdev) | ||
291 | { | ||
292 | return ETHTOOL_STATS_NUM; | ||
293 | } | ||
294 | |||
295 | static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) | ||
296 | { | ||
297 | ecmd->speed = SPEED_10000; | ||
298 | ecmd->duplex = DUPLEX_FULL; | ||
299 | ecmd->autoneg = AUTONEG_DISABLE; | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static void | ||
304 | be_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) | ||
305 | { | ||
306 | struct be_adapter *adapter = netdev_priv(netdev); | ||
307 | |||
308 | ring->rx_max_pending = adapter->rx_obj.q.len; | ||
309 | ring->tx_max_pending = adapter->tx_obj.q.len; | ||
310 | |||
311 | ring->rx_pending = atomic_read(&adapter->rx_obj.q.used); | ||
312 | ring->tx_pending = atomic_read(&adapter->tx_obj.q.used); | ||
313 | } | ||
314 | |||
315 | static void | ||
316 | be_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd) | ||
317 | { | ||
318 | struct be_adapter *adapter = netdev_priv(netdev); | ||
319 | |||
320 | be_cmd_get_flow_control(&adapter->ctrl, &ecmd->tx_pause, | ||
321 | &ecmd->rx_pause); | ||
322 | ecmd->autoneg = AUTONEG_ENABLE; | ||
323 | } | ||
324 | |||
325 | static int | ||
326 | be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd) | ||
327 | { | ||
328 | struct be_adapter *adapter = netdev_priv(netdev); | ||
329 | int status; | ||
330 | |||
331 | if (ecmd->autoneg != AUTONEG_ENABLE) | ||
332 | return -EINVAL; | ||
333 | |||
334 | status = be_cmd_set_flow_control(&adapter->ctrl, ecmd->tx_pause, | ||
335 | ecmd->rx_pause); | ||
336 | if (!status) | ||
337 | dev_warn(&adapter->pdev->dev, "Pause param set failed.\n"); | ||
338 | |||
339 | return status; | ||
340 | } | ||
341 | |||
342 | struct ethtool_ops be_ethtool_ops = { | ||
343 | .get_settings = be_get_settings, | ||
344 | .get_drvinfo = be_get_drvinfo, | ||
345 | .get_link = ethtool_op_get_link, | ||
346 | .get_coalesce = be_get_coalesce, | ||
347 | .set_coalesce = be_set_coalesce, | ||
348 | .get_ringparam = be_get_ringparam, | ||
349 | .get_pauseparam = be_get_pauseparam, | ||
350 | .set_pauseparam = be_set_pauseparam, | ||
351 | .get_rx_csum = be_get_rx_csum, | ||
352 | .set_rx_csum = be_set_rx_csum, | ||
353 | .get_tx_csum = ethtool_op_get_tx_csum, | ||
354 | .set_tx_csum = ethtool_op_set_tx_csum, | ||
355 | .get_sg = ethtool_op_get_sg, | ||
356 | .set_sg = ethtool_op_set_sg, | ||
357 | .get_tso = ethtool_op_get_tso, | ||
358 | .set_tso = ethtool_op_set_tso, | ||
359 | .get_strings = be_get_stat_strings, | ||
360 | .get_stats_count = be_get_stats_count, | ||
361 | .get_ethtool_stats = be_get_ethtool_stats, | ||
362 | }; | ||