]>
Commit | Line | Data |
---|---|---|
75a6faf6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
c5760d03 DV |
2 | /* |
3 | * aQuantia Corporation Network Driver | |
4 | * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved | |
c5760d03 DV |
5 | */ |
6 | ||
7 | /* File aq_ethtool.c: Definition of ethertool related functions. */ | |
8 | ||
9 | #include "aq_ethtool.h" | |
10 | #include "aq_nic.h" | |
c1af5427 | 11 | #include "aq_vec.h" |
8d0bcb01 | 12 | #include "aq_filters.h" |
c5760d03 DV |
13 | |
14 | static void aq_ethtool_get_regs(struct net_device *ndev, | |
15 | struct ethtool_regs *regs, void *p) | |
16 | { | |
17 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
18 | u32 regs_count = aq_nic_get_regs_count(aq_nic); | |
19 | ||
20 | memset(p, 0, regs_count * sizeof(u32)); | |
21 | aq_nic_get_regs(aq_nic, regs, p); | |
22 | } | |
23 | ||
24 | static int aq_ethtool_get_regs_len(struct net_device *ndev) | |
25 | { | |
26 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
27 | u32 regs_count = aq_nic_get_regs_count(aq_nic); | |
28 | ||
29 | return regs_count * sizeof(u32); | |
30 | } | |
31 | ||
32 | static u32 aq_ethtool_get_link(struct net_device *ndev) | |
33 | { | |
34 | return ethtool_op_get_link(ndev); | |
35 | } | |
36 | ||
f8244ab5 PR |
37 | static int aq_ethtool_get_link_ksettings(struct net_device *ndev, |
38 | struct ethtool_link_ksettings *cmd) | |
c5760d03 DV |
39 | { |
40 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
41 | ||
f8244ab5 PR |
42 | aq_nic_get_link_ksettings(aq_nic, cmd); |
43 | cmd->base.speed = netif_carrier_ok(ndev) ? | |
44 | aq_nic_get_link_speed(aq_nic) : 0U; | |
c5760d03 DV |
45 | |
46 | return 0; | |
47 | } | |
48 | ||
f8244ab5 PR |
49 | static int |
50 | aq_ethtool_set_link_ksettings(struct net_device *ndev, | |
51 | const struct ethtool_link_ksettings *cmd) | |
c5760d03 DV |
52 | { |
53 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
54 | ||
f8244ab5 | 55 | return aq_nic_set_link_ksettings(aq_nic, cmd); |
c5760d03 DV |
56 | } |
57 | ||
c5760d03 DV |
58 | static const char aq_ethtool_stat_names[][ETH_GSTRING_LEN] = { |
59 | "InPackets", | |
60 | "InUCast", | |
61 | "InMCast", | |
62 | "InBCast", | |
63 | "InErrors", | |
64 | "OutPackets", | |
65 | "OutUCast", | |
66 | "OutMCast", | |
67 | "OutBCast", | |
98bc036d IR |
68 | "InUCastOctets", |
69 | "OutUCastOctets", | |
70 | "InMCastOctets", | |
71 | "OutMCastOctets", | |
72 | "InBCastOctets", | |
73 | "OutBCastOctets", | |
74 | "InOctets", | |
75 | "OutOctets", | |
c5760d03 DV |
76 | "InPacketsDma", |
77 | "OutPacketsDma", | |
78 | "InOctetsDma", | |
79 | "OutOctetsDma", | |
80 | "InDroppedDma", | |
5d8d84e9 IR |
81 | }; |
82 | ||
83 | static const char aq_ethtool_queue_stat_names[][ETH_GSTRING_LEN] = { | |
84 | "Queue[%d] InPackets", | |
85 | "Queue[%d] OutPackets", | |
86 | "Queue[%d] Restarts", | |
87 | "Queue[%d] InJumboPackets", | |
88 | "Queue[%d] InLroPackets", | |
89 | "Queue[%d] InErrors", | |
c5760d03 DV |
90 | }; |
91 | ||
92 | static void aq_ethtool_stats(struct net_device *ndev, | |
93 | struct ethtool_stats *stats, u64 *data) | |
94 | { | |
95 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
5d8d84e9 | 96 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); |
c5760d03 | 97 | |
5d8d84e9 | 98 | memset(data, 0, (ARRAY_SIZE(aq_ethtool_stat_names) + |
e9157848 ND |
99 | ARRAY_SIZE(aq_ethtool_queue_stat_names) * |
100 | cfg->vecs) * sizeof(u64)); | |
c5760d03 DV |
101 | aq_nic_get_stats(aq_nic, data); |
102 | } | |
103 | ||
104 | static void aq_ethtool_get_drvinfo(struct net_device *ndev, | |
105 | struct ethtool_drvinfo *drvinfo) | |
106 | { | |
107 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
108 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
109 | struct pci_dev *pdev = to_pci_dev(ndev->dev.parent); | |
110 | u32 firmware_version = aq_nic_get_fw_version(aq_nic); | |
111 | u32 regs_count = aq_nic_get_regs_count(aq_nic); | |
112 | ||
113 | strlcat(drvinfo->driver, AQ_CFG_DRV_NAME, sizeof(drvinfo->driver)); | |
114 | strlcat(drvinfo->version, AQ_CFG_DRV_VERSION, sizeof(drvinfo->version)); | |
115 | ||
116 | snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), | |
117 | "%u.%u.%u", firmware_version >> 24, | |
118 | (firmware_version >> 16) & 0xFFU, firmware_version & 0xFFFFU); | |
119 | ||
120 | strlcpy(drvinfo->bus_info, pdev ? pci_name(pdev) : "", | |
121 | sizeof(drvinfo->bus_info)); | |
5d8d84e9 IR |
122 | drvinfo->n_stats = ARRAY_SIZE(aq_ethtool_stat_names) + |
123 | cfg->vecs * ARRAY_SIZE(aq_ethtool_queue_stat_names); | |
c5760d03 DV |
124 | drvinfo->testinfo_len = 0; |
125 | drvinfo->regdump_len = regs_count; | |
126 | drvinfo->eedump_len = 0; | |
127 | } | |
128 | ||
129 | static void aq_ethtool_get_strings(struct net_device *ndev, | |
130 | u32 stringset, u8 *data) | |
131 | { | |
5d8d84e9 | 132 | int i, si; |
c5760d03 DV |
133 | struct aq_nic_s *aq_nic = netdev_priv(ndev); |
134 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
5d8d84e9 IR |
135 | u8 *p = data; |
136 | ||
137 | if (stringset == ETH_SS_STATS) { | |
ff83dbf2 | 138 | memcpy(p, aq_ethtool_stat_names, |
5d8d84e9 IR |
139 | sizeof(aq_ethtool_stat_names)); |
140 | p = p + sizeof(aq_ethtool_stat_names); | |
141 | for (i = 0; i < cfg->vecs; i++) { | |
142 | for (si = 0; | |
143 | si < ARRAY_SIZE(aq_ethtool_queue_stat_names); | |
144 | si++) { | |
145 | snprintf(p, ETH_GSTRING_LEN, | |
146 | aq_ethtool_queue_stat_names[si], i); | |
147 | p += ETH_GSTRING_LEN; | |
148 | } | |
149 | } | |
150 | } | |
c5760d03 DV |
151 | } |
152 | ||
153 | static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset) | |
154 | { | |
155 | int ret = 0; | |
156 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
157 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
158 | ||
159 | switch (stringset) { | |
160 | case ETH_SS_STATS: | |
5d8d84e9 IR |
161 | ret = ARRAY_SIZE(aq_ethtool_stat_names) + |
162 | cfg->vecs * ARRAY_SIZE(aq_ethtool_queue_stat_names); | |
c5760d03 DV |
163 | break; |
164 | default: | |
165 | ret = -EOPNOTSUPP; | |
166 | } | |
167 | return ret; | |
168 | } | |
169 | ||
170 | static u32 aq_ethtool_get_rss_indir_size(struct net_device *ndev) | |
171 | { | |
172 | return AQ_CFG_RSS_INDIRECTION_TABLE_MAX; | |
173 | } | |
174 | ||
175 | static u32 aq_ethtool_get_rss_key_size(struct net_device *ndev) | |
176 | { | |
177 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
178 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
179 | ||
180 | return sizeof(cfg->aq_rss.hash_secret_key); | |
181 | } | |
182 | ||
183 | static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key, | |
184 | u8 *hfunc) | |
185 | { | |
186 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
187 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
188 | unsigned int i = 0U; | |
189 | ||
190 | if (hfunc) | |
191 | *hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */ | |
192 | if (indir) { | |
193 | for (i = 0; i < AQ_CFG_RSS_INDIRECTION_TABLE_MAX; i++) | |
194 | indir[i] = cfg->aq_rss.indirection_table[i]; | |
195 | } | |
196 | if (key) | |
197 | memcpy(key, cfg->aq_rss.hash_secret_key, | |
198 | sizeof(cfg->aq_rss.hash_secret_key)); | |
199 | return 0; | |
200 | } | |
201 | ||
39163767 DB |
202 | static int aq_ethtool_set_rss(struct net_device *netdev, const u32 *indir, |
203 | const u8 *key, const u8 hfunc) | |
204 | { | |
205 | struct aq_nic_s *aq_nic = netdev_priv(netdev); | |
206 | struct aq_nic_cfg_s *cfg; | |
207 | unsigned int i = 0U; | |
208 | u32 rss_entries; | |
209 | int err = 0; | |
210 | ||
211 | cfg = aq_nic_get_cfg(aq_nic); | |
212 | rss_entries = cfg->aq_rss.indirection_table_size; | |
213 | ||
214 | /* We do not allow change in unsupported parameters */ | |
215 | if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) | |
216 | return -EOPNOTSUPP; | |
217 | /* Fill out the redirection table */ | |
218 | if (indir) | |
219 | for (i = 0; i < rss_entries; i++) | |
220 | cfg->aq_rss.indirection_table[i] = indir[i]; | |
221 | ||
222 | /* Fill out the rss hash key */ | |
223 | if (key) { | |
224 | memcpy(cfg->aq_rss.hash_secret_key, key, | |
225 | sizeof(cfg->aq_rss.hash_secret_key)); | |
226 | err = aq_nic->aq_hw_ops->hw_rss_hash_set(aq_nic->aq_hw, | |
227 | &cfg->aq_rss); | |
228 | if (err) | |
229 | return err; | |
230 | } | |
231 | ||
232 | err = aq_nic->aq_hw_ops->hw_rss_set(aq_nic->aq_hw, &cfg->aq_rss); | |
233 | ||
234 | return err; | |
235 | } | |
236 | ||
c5760d03 DV |
237 | static int aq_ethtool_get_rxnfc(struct net_device *ndev, |
238 | struct ethtool_rxnfc *cmd, | |
239 | u32 *rule_locs) | |
240 | { | |
241 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
242 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
243 | int err = 0; | |
244 | ||
245 | switch (cmd->cmd) { | |
246 | case ETHTOOL_GRXRINGS: | |
247 | cmd->data = cfg->vecs; | |
248 | break; | |
8d0bcb01 DB |
249 | case ETHTOOL_GRXCLSRLCNT: |
250 | cmd->rule_cnt = aq_get_rxnfc_count_all_rules(aq_nic); | |
251 | break; | |
252 | case ETHTOOL_GRXCLSRULE: | |
253 | err = aq_get_rxnfc_rule(aq_nic, cmd); | |
254 | break; | |
255 | case ETHTOOL_GRXCLSRLALL: | |
256 | err = aq_get_rxnfc_all_rules(aq_nic, cmd, rule_locs); | |
257 | break; | |
258 | default: | |
259 | err = -EOPNOTSUPP; | |
260 | break; | |
261 | } | |
c5760d03 | 262 | |
8d0bcb01 DB |
263 | return err; |
264 | } | |
265 | ||
266 | static int aq_ethtool_set_rxnfc(struct net_device *ndev, | |
267 | struct ethtool_rxnfc *cmd) | |
268 | { | |
269 | int err = 0; | |
270 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
271 | ||
272 | switch (cmd->cmd) { | |
273 | case ETHTOOL_SRXCLSRLINS: | |
274 | err = aq_add_rxnfc_rule(aq_nic, cmd); | |
275 | break; | |
276 | case ETHTOOL_SRXCLSRLDEL: | |
277 | err = aq_del_rxnfc_rule(aq_nic, cmd); | |
278 | break; | |
c5760d03 DV |
279 | default: |
280 | err = -EOPNOTSUPP; | |
281 | break; | |
282 | } | |
283 | ||
284 | return err; | |
285 | } | |
286 | ||
2660d226 WY |
287 | static int aq_ethtool_get_coalesce(struct net_device *ndev, |
288 | struct ethtool_coalesce *coal) | |
b82ee71a IR |
289 | { |
290 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
291 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
292 | ||
293 | if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON || | |
294 | cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) { | |
295 | coal->rx_coalesce_usecs = cfg->rx_itr; | |
296 | coal->tx_coalesce_usecs = cfg->tx_itr; | |
297 | coal->rx_max_coalesced_frames = 0; | |
298 | coal->tx_max_coalesced_frames = 0; | |
299 | } else { | |
300 | coal->rx_coalesce_usecs = 0; | |
301 | coal->tx_coalesce_usecs = 0; | |
302 | coal->rx_max_coalesced_frames = 1; | |
303 | coal->tx_max_coalesced_frames = 1; | |
304 | } | |
305 | return 0; | |
306 | } | |
307 | ||
2660d226 WY |
308 | static int aq_ethtool_set_coalesce(struct net_device *ndev, |
309 | struct ethtool_coalesce *coal) | |
b82ee71a IR |
310 | { |
311 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
312 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
313 | ||
314 | /* This is not yet supported | |
315 | */ | |
316 | if (coal->use_adaptive_rx_coalesce || coal->use_adaptive_tx_coalesce) | |
317 | return -EOPNOTSUPP; | |
318 | ||
319 | /* Atlantic only supports timing based coalescing | |
320 | */ | |
321 | if (coal->rx_max_coalesced_frames > 1 || | |
322 | coal->rx_coalesce_usecs_irq || | |
323 | coal->rx_max_coalesced_frames_irq) | |
324 | return -EOPNOTSUPP; | |
325 | ||
326 | if (coal->tx_max_coalesced_frames > 1 || | |
327 | coal->tx_coalesce_usecs_irq || | |
328 | coal->tx_max_coalesced_frames_irq) | |
329 | return -EOPNOTSUPP; | |
330 | ||
331 | /* We do not support frame counting. Check this | |
332 | */ | |
333 | if (!(coal->rx_max_coalesced_frames == !coal->rx_coalesce_usecs)) | |
334 | return -EOPNOTSUPP; | |
335 | if (!(coal->tx_max_coalesced_frames == !coal->tx_coalesce_usecs)) | |
336 | return -EOPNOTSUPP; | |
337 | ||
338 | if (coal->rx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX || | |
339 | coal->tx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX) | |
340 | return -EINVAL; | |
341 | ||
342 | cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON; | |
343 | ||
344 | cfg->rx_itr = coal->rx_coalesce_usecs; | |
345 | cfg->tx_itr = coal->tx_coalesce_usecs; | |
346 | ||
347 | return aq_nic_update_interrupt_moderation_settings(aq_nic); | |
348 | } | |
349 | ||
a0da96c0 YE |
350 | static void aq_ethtool_get_wol(struct net_device *ndev, |
351 | struct ethtool_wolinfo *wol) | |
352 | { | |
353 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
354 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
355 | ||
356 | wol->supported = WAKE_MAGIC; | |
357 | wol->wolopts = 0; | |
358 | ||
359 | if (cfg->wol) | |
360 | wol->wolopts |= WAKE_MAGIC; | |
361 | } | |
362 | ||
363 | static int aq_ethtool_set_wol(struct net_device *ndev, | |
364 | struct ethtool_wolinfo *wol) | |
365 | { | |
366 | struct pci_dev *pdev = to_pci_dev(ndev->dev.parent); | |
367 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
368 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | |
369 | int err = 0; | |
370 | ||
371 | if (wol->wolopts & WAKE_MAGIC) | |
372 | cfg->wol |= AQ_NIC_WOL_ENABLED; | |
373 | else | |
374 | cfg->wol &= ~AQ_NIC_WOL_ENABLED; | |
375 | err = device_set_wakeup_enable(&pdev->dev, wol->wolopts); | |
376 | ||
377 | return err; | |
378 | } | |
379 | ||
92ab6407 YE |
380 | static enum hw_atl_fw2x_rate eee_mask_to_ethtool_mask(u32 speed) |
381 | { | |
382 | u32 rate = 0; | |
383 | ||
384 | if (speed & AQ_NIC_RATE_EEE_10G) | |
385 | rate |= SUPPORTED_10000baseT_Full; | |
386 | ||
387 | if (speed & AQ_NIC_RATE_EEE_2GS) | |
388 | rate |= SUPPORTED_2500baseX_Full; | |
389 | ||
390 | if (speed & AQ_NIC_RATE_EEE_1G) | |
391 | rate |= SUPPORTED_1000baseT_Full; | |
392 | ||
393 | return rate; | |
394 | } | |
395 | ||
396 | static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_eee *eee) | |
397 | { | |
398 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
399 | u32 rate, supported_rates; | |
400 | int err = 0; | |
401 | ||
402 | if (!aq_nic->aq_fw_ops->get_eee_rate) | |
403 | return -EOPNOTSUPP; | |
404 | ||
f5dce08a | 405 | mutex_lock(&aq_nic->fwreq_mutex); |
92ab6407 YE |
406 | err = aq_nic->aq_fw_ops->get_eee_rate(aq_nic->aq_hw, &rate, |
407 | &supported_rates); | |
f5dce08a | 408 | mutex_unlock(&aq_nic->fwreq_mutex); |
92ab6407 YE |
409 | if (err < 0) |
410 | return err; | |
411 | ||
412 | eee->supported = eee_mask_to_ethtool_mask(supported_rates); | |
413 | ||
414 | if (aq_nic->aq_nic_cfg.eee_speeds) | |
415 | eee->advertised = eee->supported; | |
416 | ||
417 | eee->lp_advertised = eee_mask_to_ethtool_mask(rate); | |
418 | ||
419 | eee->eee_enabled = !!eee->advertised; | |
420 | ||
421 | eee->tx_lpi_enabled = eee->eee_enabled; | |
422 | if (eee->advertised & eee->lp_advertised) | |
423 | eee->eee_active = true; | |
424 | ||
425 | return 0; | |
426 | } | |
427 | ||
428 | static int aq_ethtool_set_eee(struct net_device *ndev, struct ethtool_eee *eee) | |
429 | { | |
430 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
431 | u32 rate, supported_rates; | |
432 | struct aq_nic_cfg_s *cfg; | |
433 | int err = 0; | |
434 | ||
435 | cfg = aq_nic_get_cfg(aq_nic); | |
436 | ||
437 | if (unlikely(!aq_nic->aq_fw_ops->get_eee_rate || | |
438 | !aq_nic->aq_fw_ops->set_eee_rate)) | |
439 | return -EOPNOTSUPP; | |
440 | ||
f5dce08a | 441 | mutex_lock(&aq_nic->fwreq_mutex); |
92ab6407 YE |
442 | err = aq_nic->aq_fw_ops->get_eee_rate(aq_nic->aq_hw, &rate, |
443 | &supported_rates); | |
f5dce08a | 444 | mutex_unlock(&aq_nic->fwreq_mutex); |
92ab6407 YE |
445 | if (err < 0) |
446 | return err; | |
447 | ||
448 | if (eee->eee_enabled) { | |
449 | rate = supported_rates; | |
450 | cfg->eee_speeds = rate; | |
451 | } else { | |
452 | rate = 0; | |
453 | cfg->eee_speeds = 0; | |
454 | } | |
455 | ||
f5dce08a ND |
456 | mutex_lock(&aq_nic->fwreq_mutex); |
457 | err = aq_nic->aq_fw_ops->set_eee_rate(aq_nic->aq_hw, rate); | |
458 | mutex_unlock(&aq_nic->fwreq_mutex); | |
459 | ||
460 | return err; | |
92ab6407 YE |
461 | } |
462 | ||
b8d68b62 AM |
463 | static int aq_ethtool_nway_reset(struct net_device *ndev) |
464 | { | |
465 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
f5dce08a | 466 | int err = 0; |
b8d68b62 AM |
467 | |
468 | if (unlikely(!aq_nic->aq_fw_ops->renegotiate)) | |
469 | return -EOPNOTSUPP; | |
470 | ||
f5dce08a ND |
471 | if (netif_running(ndev)) { |
472 | mutex_lock(&aq_nic->fwreq_mutex); | |
473 | err = aq_nic->aq_fw_ops->renegotiate(aq_nic->aq_hw); | |
474 | mutex_unlock(&aq_nic->fwreq_mutex); | |
475 | } | |
b8d68b62 | 476 | |
f5dce08a | 477 | return err; |
b8d68b62 AM |
478 | } |
479 | ||
288551de IR |
480 | static void aq_ethtool_get_pauseparam(struct net_device *ndev, |
481 | struct ethtool_pauseparam *pause) | |
482 | { | |
483 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
35e8e8b4 | 484 | u32 fc = aq_nic->aq_nic_cfg.flow_control; |
288551de IR |
485 | |
486 | pause->autoneg = 0; | |
487 | ||
35e8e8b4 IR |
488 | pause->rx_pause = !!(fc & AQ_NIC_FC_RX); |
489 | pause->tx_pause = !!(fc & AQ_NIC_FC_TX); | |
490 | ||
288551de IR |
491 | } |
492 | ||
493 | static int aq_ethtool_set_pauseparam(struct net_device *ndev, | |
494 | struct ethtool_pauseparam *pause) | |
495 | { | |
496 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
497 | int err = 0; | |
498 | ||
499 | if (!aq_nic->aq_fw_ops->set_flow_control) | |
500 | return -EOPNOTSUPP; | |
501 | ||
502 | if (pause->autoneg == AUTONEG_ENABLE) | |
503 | return -EOPNOTSUPP; | |
504 | ||
505 | if (pause->rx_pause) | |
506 | aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_RX; | |
507 | else | |
508 | aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_RX; | |
509 | ||
510 | if (pause->tx_pause) | |
511 | aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_TX; | |
512 | else | |
513 | aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_TX; | |
514 | ||
f5dce08a | 515 | mutex_lock(&aq_nic->fwreq_mutex); |
288551de | 516 | err = aq_nic->aq_fw_ops->set_flow_control(aq_nic->aq_hw); |
f5dce08a | 517 | mutex_unlock(&aq_nic->fwreq_mutex); |
288551de IR |
518 | |
519 | return err; | |
520 | } | |
521 | ||
c1af5427 AM |
522 | static void aq_get_ringparam(struct net_device *ndev, |
523 | struct ethtool_ringparam *ring) | |
524 | { | |
525 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
526 | struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(aq_nic); | |
527 | ||
528 | ring->rx_pending = aq_nic_cfg->rxds; | |
529 | ring->tx_pending = aq_nic_cfg->txds; | |
530 | ||
531 | ring->rx_max_pending = aq_nic_cfg->aq_hw_caps->rxds_max; | |
532 | ring->tx_max_pending = aq_nic_cfg->aq_hw_caps->txds_max; | |
533 | } | |
534 | ||
535 | static int aq_set_ringparam(struct net_device *ndev, | |
536 | struct ethtool_ringparam *ring) | |
537 | { | |
538 | int err = 0; | |
539 | bool ndev_running = false; | |
540 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
541 | struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(aq_nic); | |
542 | const struct aq_hw_caps_s *hw_caps = aq_nic_cfg->aq_hw_caps; | |
543 | ||
544 | if (ring->rx_mini_pending || ring->rx_jumbo_pending) { | |
545 | err = -EOPNOTSUPP; | |
546 | goto err_exit; | |
547 | } | |
548 | ||
549 | if (netif_running(ndev)) { | |
550 | ndev_running = true; | |
551 | dev_close(ndev); | |
552 | } | |
553 | ||
554 | aq_nic_free_vectors(aq_nic); | |
555 | ||
556 | aq_nic_cfg->rxds = max(ring->rx_pending, hw_caps->rxds_min); | |
557 | aq_nic_cfg->rxds = min(aq_nic_cfg->rxds, hw_caps->rxds_max); | |
558 | aq_nic_cfg->rxds = ALIGN(aq_nic_cfg->rxds, AQ_HW_RXD_MULTIPLE); | |
559 | ||
560 | aq_nic_cfg->txds = max(ring->tx_pending, hw_caps->txds_min); | |
561 | aq_nic_cfg->txds = min(aq_nic_cfg->txds, hw_caps->txds_max); | |
562 | aq_nic_cfg->txds = ALIGN(aq_nic_cfg->txds, AQ_HW_TXD_MULTIPLE); | |
563 | ||
564 | for (aq_nic->aq_vecs = 0; aq_nic->aq_vecs < aq_nic_cfg->vecs; | |
565 | aq_nic->aq_vecs++) { | |
566 | aq_nic->aq_vec[aq_nic->aq_vecs] = | |
567 | aq_vec_alloc(aq_nic, aq_nic->aq_vecs, aq_nic_cfg); | |
568 | if (unlikely(!aq_nic->aq_vec[aq_nic->aq_vecs])) { | |
569 | err = -ENOMEM; | |
570 | goto err_exit; | |
571 | } | |
572 | } | |
573 | if (ndev_running) | |
00f54e68 | 574 | err = dev_open(ndev, NULL); |
c1af5427 AM |
575 | |
576 | err_exit: | |
577 | return err; | |
578 | } | |
579 | ||
c5760d03 DV |
580 | const struct ethtool_ops aq_ethtool_ops = { |
581 | .get_link = aq_ethtool_get_link, | |
582 | .get_regs_len = aq_ethtool_get_regs_len, | |
583 | .get_regs = aq_ethtool_get_regs, | |
c5760d03 DV |
584 | .get_drvinfo = aq_ethtool_get_drvinfo, |
585 | .get_strings = aq_ethtool_get_strings, | |
586 | .get_rxfh_indir_size = aq_ethtool_get_rss_indir_size, | |
a0da96c0 YE |
587 | .get_wol = aq_ethtool_get_wol, |
588 | .set_wol = aq_ethtool_set_wol, | |
b8d68b62 | 589 | .nway_reset = aq_ethtool_nway_reset, |
c1af5427 AM |
590 | .get_ringparam = aq_get_ringparam, |
591 | .set_ringparam = aq_set_ringparam, | |
92ab6407 YE |
592 | .get_eee = aq_ethtool_get_eee, |
593 | .set_eee = aq_ethtool_set_eee, | |
288551de IR |
594 | .get_pauseparam = aq_ethtool_get_pauseparam, |
595 | .set_pauseparam = aq_ethtool_set_pauseparam, | |
c5760d03 DV |
596 | .get_rxfh_key_size = aq_ethtool_get_rss_key_size, |
597 | .get_rxfh = aq_ethtool_get_rss, | |
39163767 | 598 | .set_rxfh = aq_ethtool_set_rss, |
c5760d03 | 599 | .get_rxnfc = aq_ethtool_get_rxnfc, |
8d0bcb01 | 600 | .set_rxnfc = aq_ethtool_set_rxnfc, |
c5760d03 | 601 | .get_sset_count = aq_ethtool_get_sset_count, |
f8244ab5 PR |
602 | .get_ethtool_stats = aq_ethtool_stats, |
603 | .get_link_ksettings = aq_ethtool_get_link_ksettings, | |
604 | .set_link_ksettings = aq_ethtool_set_link_ksettings, | |
b82ee71a IR |
605 | .get_coalesce = aq_ethtool_get_coalesce, |
606 | .set_coalesce = aq_ethtool_set_coalesce, | |
c5760d03 | 607 | }; |