1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * QLogic qlcnic NIC Driver |
4 | * Copyright (c) 2009-2013 QLogic Corporation |
5 | */ |
6 | |
7 | #include <linux/types.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/pci.h> |
10 | #include <linux/io.h> |
11 | #include <linux/netdevice.h> |
12 | #include <linux/ethtool.h> |
13 | |
14 | #include "qlcnic.h" |
15 | |
16 | struct qlcnic_stats { |
17 | char stat_string[ETH_GSTRING_LEN]; |
18 | int sizeof_stat; |
19 | int stat_offset; |
20 | }; |
21 | |
22 | #define QLC_SIZEOF(m) sizeof_field(struct qlcnic_adapter, m) |
23 | #define QLC_OFF(m) offsetof(struct qlcnic_adapter, m) |
24 | static const u32 qlcnic_fw_dump_level[] = { |
25 | 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff |
26 | }; |
27 | |
28 | static const struct qlcnic_stats qlcnic_gstrings_stats[] = { |
29 | {"xmit_on" , QLC_SIZEOF(stats.xmit_on), QLC_OFF(stats.xmit_on)}, |
30 | {"xmit_off" , QLC_SIZEOF(stats.xmit_off), QLC_OFF(stats.xmit_off)}, |
31 | {"xmit_called" , QLC_SIZEOF(stats.xmitcalled), |
32 | QLC_OFF(stats.xmitcalled)}, |
33 | {"xmit_finished" , QLC_SIZEOF(stats.xmitfinished), |
34 | QLC_OFF(stats.xmitfinished)}, |
35 | {"tx dma map error" , QLC_SIZEOF(stats.tx_dma_map_error), |
36 | QLC_OFF(stats.tx_dma_map_error)}, |
37 | {"tx_bytes" , QLC_SIZEOF(stats.txbytes), QLC_OFF(stats.txbytes)}, |
38 | {"tx_dropped" , QLC_SIZEOF(stats.txdropped), QLC_OFF(stats.txdropped)}, |
39 | {"rx dma map error" , QLC_SIZEOF(stats.rx_dma_map_error), |
40 | QLC_OFF(stats.rx_dma_map_error)}, |
41 | {"rx_pkts" , QLC_SIZEOF(stats.rx_pkts), QLC_OFF(stats.rx_pkts)}, |
42 | {"rx_bytes" , QLC_SIZEOF(stats.rxbytes), QLC_OFF(stats.rxbytes)}, |
43 | {"rx_dropped" , QLC_SIZEOF(stats.rxdropped), QLC_OFF(stats.rxdropped)}, |
44 | {"null rxbuf" , QLC_SIZEOF(stats.null_rxbuf), QLC_OFF(stats.null_rxbuf)}, |
45 | {"csummed" , QLC_SIZEOF(stats.csummed), QLC_OFF(stats.csummed)}, |
46 | {"lro_pkts" , QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)}, |
47 | {"lrobytes" , QLC_SIZEOF(stats.lrobytes), QLC_OFF(stats.lrobytes)}, |
48 | {"lso_frames" , QLC_SIZEOF(stats.lso_frames), QLC_OFF(stats.lso_frames)}, |
49 | {"encap_lso_frames" , QLC_SIZEOF(stats.encap_lso_frames), |
50 | QLC_OFF(stats.encap_lso_frames)}, |
51 | {"encap_tx_csummed" , QLC_SIZEOF(stats.encap_tx_csummed), |
52 | QLC_OFF(stats.encap_tx_csummed)}, |
53 | {"encap_rx_csummed" , QLC_SIZEOF(stats.encap_rx_csummed), |
54 | QLC_OFF(stats.encap_rx_csummed)}, |
55 | {"skb_alloc_failure" , QLC_SIZEOF(stats.skb_alloc_failure), |
56 | QLC_OFF(stats.skb_alloc_failure)}, |
57 | {"mac_filter_limit_overrun" , QLC_SIZEOF(stats.mac_filter_limit_overrun), |
58 | QLC_OFF(stats.mac_filter_limit_overrun)}, |
59 | {"spurious intr" , QLC_SIZEOF(stats.spurious_intr), |
60 | QLC_OFF(stats.spurious_intr)}, |
61 | {"mbx spurious intr" , QLC_SIZEOF(stats.mbx_spurious_intr), |
62 | QLC_OFF(stats.mbx_spurious_intr)}, |
63 | }; |
64 | |
65 | static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = { |
66 | "tx unicast frames" , |
67 | "tx multicast frames" , |
68 | "tx broadcast frames" , |
69 | "tx dropped frames" , |
70 | "tx errors" , |
71 | "tx local frames" , |
72 | "tx numbytes" , |
73 | "rx unicast frames" , |
74 | "rx multicast frames" , |
75 | "rx broadcast frames" , |
76 | "rx dropped frames" , |
77 | "rx errors" , |
78 | "rx local frames" , |
79 | "rx numbytes" , |
80 | }; |
81 | |
82 | static const char qlcnic_83xx_tx_stats_strings[][ETH_GSTRING_LEN] = { |
83 | "ctx_tx_bytes" , |
84 | "ctx_tx_pkts" , |
85 | "ctx_tx_errors" , |
86 | "ctx_tx_dropped_pkts" , |
87 | "ctx_tx_num_buffers" , |
88 | }; |
89 | |
90 | static const char qlcnic_83xx_mac_stats_strings[][ETH_GSTRING_LEN] = { |
91 | "mac_tx_frames" , |
92 | "mac_tx_bytes" , |
93 | "mac_tx_mcast_pkts" , |
94 | "mac_tx_bcast_pkts" , |
95 | "mac_tx_pause_cnt" , |
96 | "mac_tx_ctrl_pkt" , |
97 | "mac_tx_lt_64b_pkts" , |
98 | "mac_tx_lt_127b_pkts" , |
99 | "mac_tx_lt_255b_pkts" , |
100 | "mac_tx_lt_511b_pkts" , |
101 | "mac_tx_lt_1023b_pkts" , |
102 | "mac_tx_lt_1518b_pkts" , |
103 | "mac_tx_gt_1518b_pkts" , |
104 | "mac_rx_frames" , |
105 | "mac_rx_bytes" , |
106 | "mac_rx_mcast_pkts" , |
107 | "mac_rx_bcast_pkts" , |
108 | "mac_rx_pause_cnt" , |
109 | "mac_rx_ctrl_pkt" , |
110 | "mac_rx_lt_64b_pkts" , |
111 | "mac_rx_lt_127b_pkts" , |
112 | "mac_rx_lt_255b_pkts" , |
113 | "mac_rx_lt_511b_pkts" , |
114 | "mac_rx_lt_1023b_pkts" , |
115 | "mac_rx_lt_1518b_pkts" , |
116 | "mac_rx_gt_1518b_pkts" , |
117 | "mac_rx_length_error" , |
118 | "mac_rx_length_small" , |
119 | "mac_rx_length_large" , |
120 | "mac_rx_jabber" , |
121 | "mac_rx_dropped" , |
122 | "mac_crc_error" , |
123 | "mac_align_error" , |
124 | "eswitch_frames" , |
125 | "eswitch_bytes" , |
126 | "eswitch_multicast_frames" , |
127 | "eswitch_broadcast_frames" , |
128 | "eswitch_unicast_frames" , |
129 | "eswitch_error_free_frames" , |
130 | "eswitch_error_free_bytes" , |
131 | }; |
132 | |
133 | #define QLCNIC_STATS_LEN ARRAY_SIZE(qlcnic_gstrings_stats) |
134 | |
135 | static const char qlcnic_tx_queue_stats_strings[][ETH_GSTRING_LEN] = { |
136 | "xmit_on" , |
137 | "xmit_off" , |
138 | "xmit_called" , |
139 | "xmit_finished" , |
140 | "tx_bytes" , |
141 | }; |
142 | |
143 | #define QLCNIC_TX_STATS_LEN ARRAY_SIZE(qlcnic_tx_queue_stats_strings) |
144 | |
145 | static const char qlcnic_83xx_rx_stats_strings[][ETH_GSTRING_LEN] = { |
146 | "ctx_rx_bytes" , |
147 | "ctx_rx_pkts" , |
148 | "ctx_lro_pkt_cnt" , |
149 | "ctx_ip_csum_error" , |
150 | "ctx_rx_pkts_wo_ctx" , |
151 | "ctx_rx_pkts_drop_wo_sds_on_card" , |
152 | "ctx_rx_pkts_drop_wo_sds_on_host" , |
153 | "ctx_rx_osized_pkts" , |
154 | "ctx_rx_pkts_dropped_wo_rds" , |
155 | "ctx_rx_unexpected_mcast_pkts" , |
156 | "ctx_invalid_mac_address" , |
157 | "ctx_rx_rds_ring_prim_attempted" , |
158 | "ctx_rx_rds_ring_prim_success" , |
159 | "ctx_num_lro_flows_added" , |
160 | "ctx_num_lro_flows_removed" , |
161 | "ctx_num_lro_flows_active" , |
162 | "ctx_pkts_dropped_unknown" , |
163 | }; |
164 | |
165 | static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { |
166 | "Register_Test_on_offline" , |
167 | "Link_Test_on_offline" , |
168 | "Interrupt_Test_offline" , |
169 | "Internal_Loopback_offline" , |
170 | "External_Loopback_offline" , |
171 | "EEPROM_Test_offline" |
172 | }; |
173 | |
174 | #define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) |
175 | |
176 | static inline int qlcnic_82xx_statistics(struct qlcnic_adapter *adapter) |
177 | { |
178 | return ARRAY_SIZE(qlcnic_gstrings_stats) + |
179 | ARRAY_SIZE(qlcnic_83xx_mac_stats_strings) + |
180 | QLCNIC_TX_STATS_LEN * adapter->drv_tx_rings; |
181 | } |
182 | |
183 | static inline int qlcnic_83xx_statistics(struct qlcnic_adapter *adapter) |
184 | { |
185 | return ARRAY_SIZE(qlcnic_gstrings_stats) + |
186 | ARRAY_SIZE(qlcnic_83xx_tx_stats_strings) + |
187 | ARRAY_SIZE(qlcnic_83xx_mac_stats_strings) + |
188 | ARRAY_SIZE(qlcnic_83xx_rx_stats_strings) + |
189 | QLCNIC_TX_STATS_LEN * adapter->drv_tx_rings; |
190 | } |
191 | |
192 | static int qlcnic_dev_statistics_len(struct qlcnic_adapter *adapter) |
193 | { |
194 | int len = -1; |
195 | |
196 | if (qlcnic_82xx_check(adapter)) { |
197 | len = qlcnic_82xx_statistics(adapter); |
198 | if (adapter->flags & QLCNIC_ESWITCH_ENABLED) |
199 | len += ARRAY_SIZE(qlcnic_device_gstrings_stats); |
200 | } else if (qlcnic_83xx_check(adapter)) { |
201 | len = qlcnic_83xx_statistics(adapter); |
202 | } |
203 | |
204 | return len; |
205 | } |
206 | |
207 | #define QLCNIC_TX_INTR_NOT_CONFIGURED 0X78563412 |
208 | |
209 | #define QLCNIC_MAX_EEPROM_LEN 1024 |
210 | |
211 | static const u32 diag_registers[] = { |
212 | QLCNIC_CMDPEG_STATE, |
213 | QLCNIC_RCVPEG_STATE, |
214 | QLCNIC_FW_CAPABILITIES, |
215 | QLCNIC_CRB_DRV_ACTIVE, |
216 | QLCNIC_CRB_DEV_STATE, |
217 | QLCNIC_CRB_DRV_STATE, |
218 | QLCNIC_CRB_DRV_SCRATCH, |
219 | QLCNIC_CRB_DEV_PARTITION_INFO, |
220 | QLCNIC_CRB_DRV_IDC_VER, |
221 | QLCNIC_PEG_ALIVE_COUNTER, |
222 | QLCNIC_PEG_HALT_STATUS1, |
223 | QLCNIC_PEG_HALT_STATUS2, |
224 | -1 |
225 | }; |
226 | |
227 | |
228 | static const u32 ext_diag_registers[] = { |
229 | CRB_XG_STATE_P3P, |
230 | ISR_INT_STATE_REG, |
231 | QLCNIC_CRB_PEG_NET_0+0x3c, |
232 | QLCNIC_CRB_PEG_NET_1+0x3c, |
233 | QLCNIC_CRB_PEG_NET_2+0x3c, |
234 | QLCNIC_CRB_PEG_NET_4+0x3c, |
235 | -1 |
236 | }; |
237 | |
238 | #define QLCNIC_MGMT_API_VERSION 3 |
239 | #define QLCNIC_ETHTOOL_REGS_VER 4 |
240 | |
241 | static inline int qlcnic_get_ring_regs_len(struct qlcnic_adapter *adapter) |
242 | { |
243 | int ring_regs_cnt = (adapter->drv_tx_rings * 5) + |
244 | (adapter->max_rds_rings * 2) + |
245 | (adapter->drv_sds_rings * 3) + 5; |
246 | return ring_regs_cnt * sizeof(u32); |
247 | } |
248 | |
249 | static int qlcnic_get_regs_len(struct net_device *dev) |
250 | { |
251 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
252 | u32 len; |
253 | |
254 | if (qlcnic_83xx_check(adapter)) |
255 | len = qlcnic_83xx_get_regs_len(adapter); |
256 | else |
257 | len = sizeof(ext_diag_registers) + sizeof(diag_registers); |
258 | |
259 | len += ((QLCNIC_DEV_INFO_SIZE + 2) * sizeof(u32)); |
260 | len += qlcnic_get_ring_regs_len(adapter); |
261 | return len; |
262 | } |
263 | |
264 | static int qlcnic_get_eeprom_len(struct net_device *dev) |
265 | { |
266 | return QLCNIC_FLASH_TOTAL_SIZE; |
267 | } |
268 | |
269 | static void |
270 | qlcnic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) |
271 | { |
272 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
273 | u32 fw_major, fw_minor, fw_build; |
274 | fw_major = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_MAJOR); |
275 | fw_minor = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_MINOR); |
276 | fw_build = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_SUB); |
277 | snprintf(buf: drvinfo->fw_version, size: sizeof(drvinfo->fw_version), |
278 | fmt: "%d.%d.%d" , fw_major, fw_minor, fw_build); |
279 | |
280 | strscpy(drvinfo->bus_info, pci_name(adapter->pdev), |
281 | sizeof(drvinfo->bus_info)); |
282 | strscpy(drvinfo->driver, qlcnic_driver_name, sizeof(drvinfo->driver)); |
283 | strscpy(drvinfo->version, QLCNIC_LINUX_VERSIONID, |
284 | sizeof(drvinfo->version)); |
285 | } |
286 | |
287 | static int qlcnic_82xx_get_link_ksettings(struct qlcnic_adapter *adapter, |
288 | struct ethtool_link_ksettings *ecmd) |
289 | { |
290 | struct qlcnic_hardware_context *ahw = adapter->ahw; |
291 | u32 speed, reg; |
292 | int check_sfp_module = 0, err = 0; |
293 | u16 pcifn = ahw->pci_func; |
294 | u32 supported, advertising; |
295 | |
296 | /* read which mode */ |
297 | if (adapter->ahw->port_type == QLCNIC_GBE) { |
298 | supported = (SUPPORTED_10baseT_Half | |
299 | SUPPORTED_10baseT_Full | |
300 | SUPPORTED_100baseT_Half | |
301 | SUPPORTED_100baseT_Full | |
302 | SUPPORTED_1000baseT_Half | |
303 | SUPPORTED_1000baseT_Full); |
304 | |
305 | advertising = (ADVERTISED_100baseT_Half | |
306 | ADVERTISED_100baseT_Full | |
307 | ADVERTISED_1000baseT_Half | |
308 | ADVERTISED_1000baseT_Full); |
309 | |
310 | ecmd->base.speed = adapter->ahw->link_speed; |
311 | ecmd->base.duplex = adapter->ahw->link_duplex; |
312 | ecmd->base.autoneg = adapter->ahw->link_autoneg; |
313 | |
314 | } else if (adapter->ahw->port_type == QLCNIC_XGBE) { |
315 | u32 val = 0; |
316 | val = QLCRD32(adapter, QLCNIC_PORT_MODE_ADDR, &err); |
317 | |
318 | if (val == QLCNIC_PORT_MODE_802_3_AP) { |
319 | supported = SUPPORTED_1000baseT_Full; |
320 | advertising = ADVERTISED_1000baseT_Full; |
321 | } else { |
322 | supported = SUPPORTED_10000baseT_Full; |
323 | advertising = ADVERTISED_10000baseT_Full; |
324 | } |
325 | |
326 | if (netif_running(dev: adapter->netdev) && ahw->has_link_events) { |
327 | if (ahw->linkup) { |
328 | reg = QLCRD32(adapter, |
329 | P3P_LINK_SPEED_REG(pcifn), &err); |
330 | speed = P3P_LINK_SPEED_VAL(pcifn, reg); |
331 | ahw->link_speed = speed * P3P_LINK_SPEED_MHZ; |
332 | } |
333 | |
334 | ecmd->base.speed = ahw->link_speed; |
335 | ecmd->base.autoneg = ahw->link_autoneg; |
336 | ecmd->base.duplex = ahw->link_duplex; |
337 | goto skip; |
338 | } |
339 | |
340 | ecmd->base.speed = SPEED_UNKNOWN; |
341 | ecmd->base.duplex = DUPLEX_UNKNOWN; |
342 | ecmd->base.autoneg = AUTONEG_DISABLE; |
343 | } else |
344 | return -EIO; |
345 | |
346 | skip: |
347 | ecmd->base.phy_address = adapter->ahw->physical_port; |
348 | |
349 | switch (adapter->ahw->board_type) { |
350 | case QLCNIC_BRDTYPE_P3P_REF_QG: |
351 | case QLCNIC_BRDTYPE_P3P_4_GB: |
352 | case QLCNIC_BRDTYPE_P3P_4_GB_MM: |
353 | supported |= SUPPORTED_Autoneg; |
354 | advertising |= ADVERTISED_Autoneg; |
355 | fallthrough; |
356 | case QLCNIC_BRDTYPE_P3P_10G_CX4: |
357 | case QLCNIC_BRDTYPE_P3P_10G_CX4_LP: |
358 | case QLCNIC_BRDTYPE_P3P_10000_BASE_T: |
359 | supported |= SUPPORTED_TP; |
360 | advertising |= ADVERTISED_TP; |
361 | ecmd->base.port = PORT_TP; |
362 | ecmd->base.autoneg = adapter->ahw->link_autoneg; |
363 | break; |
364 | case QLCNIC_BRDTYPE_P3P_IMEZ: |
365 | case QLCNIC_BRDTYPE_P3P_XG_LOM: |
366 | case QLCNIC_BRDTYPE_P3P_HMEZ: |
367 | supported |= SUPPORTED_MII; |
368 | advertising |= ADVERTISED_MII; |
369 | ecmd->base.port = PORT_MII; |
370 | ecmd->base.autoneg = AUTONEG_DISABLE; |
371 | break; |
372 | case QLCNIC_BRDTYPE_P3P_10G_SFP_PLUS: |
373 | case QLCNIC_BRDTYPE_P3P_10G_SFP_CT: |
374 | case QLCNIC_BRDTYPE_P3P_10G_SFP_QT: |
375 | advertising |= ADVERTISED_TP; |
376 | supported |= SUPPORTED_TP; |
377 | check_sfp_module = netif_running(dev: adapter->netdev) && |
378 | ahw->has_link_events; |
379 | fallthrough; |
380 | case QLCNIC_BRDTYPE_P3P_10G_XFP: |
381 | supported |= SUPPORTED_FIBRE; |
382 | advertising |= ADVERTISED_FIBRE; |
383 | ecmd->base.port = PORT_FIBRE; |
384 | ecmd->base.autoneg = AUTONEG_DISABLE; |
385 | break; |
386 | case QLCNIC_BRDTYPE_P3P_10G_TP: |
387 | if (adapter->ahw->port_type == QLCNIC_XGBE) { |
388 | ecmd->base.autoneg = AUTONEG_DISABLE; |
389 | supported |= (SUPPORTED_FIBRE | SUPPORTED_TP); |
390 | advertising |= |
391 | (ADVERTISED_FIBRE | ADVERTISED_TP); |
392 | ecmd->base.port = PORT_FIBRE; |
393 | check_sfp_module = netif_running(dev: adapter->netdev) && |
394 | ahw->has_link_events; |
395 | } else { |
396 | ecmd->base.autoneg = AUTONEG_ENABLE; |
397 | supported |= (SUPPORTED_TP | SUPPORTED_Autoneg); |
398 | advertising |= |
399 | (ADVERTISED_TP | ADVERTISED_Autoneg); |
400 | ecmd->base.port = PORT_TP; |
401 | } |
402 | break; |
403 | default: |
404 | dev_err(&adapter->pdev->dev, "Unsupported board model %d\n" , |
405 | adapter->ahw->board_type); |
406 | return -EIO; |
407 | } |
408 | |
409 | if (check_sfp_module) { |
410 | switch (adapter->ahw->module_type) { |
411 | case LINKEVENT_MODULE_OPTICAL_UNKNOWN: |
412 | case LINKEVENT_MODULE_OPTICAL_SRLR: |
413 | case LINKEVENT_MODULE_OPTICAL_LRM: |
414 | case LINKEVENT_MODULE_OPTICAL_SFP_1G: |
415 | ecmd->base.port = PORT_FIBRE; |
416 | break; |
417 | case LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE: |
418 | case LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN: |
419 | case LINKEVENT_MODULE_TWINAX: |
420 | ecmd->base.port = PORT_TP; |
421 | break; |
422 | default: |
423 | ecmd->base.port = PORT_OTHER; |
424 | } |
425 | } |
426 | |
427 | ethtool_convert_legacy_u32_to_link_mode(dst: ecmd->link_modes.supported, |
428 | legacy_u32: supported); |
429 | ethtool_convert_legacy_u32_to_link_mode(dst: ecmd->link_modes.advertising, |
430 | legacy_u32: advertising); |
431 | |
432 | return 0; |
433 | } |
434 | |
435 | static int qlcnic_get_link_ksettings(struct net_device *dev, |
436 | struct ethtool_link_ksettings *ecmd) |
437 | { |
438 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
439 | |
440 | if (qlcnic_82xx_check(adapter)) |
441 | return qlcnic_82xx_get_link_ksettings(adapter, ecmd); |
442 | else if (qlcnic_83xx_check(adapter)) |
443 | return qlcnic_83xx_get_link_ksettings(adapter, ecmd); |
444 | |
445 | return -EIO; |
446 | } |
447 | |
448 | |
449 | static int qlcnic_set_port_config(struct qlcnic_adapter *adapter, |
450 | const struct ethtool_link_ksettings *ecmd) |
451 | { |
452 | u32 ret = 0, config = 0; |
453 | /* read which mode */ |
454 | if (ecmd->base.duplex) |
455 | config |= 0x1; |
456 | |
457 | if (ecmd->base.autoneg) |
458 | config |= 0x2; |
459 | |
460 | switch (ecmd->base.speed) { |
461 | case SPEED_10: |
462 | config |= (0 << 8); |
463 | break; |
464 | case SPEED_100: |
465 | config |= (1 << 8); |
466 | break; |
467 | case SPEED_1000: |
468 | config |= (10 << 8); |
469 | break; |
470 | default: |
471 | return -EIO; |
472 | } |
473 | |
474 | ret = qlcnic_fw_cmd_set_port(adapter, config); |
475 | |
476 | if (ret == QLCNIC_RCODE_NOT_SUPPORTED) |
477 | return -EOPNOTSUPP; |
478 | else if (ret) |
479 | return -EIO; |
480 | return ret; |
481 | } |
482 | |
483 | static int qlcnic_set_link_ksettings(struct net_device *dev, |
484 | const struct ethtool_link_ksettings *ecmd) |
485 | { |
486 | u32 ret = 0; |
487 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
488 | |
489 | if (qlcnic_83xx_check(adapter)) |
490 | qlcnic_83xx_get_port_type(adapter); |
491 | |
492 | if (adapter->ahw->port_type != QLCNIC_GBE) |
493 | return -EOPNOTSUPP; |
494 | |
495 | if (qlcnic_83xx_check(adapter)) |
496 | ret = qlcnic_83xx_set_link_ksettings(adapter, ecmd); |
497 | else |
498 | ret = qlcnic_set_port_config(adapter, ecmd); |
499 | |
500 | if (!ret) |
501 | return ret; |
502 | |
503 | adapter->ahw->link_speed = ecmd->base.speed; |
504 | adapter->ahw->link_duplex = ecmd->base.duplex; |
505 | adapter->ahw->link_autoneg = ecmd->base.autoneg; |
506 | |
507 | if (!netif_running(dev)) |
508 | return 0; |
509 | |
510 | dev->netdev_ops->ndo_stop(dev); |
511 | return dev->netdev_ops->ndo_open(dev); |
512 | } |
513 | |
514 | static int qlcnic_82xx_get_registers(struct qlcnic_adapter *adapter, |
515 | u32 *regs_buff) |
516 | { |
517 | int i, j = 0, err = 0; |
518 | |
519 | for (i = QLCNIC_DEV_INFO_SIZE + 1; diag_registers[j] != -1; j++, i++) |
520 | regs_buff[i] = QLC_SHARED_REG_RD32(adapter, diag_registers[j]); |
521 | j = 0; |
522 | while (ext_diag_registers[j] != -1) |
523 | regs_buff[i++] = QLCRD32(adapter, ext_diag_registers[j++], |
524 | &err); |
525 | return i; |
526 | } |
527 | |
528 | static void |
529 | qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) |
530 | { |
531 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
532 | struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; |
533 | struct qlcnic_host_sds_ring *sds_ring; |
534 | struct qlcnic_host_rds_ring *rds_rings; |
535 | struct qlcnic_host_tx_ring *tx_ring; |
536 | u32 *regs_buff = p; |
537 | int ring, i = 0; |
538 | |
539 | memset(p, 0, qlcnic_get_regs_len(dev)); |
540 | |
541 | regs->version = (QLCNIC_ETHTOOL_REGS_VER << 24) | |
542 | (adapter->ahw->revision_id << 16) | (adapter->pdev)->device; |
543 | |
544 | regs_buff[0] = (0xcafe0000 | (QLCNIC_DEV_INFO_SIZE & 0xffff)); |
545 | regs_buff[1] = QLCNIC_MGMT_API_VERSION; |
546 | |
547 | if (adapter->ahw->capabilities & QLC_83XX_ESWITCH_CAPABILITY) |
548 | regs_buff[2] = adapter->ahw->max_vnic_func; |
549 | |
550 | if (qlcnic_82xx_check(adapter)) |
551 | i = qlcnic_82xx_get_registers(adapter, regs_buff); |
552 | else |
553 | i = qlcnic_83xx_get_registers(adapter, regs_buff); |
554 | |
555 | if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) |
556 | return; |
557 | |
558 | /* Marker btw regs and TX ring count */ |
559 | regs_buff[i++] = 0xFFEFCDAB; |
560 | |
561 | regs_buff[i++] = adapter->drv_tx_rings; /* No. of TX ring */ |
562 | for (ring = 0; ring < adapter->drv_tx_rings; ring++) { |
563 | tx_ring = &adapter->tx_ring[ring]; |
564 | regs_buff[i++] = le32_to_cpu(*(tx_ring->hw_consumer)); |
565 | regs_buff[i++] = tx_ring->sw_consumer; |
566 | regs_buff[i++] = readl(addr: tx_ring->crb_cmd_producer); |
567 | regs_buff[i++] = tx_ring->producer; |
568 | if (tx_ring->crb_intr_mask) |
569 | regs_buff[i++] = readl(addr: tx_ring->crb_intr_mask); |
570 | else |
571 | regs_buff[i++] = QLCNIC_TX_INTR_NOT_CONFIGURED; |
572 | } |
573 | |
574 | regs_buff[i++] = adapter->max_rds_rings; /* No. of RX ring */ |
575 | for (ring = 0; ring < adapter->max_rds_rings; ring++) { |
576 | rds_rings = &recv_ctx->rds_rings[ring]; |
577 | regs_buff[i++] = readl(addr: rds_rings->crb_rcv_producer); |
578 | regs_buff[i++] = rds_rings->producer; |
579 | } |
580 | |
581 | regs_buff[i++] = adapter->drv_sds_rings; /* No. of SDS ring */ |
582 | for (ring = 0; ring < adapter->drv_sds_rings; ring++) { |
583 | sds_ring = &(recv_ctx->sds_rings[ring]); |
584 | regs_buff[i++] = readl(addr: sds_ring->crb_sts_consumer); |
585 | regs_buff[i++] = sds_ring->consumer; |
586 | regs_buff[i++] = readl(addr: sds_ring->crb_intr_mask); |
587 | } |
588 | } |
589 | |
590 | static u32 qlcnic_test_link(struct net_device *dev) |
591 | { |
592 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
593 | int err = 0; |
594 | u32 val; |
595 | |
596 | if (qlcnic_83xx_check(adapter)) { |
597 | val = qlcnic_83xx_test_link(adapter); |
598 | return (val & 1) ? 0 : 1; |
599 | } |
600 | val = QLCRD32(adapter, CRB_XG_STATE_P3P, &err); |
601 | if (err == -EIO) |
602 | return err; |
603 | val = XG_LINK_STATE_P3P(adapter->ahw->pci_func, val); |
604 | return (val == XG_LINK_UP_P3P) ? 0 : 1; |
605 | } |
606 | |
607 | static int |
608 | qlcnic_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, |
609 | u8 *bytes) |
610 | { |
611 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
612 | int offset; |
613 | int ret = -1; |
614 | |
615 | if (qlcnic_83xx_check(adapter)) |
616 | return 0; |
617 | if (eeprom->len == 0) |
618 | return -EINVAL; |
619 | |
620 | eeprom->magic = (adapter->pdev)->vendor | |
621 | ((adapter->pdev)->device << 16); |
622 | offset = eeprom->offset; |
623 | |
624 | if (qlcnic_82xx_check(adapter)) |
625 | ret = qlcnic_rom_fast_read_words(adapter, addr: offset, bytes, |
626 | size: eeprom->len); |
627 | if (ret < 0) |
628 | return ret; |
629 | |
630 | return 0; |
631 | } |
632 | |
633 | static void |
634 | qlcnic_get_ringparam(struct net_device *dev, |
635 | struct ethtool_ringparam *ring, |
636 | struct kernel_ethtool_ringparam *kernel_ring, |
637 | struct netlink_ext_ack *extack) |
638 | { |
639 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
640 | |
641 | ring->rx_pending = adapter->num_rxd; |
642 | ring->rx_jumbo_pending = adapter->num_jumbo_rxd; |
643 | ring->tx_pending = adapter->num_txd; |
644 | |
645 | ring->rx_max_pending = adapter->max_rxd; |
646 | ring->rx_jumbo_max_pending = adapter->max_jumbo_rxd; |
647 | ring->tx_max_pending = MAX_CMD_DESCRIPTORS; |
648 | } |
649 | |
650 | static u32 |
651 | qlcnic_validate_ringparam(u32 val, u32 min, u32 max, char *r_name) |
652 | { |
653 | u32 num_desc; |
654 | num_desc = max(val, min); |
655 | num_desc = min(num_desc, max); |
656 | num_desc = roundup_pow_of_two(num_desc); |
657 | |
658 | if (val != num_desc) { |
659 | printk(KERN_INFO "%s: setting %s ring size %d instead of %d\n" , |
660 | qlcnic_driver_name, r_name, num_desc, val); |
661 | } |
662 | |
663 | return num_desc; |
664 | } |
665 | |
666 | static int |
667 | qlcnic_set_ringparam(struct net_device *dev, |
668 | struct ethtool_ringparam *ring, |
669 | struct kernel_ethtool_ringparam *kernel_ring, |
670 | struct netlink_ext_ack *extack) |
671 | { |
672 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
673 | u16 num_rxd, num_jumbo_rxd, num_txd; |
674 | |
675 | if (ring->rx_mini_pending) |
676 | return -EOPNOTSUPP; |
677 | |
678 | num_rxd = qlcnic_validate_ringparam(val: ring->rx_pending, |
679 | MIN_RCV_DESCRIPTORS, max: adapter->max_rxd, r_name: "rx" ); |
680 | |
681 | num_jumbo_rxd = qlcnic_validate_ringparam(val: ring->rx_jumbo_pending, |
682 | MIN_JUMBO_DESCRIPTORS, max: adapter->max_jumbo_rxd, |
683 | r_name: "rx jumbo" ); |
684 | |
685 | num_txd = qlcnic_validate_ringparam(val: ring->tx_pending, |
686 | MIN_CMD_DESCRIPTORS, MAX_CMD_DESCRIPTORS, r_name: "tx" ); |
687 | |
688 | if (num_rxd == adapter->num_rxd && num_txd == adapter->num_txd && |
689 | num_jumbo_rxd == adapter->num_jumbo_rxd) |
690 | return 0; |
691 | |
692 | adapter->num_rxd = num_rxd; |
693 | adapter->num_jumbo_rxd = num_jumbo_rxd; |
694 | adapter->num_txd = num_txd; |
695 | |
696 | return qlcnic_reset_context(adapter); |
697 | } |
698 | |
699 | static int qlcnic_validate_ring_count(struct qlcnic_adapter *adapter, |
700 | u8 rx_ring, u8 tx_ring) |
701 | { |
702 | if (rx_ring == 0 || tx_ring == 0) |
703 | return -EINVAL; |
704 | |
705 | if (rx_ring != 0) { |
706 | if (rx_ring > adapter->max_sds_rings) { |
707 | netdev_err(dev: adapter->netdev, |
708 | format: "Invalid ring count, SDS ring count %d should not be greater than max %d driver sds rings.\n" , |
709 | rx_ring, adapter->max_sds_rings); |
710 | return -EINVAL; |
711 | } |
712 | } |
713 | |
714 | if (tx_ring != 0) { |
715 | if (tx_ring > adapter->max_tx_rings) { |
716 | netdev_err(dev: adapter->netdev, |
717 | format: "Invalid ring count, Tx ring count %d should not be greater than max %d driver Tx rings.\n" , |
718 | tx_ring, adapter->max_tx_rings); |
719 | return -EINVAL; |
720 | } |
721 | } |
722 | |
723 | return 0; |
724 | } |
725 | |
726 | static void qlcnic_get_channels(struct net_device *dev, |
727 | struct ethtool_channels *channel) |
728 | { |
729 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
730 | |
731 | channel->max_rx = adapter->max_sds_rings; |
732 | channel->max_tx = adapter->max_tx_rings; |
733 | channel->rx_count = adapter->drv_sds_rings; |
734 | channel->tx_count = adapter->drv_tx_rings; |
735 | } |
736 | |
737 | static int qlcnic_set_channels(struct net_device *dev, |
738 | struct ethtool_channels *channel) |
739 | { |
740 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
741 | int err; |
742 | |
743 | if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) { |
744 | netdev_err(dev, format: "No RSS/TSS support in non MSI-X mode\n" ); |
745 | return -EINVAL; |
746 | } |
747 | |
748 | if (channel->other_count || channel->combined_count) |
749 | return -EINVAL; |
750 | |
751 | err = qlcnic_validate_ring_count(adapter, rx_ring: channel->rx_count, |
752 | tx_ring: channel->tx_count); |
753 | if (err) |
754 | return err; |
755 | |
756 | if (adapter->drv_sds_rings != channel->rx_count) { |
757 | err = qlcnic_validate_rings(adapter, channel->rx_count, |
758 | QLCNIC_RX_QUEUE); |
759 | if (err) { |
760 | netdev_err(dev, format: "Unable to configure %u SDS rings\n" , |
761 | channel->rx_count); |
762 | return err; |
763 | } |
764 | adapter->drv_rss_rings = channel->rx_count; |
765 | } |
766 | |
767 | if (adapter->drv_tx_rings != channel->tx_count) { |
768 | err = qlcnic_validate_rings(adapter, channel->tx_count, |
769 | QLCNIC_TX_QUEUE); |
770 | if (err) { |
771 | netdev_err(dev, format: "Unable to configure %u Tx rings\n" , |
772 | channel->tx_count); |
773 | return err; |
774 | } |
775 | adapter->drv_tss_rings = channel->tx_count; |
776 | } |
777 | |
778 | adapter->flags |= QLCNIC_TSS_RSS; |
779 | |
780 | err = qlcnic_setup_rings(adapter); |
781 | netdev_info(dev, format: "Allocated %d SDS rings and %d Tx rings\n" , |
782 | adapter->drv_sds_rings, adapter->drv_tx_rings); |
783 | |
784 | return err; |
785 | } |
786 | |
787 | static void |
788 | qlcnic_get_pauseparam(struct net_device *netdev, |
789 | struct ethtool_pauseparam *pause) |
790 | { |
791 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
792 | int port = adapter->ahw->physical_port; |
793 | int err = 0; |
794 | __u32 val; |
795 | |
796 | if (qlcnic_83xx_check(adapter)) { |
797 | qlcnic_83xx_get_pauseparam(adapter, pause); |
798 | return; |
799 | } |
800 | if (adapter->ahw->port_type == QLCNIC_GBE) { |
801 | if ((port < 0) || (port > QLCNIC_NIU_MAX_GBE_PORTS)) |
802 | return; |
803 | /* get flow control settings */ |
804 | val = QLCRD32(adapter, QLCNIC_NIU_GB_MAC_CONFIG_0(port), &err); |
805 | if (err == -EIO) |
806 | return; |
807 | pause->rx_pause = qlcnic_gb_get_rx_flowctl(val); |
808 | val = QLCRD32(adapter, QLCNIC_NIU_GB_PAUSE_CTL, &err); |
809 | if (err == -EIO) |
810 | return; |
811 | switch (port) { |
812 | case 0: |
813 | pause->tx_pause = !(qlcnic_gb_get_gb0_mask(val)); |
814 | break; |
815 | case 1: |
816 | pause->tx_pause = !(qlcnic_gb_get_gb1_mask(val)); |
817 | break; |
818 | case 2: |
819 | pause->tx_pause = !(qlcnic_gb_get_gb2_mask(val)); |
820 | break; |
821 | case 3: |
822 | default: |
823 | pause->tx_pause = !(qlcnic_gb_get_gb3_mask(val)); |
824 | break; |
825 | } |
826 | } else if (adapter->ahw->port_type == QLCNIC_XGBE) { |
827 | if ((port < 0) || (port > QLCNIC_NIU_MAX_XG_PORTS)) |
828 | return; |
829 | pause->rx_pause = 1; |
830 | val = QLCRD32(adapter, QLCNIC_NIU_XG_PAUSE_CTL, &err); |
831 | if (err == -EIO) |
832 | return; |
833 | if (port == 0) |
834 | pause->tx_pause = !(qlcnic_xg_get_xg0_mask(val)); |
835 | else |
836 | pause->tx_pause = !(qlcnic_xg_get_xg1_mask(val)); |
837 | } else { |
838 | dev_err(&netdev->dev, "Unknown board type: %x\n" , |
839 | adapter->ahw->port_type); |
840 | } |
841 | } |
842 | |
843 | static int |
844 | qlcnic_set_pauseparam(struct net_device *netdev, |
845 | struct ethtool_pauseparam *pause) |
846 | { |
847 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
848 | int port = adapter->ahw->physical_port; |
849 | int err = 0; |
850 | __u32 val; |
851 | |
852 | if (qlcnic_83xx_check(adapter)) |
853 | return qlcnic_83xx_set_pauseparam(adapter, pause); |
854 | |
855 | /* read mode */ |
856 | if (adapter->ahw->port_type == QLCNIC_GBE) { |
857 | if ((port < 0) || (port > QLCNIC_NIU_MAX_GBE_PORTS)) |
858 | return -EIO; |
859 | /* set flow control */ |
860 | val = QLCRD32(adapter, QLCNIC_NIU_GB_MAC_CONFIG_0(port), &err); |
861 | if (err == -EIO) |
862 | return err; |
863 | |
864 | if (pause->rx_pause) |
865 | qlcnic_gb_rx_flowctl(val); |
866 | else |
867 | qlcnic_gb_unset_rx_flowctl(val); |
868 | |
869 | QLCWR32(adapter, QLCNIC_NIU_GB_MAC_CONFIG_0(port), |
870 | val); |
871 | QLCWR32(adapter, QLCNIC_NIU_GB_MAC_CONFIG_0(port), val); |
872 | /* set autoneg */ |
873 | val = QLCRD32(adapter, QLCNIC_NIU_GB_PAUSE_CTL, &err); |
874 | if (err == -EIO) |
875 | return err; |
876 | switch (port) { |
877 | case 0: |
878 | if (pause->tx_pause) |
879 | qlcnic_gb_unset_gb0_mask(val); |
880 | else |
881 | qlcnic_gb_set_gb0_mask(val); |
882 | break; |
883 | case 1: |
884 | if (pause->tx_pause) |
885 | qlcnic_gb_unset_gb1_mask(val); |
886 | else |
887 | qlcnic_gb_set_gb1_mask(val); |
888 | break; |
889 | case 2: |
890 | if (pause->tx_pause) |
891 | qlcnic_gb_unset_gb2_mask(val); |
892 | else |
893 | qlcnic_gb_set_gb2_mask(val); |
894 | break; |
895 | case 3: |
896 | default: |
897 | if (pause->tx_pause) |
898 | qlcnic_gb_unset_gb3_mask(val); |
899 | else |
900 | qlcnic_gb_set_gb3_mask(val); |
901 | break; |
902 | } |
903 | QLCWR32(adapter, QLCNIC_NIU_GB_PAUSE_CTL, val); |
904 | } else if (adapter->ahw->port_type == QLCNIC_XGBE) { |
905 | if (!pause->rx_pause || pause->autoneg) |
906 | return -EOPNOTSUPP; |
907 | |
908 | if ((port < 0) || (port > QLCNIC_NIU_MAX_XG_PORTS)) |
909 | return -EIO; |
910 | |
911 | val = QLCRD32(adapter, QLCNIC_NIU_XG_PAUSE_CTL, &err); |
912 | if (err == -EIO) |
913 | return err; |
914 | if (port == 0) { |
915 | if (pause->tx_pause) |
916 | qlcnic_xg_unset_xg0_mask(val); |
917 | else |
918 | qlcnic_xg_set_xg0_mask(val); |
919 | } else { |
920 | if (pause->tx_pause) |
921 | qlcnic_xg_unset_xg1_mask(val); |
922 | else |
923 | qlcnic_xg_set_xg1_mask(val); |
924 | } |
925 | QLCWR32(adapter, QLCNIC_NIU_XG_PAUSE_CTL, val); |
926 | } else { |
927 | dev_err(&netdev->dev, "Unknown board type: %x\n" , |
928 | adapter->ahw->port_type); |
929 | } |
930 | return 0; |
931 | } |
932 | |
933 | static int qlcnic_reg_test(struct net_device *dev) |
934 | { |
935 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
936 | u32 data_read; |
937 | int err = 0; |
938 | |
939 | if (qlcnic_83xx_check(adapter)) |
940 | return qlcnic_83xx_reg_test(adapter); |
941 | |
942 | data_read = QLCRD32(adapter, QLCNIC_PCIX_PH_REG(0), &err); |
943 | if (err == -EIO) |
944 | return err; |
945 | if ((data_read & 0xffff) != adapter->pdev->vendor) |
946 | return 1; |
947 | |
948 | return 0; |
949 | } |
950 | |
951 | static int qlcnic_eeprom_test(struct net_device *dev) |
952 | { |
953 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
954 | |
955 | if (qlcnic_82xx_check(adapter)) |
956 | return 0; |
957 | |
958 | return qlcnic_83xx_flash_test(adapter); |
959 | } |
960 | |
961 | static int qlcnic_get_sset_count(struct net_device *dev, int sset) |
962 | { |
963 | |
964 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
965 | switch (sset) { |
966 | case ETH_SS_TEST: |
967 | return QLCNIC_TEST_LEN; |
968 | case ETH_SS_STATS: |
969 | return qlcnic_dev_statistics_len(adapter); |
970 | default: |
971 | return -EOPNOTSUPP; |
972 | } |
973 | } |
974 | |
975 | static int qlcnic_irq_test(struct net_device *netdev) |
976 | { |
977 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
978 | struct qlcnic_hardware_context *ahw = adapter->ahw; |
979 | struct qlcnic_cmd_args cmd; |
980 | int ret, drv_sds_rings = adapter->drv_sds_rings; |
981 | int drv_tx_rings = adapter->drv_tx_rings; |
982 | |
983 | if (qlcnic_83xx_check(adapter)) |
984 | return qlcnic_83xx_interrupt_test(netdev); |
985 | |
986 | if (test_and_set_bit(__QLCNIC_RESETTING, addr: &adapter->state)) |
987 | return -EIO; |
988 | |
989 | ret = qlcnic_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST); |
990 | if (ret) |
991 | goto clear_diag_irq; |
992 | |
993 | ahw->diag_cnt = 0; |
994 | ret = qlcnic_alloc_mbx_args(mbx: &cmd, adapter, QLCNIC_CMD_INTRPT_TEST); |
995 | if (ret) |
996 | goto free_diag_res; |
997 | |
998 | cmd.req.arg[1] = ahw->pci_func; |
999 | ret = qlcnic_issue_cmd(adapter, cmd: &cmd); |
1000 | if (ret) |
1001 | goto done; |
1002 | |
1003 | usleep_range(min: 1000, max: 12000); |
1004 | ret = !ahw->diag_cnt; |
1005 | |
1006 | done: |
1007 | qlcnic_free_mbx_args(cmd: &cmd); |
1008 | |
1009 | free_diag_res: |
1010 | qlcnic_diag_free_res(netdev, drv_sds_rings); |
1011 | |
1012 | clear_diag_irq: |
1013 | adapter->drv_sds_rings = drv_sds_rings; |
1014 | adapter->drv_tx_rings = drv_tx_rings; |
1015 | clear_bit(__QLCNIC_RESETTING, addr: &adapter->state); |
1016 | |
1017 | return ret; |
1018 | } |
1019 | |
1020 | #define QLCNIC_ILB_PKT_SIZE 64 |
1021 | #define QLCNIC_NUM_ILB_PKT 16 |
1022 | #define QLCNIC_ILB_MAX_RCV_LOOP 10 |
1023 | #define QLCNIC_LB_PKT_POLL_DELAY_MSEC 1 |
1024 | #define QLCNIC_LB_PKT_POLL_COUNT 20 |
1025 | |
1026 | static void qlcnic_create_loopback_buff(unsigned char *data, u8 mac[]) |
1027 | { |
1028 | static const unsigned char random_data[] = {0xa8, 0x06, 0x45, 0x00}; |
1029 | |
1030 | memset(data, 0x4e, QLCNIC_ILB_PKT_SIZE); |
1031 | |
1032 | memcpy(data, mac, ETH_ALEN); |
1033 | memcpy(data + ETH_ALEN, mac, ETH_ALEN); |
1034 | |
1035 | memcpy(data + 2 * ETH_ALEN, random_data, sizeof(random_data)); |
1036 | } |
1037 | |
1038 | int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]) |
1039 | { |
1040 | unsigned char buff[QLCNIC_ILB_PKT_SIZE]; |
1041 | qlcnic_create_loopback_buff(data: buff, mac); |
1042 | return memcmp(p: data, q: buff, QLCNIC_ILB_PKT_SIZE); |
1043 | } |
1044 | |
1045 | int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode) |
1046 | { |
1047 | struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; |
1048 | struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0]; |
1049 | struct sk_buff *skb; |
1050 | int i, loop, cnt = 0; |
1051 | |
1052 | for (i = 0; i < QLCNIC_NUM_ILB_PKT; i++) { |
1053 | skb = netdev_alloc_skb(dev: adapter->netdev, QLCNIC_ILB_PKT_SIZE); |
1054 | if (!skb) |
1055 | goto error; |
1056 | qlcnic_create_loopback_buff(data: skb->data, mac: adapter->mac_addr); |
1057 | skb_put(skb, QLCNIC_ILB_PKT_SIZE); |
1058 | adapter->ahw->diag_cnt = 0; |
1059 | qlcnic_xmit_frame(skb, adapter->netdev); |
1060 | loop = 0; |
1061 | |
1062 | do { |
1063 | msleep(QLCNIC_LB_PKT_POLL_DELAY_MSEC); |
1064 | qlcnic_process_rcv_ring_diag(sds_ring); |
1065 | if (loop++ > QLCNIC_LB_PKT_POLL_COUNT) |
1066 | break; |
1067 | } while (!adapter->ahw->diag_cnt); |
1068 | |
1069 | dev_kfree_skb_any(skb); |
1070 | |
1071 | if (!adapter->ahw->diag_cnt) |
1072 | dev_warn(&adapter->pdev->dev, |
1073 | "LB Test: packet #%d was not received\n" , |
1074 | i + 1); |
1075 | else |
1076 | cnt++; |
1077 | } |
1078 | if (cnt != i) { |
1079 | error: |
1080 | dev_err(&adapter->pdev->dev, |
1081 | "LB Test: failed, TX[%d], RX[%d]\n" , i, cnt); |
1082 | if (mode != QLCNIC_ILB_MODE) |
1083 | dev_warn(&adapter->pdev->dev, |
1084 | "WARNING: Please check loopback cable\n" ); |
1085 | return -1; |
1086 | } |
1087 | return 0; |
1088 | } |
1089 | |
1090 | static int qlcnic_loopback_test(struct net_device *netdev, u8 mode) |
1091 | { |
1092 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
1093 | int drv_tx_rings = adapter->drv_tx_rings; |
1094 | int drv_sds_rings = adapter->drv_sds_rings; |
1095 | struct qlcnic_host_sds_ring *sds_ring; |
1096 | struct qlcnic_hardware_context *ahw = adapter->ahw; |
1097 | int loop = 0; |
1098 | int ret; |
1099 | |
1100 | if (qlcnic_83xx_check(adapter)) |
1101 | return qlcnic_83xx_loopback_test(netdev, mode); |
1102 | |
1103 | if (!(ahw->capabilities & QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK)) { |
1104 | dev_info(&adapter->pdev->dev, |
1105 | "Firmware do not support loopback test\n" ); |
1106 | return -EOPNOTSUPP; |
1107 | } |
1108 | |
1109 | dev_warn(&adapter->pdev->dev, "%s loopback test in progress\n" , |
1110 | mode == QLCNIC_ILB_MODE ? "internal" : "external" ); |
1111 | if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { |
1112 | dev_warn(&adapter->pdev->dev, |
1113 | "Loopback test not supported in nonprivileged mode\n" ); |
1114 | return 0; |
1115 | } |
1116 | |
1117 | if (test_and_set_bit(__QLCNIC_RESETTING, addr: &adapter->state)) |
1118 | return -EBUSY; |
1119 | |
1120 | ret = qlcnic_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST); |
1121 | if (ret) |
1122 | goto clear_it; |
1123 | |
1124 | sds_ring = &adapter->recv_ctx->sds_rings[0]; |
1125 | ret = qlcnic_set_lb_mode(adapter, mode); |
1126 | if (ret) |
1127 | goto free_res; |
1128 | |
1129 | ahw->diag_cnt = 0; |
1130 | do { |
1131 | msleep(msecs: 500); |
1132 | qlcnic_process_rcv_ring_diag(sds_ring); |
1133 | if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) { |
1134 | netdev_info(dev: netdev, |
1135 | format: "Firmware didn't sent link up event to loopback request\n" ); |
1136 | ret = -ETIMEDOUT; |
1137 | goto free_res; |
1138 | } else if (adapter->ahw->diag_cnt) { |
1139 | ret = adapter->ahw->diag_cnt; |
1140 | goto free_res; |
1141 | } |
1142 | } while (!QLCNIC_IS_LB_CONFIGURED(ahw->loopback_state)); |
1143 | |
1144 | ret = qlcnic_do_lb_test(adapter, mode); |
1145 | |
1146 | qlcnic_clear_lb_mode(adapter, mode); |
1147 | |
1148 | free_res: |
1149 | qlcnic_diag_free_res(netdev, drv_sds_rings); |
1150 | |
1151 | clear_it: |
1152 | adapter->drv_sds_rings = drv_sds_rings; |
1153 | adapter->drv_tx_rings = drv_tx_rings; |
1154 | clear_bit(__QLCNIC_RESETTING, addr: &adapter->state); |
1155 | return ret; |
1156 | } |
1157 | |
1158 | static void |
1159 | qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, |
1160 | u64 *data) |
1161 | { |
1162 | memset(data, 0, sizeof(u64) * QLCNIC_TEST_LEN); |
1163 | |
1164 | data[0] = qlcnic_reg_test(dev); |
1165 | if (data[0]) |
1166 | eth_test->flags |= ETH_TEST_FL_FAILED; |
1167 | |
1168 | data[1] = (u64) qlcnic_test_link(dev); |
1169 | if (data[1]) |
1170 | eth_test->flags |= ETH_TEST_FL_FAILED; |
1171 | |
1172 | if (eth_test->flags & ETH_TEST_FL_OFFLINE) { |
1173 | data[2] = qlcnic_irq_test(netdev: dev); |
1174 | if (data[2]) |
1175 | eth_test->flags |= ETH_TEST_FL_FAILED; |
1176 | |
1177 | data[3] = qlcnic_loopback_test(netdev: dev, QLCNIC_ILB_MODE); |
1178 | if (data[3]) |
1179 | eth_test->flags |= ETH_TEST_FL_FAILED; |
1180 | |
1181 | if (eth_test->flags & ETH_TEST_FL_EXTERNAL_LB) { |
1182 | data[4] = qlcnic_loopback_test(netdev: dev, QLCNIC_ELB_MODE); |
1183 | if (data[4]) |
1184 | eth_test->flags |= ETH_TEST_FL_FAILED; |
1185 | eth_test->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE; |
1186 | } |
1187 | |
1188 | data[5] = qlcnic_eeprom_test(dev); |
1189 | if (data[5]) |
1190 | eth_test->flags |= ETH_TEST_FL_FAILED; |
1191 | } |
1192 | } |
1193 | |
1194 | static void |
1195 | qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) |
1196 | { |
1197 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
1198 | int index, i, num_stats; |
1199 | |
1200 | switch (stringset) { |
1201 | case ETH_SS_TEST: |
1202 | memcpy(data, *qlcnic_gstrings_test, |
1203 | QLCNIC_TEST_LEN * ETH_GSTRING_LEN); |
1204 | break; |
1205 | case ETH_SS_STATS: |
1206 | num_stats = ARRAY_SIZE(qlcnic_tx_queue_stats_strings); |
1207 | for (i = 0; i < adapter->drv_tx_rings; i++) { |
1208 | for (index = 0; index < num_stats; index++) { |
1209 | sprintf(buf: data, fmt: "tx_queue_%d %s" , i, |
1210 | qlcnic_tx_queue_stats_strings[index]); |
1211 | data += ETH_GSTRING_LEN; |
1212 | } |
1213 | } |
1214 | |
1215 | for (index = 0; index < QLCNIC_STATS_LEN; index++) { |
1216 | memcpy(data + index * ETH_GSTRING_LEN, |
1217 | qlcnic_gstrings_stats[index].stat_string, |
1218 | ETH_GSTRING_LEN); |
1219 | } |
1220 | |
1221 | if (qlcnic_83xx_check(adapter)) { |
1222 | num_stats = ARRAY_SIZE(qlcnic_83xx_tx_stats_strings); |
1223 | for (i = 0; i < num_stats; i++, index++) |
1224 | memcpy(data + index * ETH_GSTRING_LEN, |
1225 | qlcnic_83xx_tx_stats_strings[i], |
1226 | ETH_GSTRING_LEN); |
1227 | num_stats = ARRAY_SIZE(qlcnic_83xx_mac_stats_strings); |
1228 | for (i = 0; i < num_stats; i++, index++) |
1229 | memcpy(data + index * ETH_GSTRING_LEN, |
1230 | qlcnic_83xx_mac_stats_strings[i], |
1231 | ETH_GSTRING_LEN); |
1232 | num_stats = ARRAY_SIZE(qlcnic_83xx_rx_stats_strings); |
1233 | for (i = 0; i < num_stats; i++, index++) |
1234 | memcpy(data + index * ETH_GSTRING_LEN, |
1235 | qlcnic_83xx_rx_stats_strings[i], |
1236 | ETH_GSTRING_LEN); |
1237 | return; |
1238 | } else { |
1239 | num_stats = ARRAY_SIZE(qlcnic_83xx_mac_stats_strings); |
1240 | for (i = 0; i < num_stats; i++, index++) |
1241 | memcpy(data + index * ETH_GSTRING_LEN, |
1242 | qlcnic_83xx_mac_stats_strings[i], |
1243 | ETH_GSTRING_LEN); |
1244 | } |
1245 | if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) |
1246 | return; |
1247 | num_stats = ARRAY_SIZE(qlcnic_device_gstrings_stats); |
1248 | for (i = 0; i < num_stats; index++, i++) { |
1249 | memcpy(data + index * ETH_GSTRING_LEN, |
1250 | qlcnic_device_gstrings_stats[i], |
1251 | ETH_GSTRING_LEN); |
1252 | } |
1253 | } |
1254 | } |
1255 | |
1256 | static u64 *qlcnic_fill_stats(u64 *data, void *stats, int type) |
1257 | { |
1258 | if (type == QLCNIC_MAC_STATS) { |
1259 | struct qlcnic_mac_statistics *mac_stats = |
1260 | (struct qlcnic_mac_statistics *)stats; |
1261 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_frames); |
1262 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_bytes); |
1263 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_mcast_pkts); |
1264 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_bcast_pkts); |
1265 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_pause_cnt); |
1266 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_ctrl_pkt); |
1267 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_64b_pkts); |
1268 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_127b_pkts); |
1269 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_255b_pkts); |
1270 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_511b_pkts); |
1271 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_1023b_pkts); |
1272 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_1518b_pkts); |
1273 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_gt_1518b_pkts); |
1274 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_frames); |
1275 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_bytes); |
1276 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_mcast_pkts); |
1277 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_bcast_pkts); |
1278 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_pause_cnt); |
1279 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_ctrl_pkt); |
1280 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_64b_pkts); |
1281 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_127b_pkts); |
1282 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_255b_pkts); |
1283 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_511b_pkts); |
1284 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_1023b_pkts); |
1285 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_1518b_pkts); |
1286 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_gt_1518b_pkts); |
1287 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_error); |
1288 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_small); |
1289 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_large); |
1290 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_jabber); |
1291 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_dropped); |
1292 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_crc_error); |
1293 | *data++ = QLCNIC_FILL_STATS(mac_stats->mac_align_error); |
1294 | } else if (type == QLCNIC_ESW_STATS) { |
1295 | struct __qlcnic_esw_statistics *esw_stats = |
1296 | (struct __qlcnic_esw_statistics *)stats; |
1297 | *data++ = QLCNIC_FILL_STATS(esw_stats->unicast_frames); |
1298 | *data++ = QLCNIC_FILL_STATS(esw_stats->multicast_frames); |
1299 | *data++ = QLCNIC_FILL_STATS(esw_stats->broadcast_frames); |
1300 | *data++ = QLCNIC_FILL_STATS(esw_stats->dropped_frames); |
1301 | *data++ = QLCNIC_FILL_STATS(esw_stats->errors); |
1302 | *data++ = QLCNIC_FILL_STATS(esw_stats->local_frames); |
1303 | *data++ = QLCNIC_FILL_STATS(esw_stats->numbytes); |
1304 | } |
1305 | return data; |
1306 | } |
1307 | |
1308 | void qlcnic_update_stats(struct qlcnic_adapter *adapter) |
1309 | { |
1310 | struct qlcnic_tx_queue_stats tx_stats; |
1311 | struct qlcnic_host_tx_ring *tx_ring; |
1312 | int ring; |
1313 | |
1314 | memset(&tx_stats, 0, sizeof(tx_stats)); |
1315 | for (ring = 0; ring < adapter->drv_tx_rings; ring++) { |
1316 | tx_ring = &adapter->tx_ring[ring]; |
1317 | tx_stats.xmit_on += tx_ring->tx_stats.xmit_on; |
1318 | tx_stats.xmit_off += tx_ring->tx_stats.xmit_off; |
1319 | tx_stats.xmit_called += tx_ring->tx_stats.xmit_called; |
1320 | tx_stats.xmit_finished += tx_ring->tx_stats.xmit_finished; |
1321 | tx_stats.tx_bytes += tx_ring->tx_stats.tx_bytes; |
1322 | } |
1323 | |
1324 | adapter->stats.xmit_on = tx_stats.xmit_on; |
1325 | adapter->stats.xmit_off = tx_stats.xmit_off; |
1326 | adapter->stats.xmitcalled = tx_stats.xmit_called; |
1327 | adapter->stats.xmitfinished = tx_stats.xmit_finished; |
1328 | adapter->stats.txbytes = tx_stats.tx_bytes; |
1329 | } |
1330 | |
1331 | static u64 *qlcnic_fill_tx_queue_stats(u64 *data, void *stats) |
1332 | { |
1333 | struct qlcnic_host_tx_ring *tx_ring; |
1334 | |
1335 | tx_ring = (struct qlcnic_host_tx_ring *)stats; |
1336 | |
1337 | *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_on); |
1338 | *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_off); |
1339 | *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_called); |
1340 | *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_finished); |
1341 | *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.tx_bytes); |
1342 | |
1343 | return data; |
1344 | } |
1345 | |
1346 | static void qlcnic_get_ethtool_stats(struct net_device *dev, |
1347 | struct ethtool_stats *stats, u64 *data) |
1348 | { |
1349 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
1350 | struct qlcnic_host_tx_ring *tx_ring; |
1351 | struct qlcnic_esw_statistics port_stats; |
1352 | struct qlcnic_mac_statistics mac_stats; |
1353 | int index, ret, length, size, ring; |
1354 | char *p; |
1355 | |
1356 | memset(data, 0, stats->n_stats * sizeof(u64)); |
1357 | |
1358 | for (ring = 0; ring < adapter->drv_tx_rings; ring++) { |
1359 | if (adapter->is_up == QLCNIC_ADAPTER_UP_MAGIC) { |
1360 | tx_ring = &adapter->tx_ring[ring]; |
1361 | data = qlcnic_fill_tx_queue_stats(data, stats: tx_ring); |
1362 | qlcnic_update_stats(adapter); |
1363 | } else { |
1364 | data += QLCNIC_TX_STATS_LEN; |
1365 | } |
1366 | } |
1367 | |
1368 | length = QLCNIC_STATS_LEN; |
1369 | for (index = 0; index < length; index++) { |
1370 | p = (char *)adapter + qlcnic_gstrings_stats[index].stat_offset; |
1371 | size = qlcnic_gstrings_stats[index].sizeof_stat; |
1372 | *data++ = (size == sizeof(u64)) ? (*(u64 *)p) : ((*(u32 *)p)); |
1373 | } |
1374 | |
1375 | if (qlcnic_83xx_check(adapter)) { |
1376 | if (adapter->ahw->linkup) |
1377 | qlcnic_83xx_get_stats(adapter, data); |
1378 | return; |
1379 | } else { |
1380 | /* Retrieve MAC statistics from firmware */ |
1381 | memset(&mac_stats, 0, sizeof(struct qlcnic_mac_statistics)); |
1382 | qlcnic_get_mac_stats(adapter, &mac_stats); |
1383 | data = qlcnic_fill_stats(data, stats: &mac_stats, QLCNIC_MAC_STATS); |
1384 | } |
1385 | |
1386 | if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) |
1387 | return; |
1388 | |
1389 | memset(&port_stats, 0, sizeof(struct qlcnic_esw_statistics)); |
1390 | ret = qlcnic_get_port_stats(adapter, adapter->ahw->pci_func, |
1391 | QLCNIC_QUERY_RX_COUNTER, &port_stats.rx); |
1392 | if (ret) |
1393 | return; |
1394 | |
1395 | data = qlcnic_fill_stats(data, stats: &port_stats.rx, QLCNIC_ESW_STATS); |
1396 | ret = qlcnic_get_port_stats(adapter, adapter->ahw->pci_func, |
1397 | QLCNIC_QUERY_TX_COUNTER, &port_stats.tx); |
1398 | if (ret) |
1399 | return; |
1400 | |
1401 | qlcnic_fill_stats(data, stats: &port_stats.tx, QLCNIC_ESW_STATS); |
1402 | } |
1403 | |
1404 | static int qlcnic_set_led(struct net_device *dev, |
1405 | enum ethtool_phys_id_state state) |
1406 | { |
1407 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
1408 | int drv_sds_rings = adapter->drv_sds_rings; |
1409 | int err = -EIO, active = 1; |
1410 | |
1411 | if (qlcnic_83xx_check(adapter)) |
1412 | return qlcnic_83xx_set_led(dev, state); |
1413 | |
1414 | if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { |
1415 | netdev_warn(dev, format: "LED test not supported for non " |
1416 | "privilege function\n" ); |
1417 | return -EOPNOTSUPP; |
1418 | } |
1419 | |
1420 | switch (state) { |
1421 | case ETHTOOL_ID_ACTIVE: |
1422 | if (test_and_set_bit(__QLCNIC_LED_ENABLE, addr: &adapter->state)) |
1423 | return -EBUSY; |
1424 | |
1425 | if (test_bit(__QLCNIC_RESETTING, &adapter->state)) |
1426 | break; |
1427 | |
1428 | if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { |
1429 | if (qlcnic_diag_alloc_res(netdev: dev, QLCNIC_LED_TEST)) |
1430 | break; |
1431 | set_bit(__QLCNIC_DIAG_RES_ALLOC, addr: &adapter->state); |
1432 | } |
1433 | |
1434 | if (adapter->nic_ops->config_led(adapter, 1, 0xf) == 0) { |
1435 | err = 0; |
1436 | break; |
1437 | } |
1438 | |
1439 | dev_err(&adapter->pdev->dev, |
1440 | "Failed to set LED blink state.\n" ); |
1441 | break; |
1442 | |
1443 | case ETHTOOL_ID_INACTIVE: |
1444 | active = 0; |
1445 | |
1446 | if (test_bit(__QLCNIC_RESETTING, &adapter->state)) |
1447 | break; |
1448 | |
1449 | if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { |
1450 | if (qlcnic_diag_alloc_res(netdev: dev, QLCNIC_LED_TEST)) |
1451 | break; |
1452 | set_bit(__QLCNIC_DIAG_RES_ALLOC, addr: &adapter->state); |
1453 | } |
1454 | |
1455 | if (adapter->nic_ops->config_led(adapter, 0, 0xf)) |
1456 | dev_err(&adapter->pdev->dev, |
1457 | "Failed to reset LED blink state.\n" ); |
1458 | |
1459 | break; |
1460 | |
1461 | default: |
1462 | return -EINVAL; |
1463 | } |
1464 | |
1465 | if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, addr: &adapter->state)) |
1466 | qlcnic_diag_free_res(netdev: dev, drv_sds_rings); |
1467 | |
1468 | if (!active || err) |
1469 | clear_bit(__QLCNIC_LED_ENABLE, addr: &adapter->state); |
1470 | |
1471 | return err; |
1472 | } |
1473 | |
1474 | static void |
1475 | qlcnic_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) |
1476 | { |
1477 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
1478 | u32 wol_cfg; |
1479 | int err = 0; |
1480 | |
1481 | if (qlcnic_83xx_check(adapter)) |
1482 | return; |
1483 | wol->supported = 0; |
1484 | wol->wolopts = 0; |
1485 | |
1486 | wol_cfg = QLCRD32(adapter, QLCNIC_WOL_CONFIG_NV, &err); |
1487 | if (err == -EIO) |
1488 | return; |
1489 | if (wol_cfg & (1UL << adapter->portnum)) |
1490 | wol->supported |= WAKE_MAGIC; |
1491 | |
1492 | wol_cfg = QLCRD32(adapter, QLCNIC_WOL_CONFIG, &err); |
1493 | if (wol_cfg & (1UL << adapter->portnum)) |
1494 | wol->wolopts |= WAKE_MAGIC; |
1495 | } |
1496 | |
1497 | static int |
1498 | qlcnic_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) |
1499 | { |
1500 | struct qlcnic_adapter *adapter = netdev_priv(dev); |
1501 | u32 wol_cfg; |
1502 | int err = 0; |
1503 | |
1504 | if (qlcnic_83xx_check(adapter)) |
1505 | return -EOPNOTSUPP; |
1506 | if (wol->wolopts & ~WAKE_MAGIC) |
1507 | return -EINVAL; |
1508 | |
1509 | wol_cfg = QLCRD32(adapter, QLCNIC_WOL_CONFIG_NV, &err); |
1510 | if (err == -EIO) |
1511 | return err; |
1512 | if (!(wol_cfg & (1 << adapter->portnum))) |
1513 | return -EOPNOTSUPP; |
1514 | |
1515 | wol_cfg = QLCRD32(adapter, QLCNIC_WOL_CONFIG, &err); |
1516 | if (err == -EIO) |
1517 | return err; |
1518 | if (wol->wolopts & WAKE_MAGIC) |
1519 | wol_cfg |= 1UL << adapter->portnum; |
1520 | else |
1521 | wol_cfg &= ~(1UL << adapter->portnum); |
1522 | |
1523 | QLCWR32(adapter, QLCNIC_WOL_CONFIG, wol_cfg); |
1524 | |
1525 | return 0; |
1526 | } |
1527 | |
1528 | /* |
1529 | * Set the coalescing parameters. Currently only normal is supported. |
1530 | * If rx_coalesce_usecs == 0 or rx_max_coalesced_frames == 0 then set the |
1531 | * firmware coalescing to default. |
1532 | */ |
1533 | static int qlcnic_set_intr_coalesce(struct net_device *netdev, |
1534 | struct ethtool_coalesce *ethcoal, |
1535 | struct kernel_ethtool_coalesce *kernel_coal, |
1536 | struct netlink_ext_ack *extack) |
1537 | { |
1538 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
1539 | int err; |
1540 | |
1541 | if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) |
1542 | return -EINVAL; |
1543 | |
1544 | /* |
1545 | * Return Error if unsupported values or |
1546 | * unsupported parameters are set. |
1547 | */ |
1548 | if (ethcoal->rx_coalesce_usecs > 0xffff || |
1549 | ethcoal->rx_max_coalesced_frames > 0xffff || |
1550 | ethcoal->tx_coalesce_usecs > 0xffff || |
1551 | ethcoal->tx_max_coalesced_frames > 0xffff) |
1552 | return -EINVAL; |
1553 | |
1554 | err = qlcnic_config_intr_coalesce(adapter, ethcoal); |
1555 | |
1556 | return err; |
1557 | } |
1558 | |
1559 | static int qlcnic_get_intr_coalesce(struct net_device *netdev, |
1560 | struct ethtool_coalesce *ethcoal, |
1561 | struct kernel_ethtool_coalesce *kernel_coal, |
1562 | struct netlink_ext_ack *extack) |
1563 | { |
1564 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
1565 | |
1566 | if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) |
1567 | return -EINVAL; |
1568 | |
1569 | ethcoal->rx_coalesce_usecs = adapter->ahw->coal.rx_time_us; |
1570 | ethcoal->rx_max_coalesced_frames = adapter->ahw->coal.rx_packets; |
1571 | ethcoal->tx_coalesce_usecs = adapter->ahw->coal.tx_time_us; |
1572 | ethcoal->tx_max_coalesced_frames = adapter->ahw->coal.tx_packets; |
1573 | |
1574 | return 0; |
1575 | } |
1576 | |
1577 | static u32 qlcnic_get_msglevel(struct net_device *netdev) |
1578 | { |
1579 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
1580 | |
1581 | return adapter->ahw->msg_enable; |
1582 | } |
1583 | |
1584 | static void qlcnic_set_msglevel(struct net_device *netdev, u32 msglvl) |
1585 | { |
1586 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
1587 | |
1588 | adapter->ahw->msg_enable = msglvl; |
1589 | } |
1590 | |
1591 | int qlcnic_enable_fw_dump_state(struct qlcnic_adapter *adapter) |
1592 | { |
1593 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
1594 | u32 val; |
1595 | |
1596 | if (qlcnic_84xx_check(adapter)) { |
1597 | if (qlcnic_83xx_lock_driver(adapter)) |
1598 | return -EBUSY; |
1599 | |
1600 | val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); |
1601 | val &= ~QLC_83XX_IDC_DISABLE_FW_DUMP; |
1602 | QLCWRX(adapter->ahw, QLC_83XX_IDC_CTRL, val); |
1603 | |
1604 | qlcnic_83xx_unlock_driver(adapter); |
1605 | } else { |
1606 | fw_dump->enable = true; |
1607 | } |
1608 | |
1609 | dev_info(&adapter->pdev->dev, "FW dump enabled\n" ); |
1610 | |
1611 | return 0; |
1612 | } |
1613 | |
1614 | static int qlcnic_disable_fw_dump_state(struct qlcnic_adapter *adapter) |
1615 | { |
1616 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
1617 | u32 val; |
1618 | |
1619 | if (qlcnic_84xx_check(adapter)) { |
1620 | if (qlcnic_83xx_lock_driver(adapter)) |
1621 | return -EBUSY; |
1622 | |
1623 | val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); |
1624 | val |= QLC_83XX_IDC_DISABLE_FW_DUMP; |
1625 | QLCWRX(adapter->ahw, QLC_83XX_IDC_CTRL, val); |
1626 | |
1627 | qlcnic_83xx_unlock_driver(adapter); |
1628 | } else { |
1629 | fw_dump->enable = false; |
1630 | } |
1631 | |
1632 | dev_info(&adapter->pdev->dev, "FW dump disabled\n" ); |
1633 | |
1634 | return 0; |
1635 | } |
1636 | |
1637 | bool qlcnic_check_fw_dump_state(struct qlcnic_adapter *adapter) |
1638 | { |
1639 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
1640 | bool state; |
1641 | u32 val; |
1642 | |
1643 | if (qlcnic_84xx_check(adapter)) { |
1644 | val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); |
1645 | state = (val & QLC_83XX_IDC_DISABLE_FW_DUMP) ? false : true; |
1646 | } else { |
1647 | state = fw_dump->enable; |
1648 | } |
1649 | |
1650 | return state; |
1651 | } |
1652 | |
1653 | static int |
1654 | qlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) |
1655 | { |
1656 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
1657 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
1658 | |
1659 | if (!fw_dump->tmpl_hdr) { |
1660 | netdev_err(dev: adapter->netdev, format: "FW Dump not supported\n" ); |
1661 | return -ENOTSUPP; |
1662 | } |
1663 | |
1664 | if (fw_dump->clr) |
1665 | dump->len = fw_dump->tmpl_hdr_size + fw_dump->size; |
1666 | else |
1667 | dump->len = 0; |
1668 | |
1669 | if (!qlcnic_check_fw_dump_state(adapter)) |
1670 | dump->flag = ETH_FW_DUMP_DISABLE; |
1671 | else |
1672 | dump->flag = fw_dump->cap_mask; |
1673 | |
1674 | dump->version = adapter->fw_version; |
1675 | return 0; |
1676 | } |
1677 | |
1678 | static int |
1679 | qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, |
1680 | void *buffer) |
1681 | { |
1682 | int i, copy_sz; |
1683 | u32 *hdr_ptr; |
1684 | __le32 *data; |
1685 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
1686 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
1687 | |
1688 | if (!fw_dump->tmpl_hdr) { |
1689 | netdev_err(dev: netdev, format: "FW Dump not supported\n" ); |
1690 | return -ENOTSUPP; |
1691 | } |
1692 | |
1693 | if (!fw_dump->clr) { |
1694 | netdev_info(dev: netdev, format: "Dump not available\n" ); |
1695 | return -EINVAL; |
1696 | } |
1697 | |
1698 | /* Copy template header first */ |
1699 | copy_sz = fw_dump->tmpl_hdr_size; |
1700 | hdr_ptr = (u32 *)fw_dump->tmpl_hdr; |
1701 | data = buffer; |
1702 | for (i = 0; i < copy_sz/sizeof(u32); i++) |
1703 | *data++ = cpu_to_le32(*hdr_ptr++); |
1704 | |
1705 | /* Copy captured dump data */ |
1706 | memcpy(buffer + copy_sz, fw_dump->data, fw_dump->size); |
1707 | dump->len = copy_sz + fw_dump->size; |
1708 | dump->flag = fw_dump->cap_mask; |
1709 | |
1710 | /* Free dump area once data has been captured */ |
1711 | vfree(addr: fw_dump->data); |
1712 | fw_dump->data = NULL; |
1713 | fw_dump->clr = 0; |
1714 | netdev_info(dev: netdev, format: "extracted the FW dump Successfully\n" ); |
1715 | return 0; |
1716 | } |
1717 | |
1718 | static int qlcnic_set_dump_mask(struct qlcnic_adapter *adapter, u32 mask) |
1719 | { |
1720 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
1721 | struct net_device *netdev = adapter->netdev; |
1722 | |
1723 | if (!qlcnic_check_fw_dump_state(adapter)) { |
1724 | netdev_info(dev: netdev, |
1725 | format: "Can not change driver mask to 0x%x. FW dump not enabled\n" , |
1726 | mask); |
1727 | return -EOPNOTSUPP; |
1728 | } |
1729 | |
1730 | fw_dump->cap_mask = mask; |
1731 | |
1732 | /* Store new capture mask in template header as well*/ |
1733 | qlcnic_store_cap_mask(adapter, tmpl_hdr: fw_dump->tmpl_hdr, mask); |
1734 | |
1735 | netdev_info(dev: netdev, format: "Driver mask changed to: 0x%x\n" , mask); |
1736 | return 0; |
1737 | } |
1738 | |
1739 | static int |
1740 | qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) |
1741 | { |
1742 | struct qlcnic_adapter *adapter = netdev_priv(dev: netdev); |
1743 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
1744 | bool valid_mask = false; |
1745 | int i, ret = 0; |
1746 | |
1747 | switch (val->flag) { |
1748 | case QLCNIC_FORCE_FW_DUMP_KEY: |
1749 | if (!fw_dump->tmpl_hdr) { |
1750 | netdev_err(dev: netdev, format: "FW dump not supported\n" ); |
1751 | ret = -EOPNOTSUPP; |
1752 | break; |
1753 | } |
1754 | |
1755 | if (!qlcnic_check_fw_dump_state(adapter)) { |
1756 | netdev_info(dev: netdev, format: "FW dump not enabled\n" ); |
1757 | ret = -EOPNOTSUPP; |
1758 | break; |
1759 | } |
1760 | |
1761 | if (fw_dump->clr) { |
1762 | netdev_info(dev: netdev, |
1763 | format: "Previous dump not cleared, not forcing dump\n" ); |
1764 | break; |
1765 | } |
1766 | |
1767 | netdev_info(dev: netdev, format: "Forcing a FW dump\n" ); |
1768 | qlcnic_dev_request_reset(adapter, key: val->flag); |
1769 | break; |
1770 | case QLCNIC_DISABLE_FW_DUMP: |
1771 | if (!fw_dump->tmpl_hdr) { |
1772 | netdev_err(dev: netdev, format: "FW dump not supported\n" ); |
1773 | ret = -EOPNOTSUPP; |
1774 | break; |
1775 | } |
1776 | |
1777 | ret = qlcnic_disable_fw_dump_state(adapter); |
1778 | break; |
1779 | |
1780 | case QLCNIC_ENABLE_FW_DUMP: |
1781 | if (!fw_dump->tmpl_hdr) { |
1782 | netdev_err(dev: netdev, format: "FW dump not supported\n" ); |
1783 | ret = -EOPNOTSUPP; |
1784 | break; |
1785 | } |
1786 | |
1787 | ret = qlcnic_enable_fw_dump_state(adapter); |
1788 | break; |
1789 | |
1790 | case QLCNIC_FORCE_FW_RESET: |
1791 | netdev_info(dev: netdev, format: "Forcing a FW reset\n" ); |
1792 | qlcnic_dev_request_reset(adapter, key: val->flag); |
1793 | adapter->flags &= ~QLCNIC_FW_RESET_OWNER; |
1794 | break; |
1795 | |
1796 | case QLCNIC_SET_QUIESCENT: |
1797 | case QLCNIC_RESET_QUIESCENT: |
1798 | if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) |
1799 | netdev_info(dev: netdev, format: "Device is in non-operational state\n" ); |
1800 | break; |
1801 | |
1802 | default: |
1803 | if (!fw_dump->tmpl_hdr) { |
1804 | netdev_err(dev: netdev, format: "FW dump not supported\n" ); |
1805 | ret = -EOPNOTSUPP; |
1806 | break; |
1807 | } |
1808 | |
1809 | for (i = 0; i < ARRAY_SIZE(qlcnic_fw_dump_level); i++) { |
1810 | if (val->flag == qlcnic_fw_dump_level[i]) { |
1811 | valid_mask = true; |
1812 | break; |
1813 | } |
1814 | } |
1815 | |
1816 | if (valid_mask) { |
1817 | ret = qlcnic_set_dump_mask(adapter, mask: val->flag); |
1818 | } else { |
1819 | netdev_info(dev: netdev, format: "Invalid dump level: 0x%x\n" , |
1820 | val->flag); |
1821 | ret = -EINVAL; |
1822 | } |
1823 | } |
1824 | return ret; |
1825 | } |
1826 | |
1827 | const struct ethtool_ops qlcnic_ethtool_ops = { |
1828 | .supported_coalesce_params = ETHTOOL_COALESCE_USECS | |
1829 | ETHTOOL_COALESCE_MAX_FRAMES, |
1830 | .get_drvinfo = qlcnic_get_drvinfo, |
1831 | .get_regs_len = qlcnic_get_regs_len, |
1832 | .get_regs = qlcnic_get_regs, |
1833 | .get_link = ethtool_op_get_link, |
1834 | .get_eeprom_len = qlcnic_get_eeprom_len, |
1835 | .get_eeprom = qlcnic_get_eeprom, |
1836 | .get_ringparam = qlcnic_get_ringparam, |
1837 | .set_ringparam = qlcnic_set_ringparam, |
1838 | .get_channels = qlcnic_get_channels, |
1839 | .set_channels = qlcnic_set_channels, |
1840 | .get_pauseparam = qlcnic_get_pauseparam, |
1841 | .set_pauseparam = qlcnic_set_pauseparam, |
1842 | .get_wol = qlcnic_get_wol, |
1843 | .set_wol = qlcnic_set_wol, |
1844 | .self_test = qlcnic_diag_test, |
1845 | .get_strings = qlcnic_get_strings, |
1846 | .get_ethtool_stats = qlcnic_get_ethtool_stats, |
1847 | .get_sset_count = qlcnic_get_sset_count, |
1848 | .get_coalesce = qlcnic_get_intr_coalesce, |
1849 | .set_coalesce = qlcnic_set_intr_coalesce, |
1850 | .set_phys_id = qlcnic_set_led, |
1851 | .set_msglevel = qlcnic_set_msglevel, |
1852 | .get_msglevel = qlcnic_get_msglevel, |
1853 | .get_dump_flag = qlcnic_get_dump_flag, |
1854 | .get_dump_data = qlcnic_get_dump_data, |
1855 | .set_dump = qlcnic_set_dump, |
1856 | .get_link_ksettings = qlcnic_get_link_ksettings, |
1857 | .set_link_ksettings = qlcnic_set_link_ksettings, |
1858 | }; |
1859 | |
1860 | const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops = { |
1861 | .supported_coalesce_params = ETHTOOL_COALESCE_USECS | |
1862 | ETHTOOL_COALESCE_MAX_FRAMES, |
1863 | .get_drvinfo = qlcnic_get_drvinfo, |
1864 | .get_regs_len = qlcnic_get_regs_len, |
1865 | .get_regs = qlcnic_get_regs, |
1866 | .get_link = ethtool_op_get_link, |
1867 | .get_eeprom_len = qlcnic_get_eeprom_len, |
1868 | .get_eeprom = qlcnic_get_eeprom, |
1869 | .get_ringparam = qlcnic_get_ringparam, |
1870 | .set_ringparam = qlcnic_set_ringparam, |
1871 | .get_channels = qlcnic_get_channels, |
1872 | .get_pauseparam = qlcnic_get_pauseparam, |
1873 | .get_wol = qlcnic_get_wol, |
1874 | .get_strings = qlcnic_get_strings, |
1875 | .get_ethtool_stats = qlcnic_get_ethtool_stats, |
1876 | .get_sset_count = qlcnic_get_sset_count, |
1877 | .get_coalesce = qlcnic_get_intr_coalesce, |
1878 | .set_coalesce = qlcnic_set_intr_coalesce, |
1879 | .set_msglevel = qlcnic_set_msglevel, |
1880 | .get_msglevel = qlcnic_get_msglevel, |
1881 | .get_link_ksettings = qlcnic_get_link_ksettings, |
1882 | }; |
1883 | |
1884 | const struct ethtool_ops qlcnic_ethtool_failed_ops = { |
1885 | .get_drvinfo = qlcnic_get_drvinfo, |
1886 | .set_msglevel = qlcnic_set_msglevel, |
1887 | .get_msglevel = qlcnic_get_msglevel, |
1888 | .set_dump = qlcnic_set_dump, |
1889 | .get_link_ksettings = qlcnic_get_link_ksettings, |
1890 | }; |
1891 | |