]>
Commit | Line | Data |
---|---|---|
d71d8381 JS |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // Copyright (c) 2016-2017 Hisilicon Limited. | |
496d03e9 S |
3 | |
4 | #include <linux/etherdevice.h> | |
5 | #include <linux/string.h> | |
16b5e501 | 6 | #include <linux/phy.h> |
496d03e9 S |
7 | |
8 | #include "hns3_enet.h" | |
9 | ||
10 | struct hns3_stats { | |
11 | char stats_string[ETH_GSTRING_LEN]; | |
496d03e9 S |
12 | int stats_offset; |
13 | }; | |
14 | ||
15 | /* tqp related stats */ | |
16 | #define HNS3_TQP_STAT(_string, _member) { \ | |
17 | .stats_string = _string, \ | |
57ffee73 JS |
18 | .stats_offset = offsetof(struct hns3_enet_ring, stats) +\ |
19 | offsetof(struct ring_stats, _member), \ | |
b59f558c | 20 | } |
496d03e9 S |
21 | |
22 | static const struct hns3_stats hns3_txq_stats[] = { | |
23 | /* Tx per-queue statistics */ | |
a6c51c26 | 24 | HNS3_TQP_STAT("io_err_cnt", io_err_cnt), |
0c218123 | 25 | HNS3_TQP_STAT("dropped", sw_err_cnt), |
a6c51c26 JS |
26 | HNS3_TQP_STAT("seg_pkt_cnt", seg_pkt_cnt), |
27 | HNS3_TQP_STAT("packets", tx_pkts), | |
28 | HNS3_TQP_STAT("bytes", tx_bytes), | |
29 | HNS3_TQP_STAT("errors", tx_err_cnt), | |
0c218123 JS |
30 | HNS3_TQP_STAT("wake", restart_queue), |
31 | HNS3_TQP_STAT("busy", tx_busy), | |
3d5f3741 | 32 | HNS3_TQP_STAT("copy", tx_copy), |
496d03e9 S |
33 | }; |
34 | ||
35 | #define HNS3_TXQ_STATS_COUNT ARRAY_SIZE(hns3_txq_stats) | |
36 | ||
37 | static const struct hns3_stats hns3_rxq_stats[] = { | |
38 | /* Rx per-queue statistics */ | |
a6c51c26 | 39 | HNS3_TQP_STAT("io_err_cnt", io_err_cnt), |
0c218123 | 40 | HNS3_TQP_STAT("dropped", sw_err_cnt), |
a6c51c26 JS |
41 | HNS3_TQP_STAT("seg_pkt_cnt", seg_pkt_cnt), |
42 | HNS3_TQP_STAT("packets", rx_pkts), | |
43 | HNS3_TQP_STAT("bytes", rx_bytes), | |
44 | HNS3_TQP_STAT("errors", rx_err_cnt), | |
45 | HNS3_TQP_STAT("reuse_pg_cnt", reuse_pg_cnt), | |
46 | HNS3_TQP_STAT("err_pkt_len", err_pkt_len), | |
a6c51c26 JS |
47 | HNS3_TQP_STAT("err_bd_num", err_bd_num), |
48 | HNS3_TQP_STAT("l2_err", l2_err), | |
49 | HNS3_TQP_STAT("l3l4_csum_err", l3l4_csum_err), | |
c376fa1a | 50 | HNS3_TQP_STAT("multicast", rx_multicast), |
d21ff4f9 | 51 | HNS3_TQP_STAT("non_reuse_pg", non_reuse_pg), |
496d03e9 S |
52 | }; |
53 | ||
54 | #define HNS3_RXQ_STATS_COUNT ARRAY_SIZE(hns3_rxq_stats) | |
55 | ||
56 | #define HNS3_TQP_STATS_COUNT (HNS3_TXQ_STATS_COUNT + HNS3_RXQ_STATS_COUNT) | |
57 | ||
d4017665 | 58 | #define HNS3_SELF_TEST_TYPE_NUM 3 |
c39c4d98 YL |
59 | #define HNS3_NIC_LB_TEST_PKT_NUM 1 |
60 | #define HNS3_NIC_LB_TEST_RING_ID 0 | |
61 | #define HNS3_NIC_LB_TEST_PACKET_SIZE 128 | |
b37ce587 | 62 | #define HNS3_NIC_LB_SETUP_USEC 10000 |
c39c4d98 YL |
63 | |
64 | /* Nic loopback test err */ | |
65 | #define HNS3_NIC_LB_TEST_NO_MEM_ERR 1 | |
66 | #define HNS3_NIC_LB_TEST_TX_CNT_ERR 2 | |
67 | #define HNS3_NIC_LB_TEST_RX_CNT_ERR 3 | |
68 | ||
496d03e9 S |
69 | struct hns3_link_mode_mapping { |
70 | u32 hns3_link_mode; | |
71 | u32 ethtool_link_mode; | |
72 | }; | |
73 | ||
e4d68dae | 74 | static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop, bool en) |
c39c4d98 YL |
75 | { |
76 | struct hnae3_handle *h = hns3_get_handle(ndev); | |
829edbd8 | 77 | bool vlan_filter_enable; |
c39c4d98 YL |
78 | int ret; |
79 | ||
80 | if (!h->ae_algo->ops->set_loopback || | |
81 | !h->ae_algo->ops->set_promisc_mode) | |
82 | return -EOPNOTSUPP; | |
83 | ||
84 | switch (loop) { | |
4dc13b96 FL |
85 | case HNAE3_LOOP_SERIAL_SERDES: |
86 | case HNAE3_LOOP_PARALLEL_SERDES: | |
eb66d503 | 87 | case HNAE3_LOOP_APP: |
e4d68dae | 88 | ret = h->ae_algo->ops->set_loopback(h, loop, en); |
c39c4d98 YL |
89 | break; |
90 | default: | |
91 | ret = -ENOTSUPP; | |
92 | break; | |
93 | } | |
94 | ||
95 | if (ret) | |
96 | return ret; | |
97 | ||
829edbd8 JS |
98 | if (en) { |
99 | h->ae_algo->ops->set_promisc_mode(h, true, true); | |
100 | } else { | |
101 | /* recover promisc mode before loopback test */ | |
102 | hns3_update_promisc_mode(ndev, h->netdev_flags); | |
103 | vlan_filter_enable = ndev->flags & IFF_PROMISC ? false : true; | |
104 | hns3_enable_vlan_filter(ndev, vlan_filter_enable); | |
105 | } | |
c39c4d98 YL |
106 | |
107 | return ret; | |
108 | } | |
109 | ||
110 | static int hns3_lp_up(struct net_device *ndev, enum hnae3_loop loop_mode) | |
111 | { | |
112 | struct hnae3_handle *h = hns3_get_handle(ndev); | |
113 | int ret; | |
114 | ||
7b763f3f FL |
115 | ret = hns3_nic_reset_all_ring(h); |
116 | if (ret) | |
117 | return ret; | |
118 | ||
e4d68dae | 119 | ret = hns3_lp_setup(ndev, loop_mode, true); |
b37ce587 | 120 | usleep_range(HNS3_NIC_LB_SETUP_USEC, HNS3_NIC_LB_SETUP_USEC * 2); |
c39c4d98 | 121 | |
47ef6dec | 122 | return ret; |
c39c4d98 YL |
123 | } |
124 | ||
e4d68dae | 125 | static int hns3_lp_down(struct net_device *ndev, enum hnae3_loop loop_mode) |
c39c4d98 | 126 | { |
c39c4d98 YL |
127 | int ret; |
128 | ||
e4d68dae | 129 | ret = hns3_lp_setup(ndev, loop_mode, false); |
c39c4d98 YL |
130 | if (ret) { |
131 | netdev_err(ndev, "lb_setup return error: %d\n", ret); | |
132 | return ret; | |
133 | } | |
134 | ||
b37ce587 | 135 | usleep_range(HNS3_NIC_LB_SETUP_USEC, HNS3_NIC_LB_SETUP_USEC * 2); |
c39c4d98 YL |
136 | |
137 | return 0; | |
138 | } | |
139 | ||
140 | static void hns3_lp_setup_skb(struct sk_buff *skb) | |
141 | { | |
142 | struct net_device *ndev = skb->dev; | |
143 | unsigned char *packet; | |
144 | struct ethhdr *ethh; | |
145 | unsigned int i; | |
146 | ||
147 | skb_reserve(skb, NET_IP_ALIGN); | |
148 | ethh = skb_put(skb, sizeof(struct ethhdr)); | |
149 | packet = skb_put(skb, HNS3_NIC_LB_TEST_PACKET_SIZE); | |
150 | ||
151 | memcpy(ethh->h_dest, ndev->dev_addr, ETH_ALEN); | |
9b2f3477 WL |
152 | |
153 | /* The dst mac addr of loopback packet is the same as the host' | |
154 | * mac addr, the SSU component may loop back the packet to host | |
155 | * before the packet reaches mac or serdes, which will defect | |
156 | * the purpose of mac or serdes selftest. | |
157 | */ | |
7f7d9e50 | 158 | ethh->h_dest[5] += 0x1f; |
c39c4d98 YL |
159 | eth_zero_addr(ethh->h_source); |
160 | ethh->h_proto = htons(ETH_P_ARP); | |
161 | skb_reset_mac_header(skb); | |
162 | ||
163 | for (i = 0; i < HNS3_NIC_LB_TEST_PACKET_SIZE; i++) | |
164 | packet[i] = (unsigned char)(i & 0xff); | |
165 | } | |
166 | ||
167 | static void hns3_lb_check_skb_data(struct hns3_enet_ring *ring, | |
168 | struct sk_buff *skb) | |
169 | { | |
170 | struct hns3_enet_tqp_vector *tqp_vector = ring->tqp_vector; | |
171 | unsigned char *packet = skb->data; | |
172 | u32 i; | |
173 | ||
174 | for (i = 0; i < skb->len; i++) | |
175 | if (packet[i] != (unsigned char)(i & 0xff)) | |
176 | break; | |
177 | ||
178 | /* The packet is correctly received */ | |
179 | if (i == skb->len) | |
180 | tqp_vector->rx_group.total_packets++; | |
181 | else | |
182 | print_hex_dump(KERN_ERR, "selftest:", DUMP_PREFIX_OFFSET, 16, 1, | |
183 | skb->data, skb->len, true); | |
184 | ||
185 | dev_kfree_skb_any(skb); | |
186 | } | |
187 | ||
188 | static u32 hns3_lb_check_rx_ring(struct hns3_nic_priv *priv, u32 budget) | |
189 | { | |
190 | struct hnae3_handle *h = priv->ae_handle; | |
191 | struct hnae3_knic_private_info *kinfo; | |
192 | u32 i, rcv_good_pkt_total = 0; | |
193 | ||
194 | kinfo = &h->kinfo; | |
195 | for (i = kinfo->num_tqps; i < kinfo->num_tqps * 2; i++) { | |
196 | struct hns3_enet_ring *ring = priv->ring_data[i].ring; | |
197 | struct hns3_enet_ring_group *rx_group; | |
198 | u64 pre_rx_pkt; | |
199 | ||
200 | rx_group = &ring->tqp_vector->rx_group; | |
201 | pre_rx_pkt = rx_group->total_packets; | |
202 | ||
d7099d15 | 203 | preempt_disable(); |
c39c4d98 | 204 | hns3_clean_rx_ring(ring, budget, hns3_lb_check_skb_data); |
d7099d15 | 205 | preempt_enable(); |
c39c4d98 YL |
206 | |
207 | rcv_good_pkt_total += (rx_group->total_packets - pre_rx_pkt); | |
208 | rx_group->total_packets = pre_rx_pkt; | |
209 | } | |
210 | return rcv_good_pkt_total; | |
211 | } | |
212 | ||
213 | static void hns3_lb_clear_tx_ring(struct hns3_nic_priv *priv, u32 start_ringid, | |
214 | u32 end_ringid, u32 budget) | |
215 | { | |
216 | u32 i; | |
217 | ||
218 | for (i = start_ringid; i <= end_ringid; i++) { | |
219 | struct hns3_enet_ring *ring = priv->ring_data[i].ring; | |
220 | ||
799997a3 | 221 | hns3_clean_tx_ring(ring); |
c39c4d98 YL |
222 | } |
223 | } | |
224 | ||
225 | /** | |
226 | * hns3_lp_run_test - run loopback test | |
227 | * @ndev: net device | |
228 | * @mode: loopback type | |
229 | */ | |
230 | static int hns3_lp_run_test(struct net_device *ndev, enum hnae3_loop mode) | |
231 | { | |
232 | struct hns3_nic_priv *priv = netdev_priv(ndev); | |
233 | struct sk_buff *skb; | |
234 | u32 i, good_cnt; | |
235 | int ret_val = 0; | |
236 | ||
237 | skb = alloc_skb(HNS3_NIC_LB_TEST_PACKET_SIZE + ETH_HLEN + NET_IP_ALIGN, | |
238 | GFP_KERNEL); | |
239 | if (!skb) | |
240 | return HNS3_NIC_LB_TEST_NO_MEM_ERR; | |
241 | ||
242 | skb->dev = ndev; | |
243 | hns3_lp_setup_skb(skb); | |
244 | skb->queue_mapping = HNS3_NIC_LB_TEST_RING_ID; | |
245 | ||
246 | good_cnt = 0; | |
247 | for (i = 0; i < HNS3_NIC_LB_TEST_PKT_NUM; i++) { | |
248 | netdev_tx_t tx_ret; | |
249 | ||
250 | skb_get(skb); | |
251 | tx_ret = hns3_nic_net_xmit(skb, ndev); | |
8f9eed1a | 252 | if (tx_ret == NETDEV_TX_OK) { |
c39c4d98 | 253 | good_cnt++; |
8f9eed1a YL |
254 | } else { |
255 | kfree_skb(skb); | |
c39c4d98 YL |
256 | netdev_err(ndev, "hns3_lb_run_test xmit failed: %d\n", |
257 | tx_ret); | |
8f9eed1a | 258 | } |
c39c4d98 YL |
259 | } |
260 | if (good_cnt != HNS3_NIC_LB_TEST_PKT_NUM) { | |
261 | ret_val = HNS3_NIC_LB_TEST_TX_CNT_ERR; | |
262 | netdev_err(ndev, "mode %d sent fail, cnt=0x%x, budget=0x%x\n", | |
263 | mode, good_cnt, HNS3_NIC_LB_TEST_PKT_NUM); | |
264 | goto out; | |
265 | } | |
266 | ||
267 | /* Allow 200 milliseconds for packets to go from Tx to Rx */ | |
268 | msleep(200); | |
269 | ||
270 | good_cnt = hns3_lb_check_rx_ring(priv, HNS3_NIC_LB_TEST_PKT_NUM); | |
271 | if (good_cnt != HNS3_NIC_LB_TEST_PKT_NUM) { | |
272 | ret_val = HNS3_NIC_LB_TEST_RX_CNT_ERR; | |
273 | netdev_err(ndev, "mode %d recv fail, cnt=0x%x, budget=0x%x\n", | |
274 | mode, good_cnt, HNS3_NIC_LB_TEST_PKT_NUM); | |
275 | } | |
276 | ||
277 | out: | |
278 | hns3_lb_clear_tx_ring(priv, HNS3_NIC_LB_TEST_RING_ID, | |
279 | HNS3_NIC_LB_TEST_RING_ID, | |
280 | HNS3_NIC_LB_TEST_PKT_NUM); | |
281 | ||
282 | kfree_skb(skb); | |
283 | return ret_val; | |
284 | } | |
285 | ||
286 | /** | |
287 | * hns3_nic_self_test - self test | |
288 | * @ndev: net device | |
289 | * @eth_test: test cmd | |
290 | * @data: test result | |
291 | */ | |
292 | static void hns3_self_test(struct net_device *ndev, | |
293 | struct ethtool_test *eth_test, u64 *data) | |
294 | { | |
295 | struct hns3_nic_priv *priv = netdev_priv(ndev); | |
296 | struct hnae3_handle *h = priv->ae_handle; | |
5fd50ac3 | 297 | int st_param[HNS3_SELF_TEST_TYPE_NUM][2]; |
c39c4d98 | 298 | bool if_running = netif_running(ndev); |
f96818a7 YL |
299 | #if IS_ENABLED(CONFIG_VLAN_8021Q) |
300 | bool dis_vlan_filter; | |
301 | #endif | |
c39c4d98 YL |
302 | int test_index = 0; |
303 | u32 i; | |
304 | ||
257e4f29 HT |
305 | if (hns3_nic_resetting(ndev)) { |
306 | netdev_err(ndev, "dev resetting!"); | |
307 | return; | |
308 | } | |
309 | ||
c39c4d98 YL |
310 | /* Only do offline selftest, or pass by default */ |
311 | if (eth_test->flags != ETH_TEST_FL_OFFLINE) | |
312 | return; | |
313 | ||
eb66d503 FL |
314 | st_param[HNAE3_LOOP_APP][0] = HNAE3_LOOP_APP; |
315 | st_param[HNAE3_LOOP_APP][1] = | |
316 | h->flags & HNAE3_SUPPORT_APP_LOOPBACK; | |
c39c4d98 | 317 | |
4dc13b96 FL |
318 | st_param[HNAE3_LOOP_SERIAL_SERDES][0] = HNAE3_LOOP_SERIAL_SERDES; |
319 | st_param[HNAE3_LOOP_SERIAL_SERDES][1] = | |
320 | h->flags & HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK; | |
321 | ||
322 | st_param[HNAE3_LOOP_PARALLEL_SERDES][0] = | |
323 | HNAE3_LOOP_PARALLEL_SERDES; | |
324 | st_param[HNAE3_LOOP_PARALLEL_SERDES][1] = | |
325 | h->flags & HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK; | |
5fd50ac3 | 326 | |
c39c4d98 | 327 | if (if_running) |
3f8601f0 | 328 | ndev->netdev_ops->ndo_stop(ndev); |
c39c4d98 | 329 | |
f96818a7 YL |
330 | #if IS_ENABLED(CONFIG_VLAN_8021Q) |
331 | /* Disable the vlan filter for selftest does not support it */ | |
332 | dis_vlan_filter = (ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) && | |
333 | h->ae_algo->ops->enable_vlan_filter; | |
334 | if (dis_vlan_filter) | |
335 | h->ae_algo->ops->enable_vlan_filter(h, false); | |
336 | #endif | |
337 | ||
7786a996 JS |
338 | /* Tell firmware to stop mac autoneg before loopback test start, |
339 | * otherwise loopback test may be failed when the port is still | |
340 | * negotiating. | |
341 | */ | |
342 | if (h->ae_algo->ops->halt_autoneg) | |
343 | h->ae_algo->ops->halt_autoneg(h, true); | |
344 | ||
c39c4d98 YL |
345 | set_bit(HNS3_NIC_STATE_TESTING, &priv->state); |
346 | ||
5fd50ac3 | 347 | for (i = 0; i < HNS3_SELF_TEST_TYPE_NUM; i++) { |
c39c4d98 YL |
348 | enum hnae3_loop loop_type = (enum hnae3_loop)st_param[i][0]; |
349 | ||
350 | if (!st_param[i][1]) | |
351 | continue; | |
352 | ||
353 | data[test_index] = hns3_lp_up(ndev, loop_type); | |
47ef6dec | 354 | if (!data[test_index]) |
c39c4d98 | 355 | data[test_index] = hns3_lp_run_test(ndev, loop_type); |
47ef6dec JS |
356 | |
357 | hns3_lp_down(ndev, loop_type); | |
c39c4d98 YL |
358 | |
359 | if (data[test_index]) | |
360 | eth_test->flags |= ETH_TEST_FL_FAILED; | |
361 | ||
362 | test_index++; | |
363 | } | |
364 | ||
365 | clear_bit(HNS3_NIC_STATE_TESTING, &priv->state); | |
366 | ||
7786a996 JS |
367 | if (h->ae_algo->ops->halt_autoneg) |
368 | h->ae_algo->ops->halt_autoneg(h, false); | |
369 | ||
f96818a7 YL |
370 | #if IS_ENABLED(CONFIG_VLAN_8021Q) |
371 | if (dis_vlan_filter) | |
372 | h->ae_algo->ops->enable_vlan_filter(h, true); | |
373 | #endif | |
374 | ||
c39c4d98 | 375 | if (if_running) |
3f8601f0 | 376 | ndev->netdev_ops->ndo_open(ndev); |
c39c4d98 YL |
377 | } |
378 | ||
496d03e9 S |
379 | static int hns3_get_sset_count(struct net_device *netdev, int stringset) |
380 | { | |
9780cb97 | 381 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 S |
382 | const struct hnae3_ae_ops *ops = h->ae_algo->ops; |
383 | ||
384 | if (!ops->get_sset_count) | |
385 | return -EOPNOTSUPP; | |
386 | ||
387 | switch (stringset) { | |
388 | case ETH_SS_STATS: | |
389 | return ((HNS3_TQP_STATS_COUNT * h->kinfo.num_tqps) + | |
390 | ops->get_sset_count(h, stringset)); | |
391 | ||
392 | case ETH_SS_TEST: | |
393 | return ops->get_sset_count(h, stringset); | |
496d03e9 | 394 | |
fa7a4bd5 JS |
395 | default: |
396 | return -EOPNOTSUPP; | |
397 | } | |
496d03e9 S |
398 | } |
399 | ||
400 | static void *hns3_update_strings(u8 *data, const struct hns3_stats *stats, | |
a6c51c26 | 401 | u32 stat_count, u32 num_tqps, const char *prefix) |
496d03e9 | 402 | { |
a6c51c26 | 403 | #define MAX_PREFIX_SIZE (6 + 4) |
496d03e9 S |
404 | u32 size_left; |
405 | u32 i, j; | |
406 | u32 n1; | |
407 | ||
408 | for (i = 0; i < num_tqps; i++) { | |
409 | for (j = 0; j < stat_count; j++) { | |
410 | data[ETH_GSTRING_LEN - 1] = '\0'; | |
411 | ||
412 | /* first, prepend the prefix string */ | |
0c218123 | 413 | n1 = snprintf(data, MAX_PREFIX_SIZE, "%s%d_", |
a6c51c26 | 414 | prefix, i); |
496d03e9 S |
415 | n1 = min_t(uint, n1, MAX_PREFIX_SIZE - 1); |
416 | size_left = (ETH_GSTRING_LEN - 1) - n1; | |
417 | ||
418 | /* now, concatenate the stats string to it */ | |
419 | strncat(data, stats[j].stats_string, size_left); | |
420 | data += ETH_GSTRING_LEN; | |
421 | } | |
422 | } | |
423 | ||
424 | return data; | |
425 | } | |
426 | ||
427 | static u8 *hns3_get_strings_tqps(struct hnae3_handle *handle, u8 *data) | |
428 | { | |
429 | struct hnae3_knic_private_info *kinfo = &handle->kinfo; | |
a6c51c26 JS |
430 | const char tx_prefix[] = "txq"; |
431 | const char rx_prefix[] = "rxq"; | |
496d03e9 S |
432 | |
433 | /* get strings for Tx */ | |
434 | data = hns3_update_strings(data, hns3_txq_stats, HNS3_TXQ_STATS_COUNT, | |
a6c51c26 | 435 | kinfo->num_tqps, tx_prefix); |
496d03e9 S |
436 | |
437 | /* get strings for Rx */ | |
438 | data = hns3_update_strings(data, hns3_rxq_stats, HNS3_RXQ_STATS_COUNT, | |
a6c51c26 | 439 | kinfo->num_tqps, rx_prefix); |
496d03e9 S |
440 | |
441 | return data; | |
442 | } | |
443 | ||
444 | static void hns3_get_strings(struct net_device *netdev, u32 stringset, u8 *data) | |
445 | { | |
9780cb97 | 446 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 S |
447 | const struct hnae3_ae_ops *ops = h->ae_algo->ops; |
448 | char *buff = (char *)data; | |
449 | ||
450 | if (!ops->get_strings) | |
451 | return; | |
452 | ||
453 | switch (stringset) { | |
454 | case ETH_SS_STATS: | |
455 | buff = hns3_get_strings_tqps(h, buff); | |
63cbf7a9 | 456 | ops->get_strings(h, stringset, (u8 *)buff); |
496d03e9 S |
457 | break; |
458 | case ETH_SS_TEST: | |
459 | ops->get_strings(h, stringset, data); | |
460 | break; | |
fa7a4bd5 JS |
461 | default: |
462 | break; | |
496d03e9 S |
463 | } |
464 | } | |
465 | ||
466 | static u64 *hns3_get_stats_tqps(struct hnae3_handle *handle, u64 *data) | |
467 | { | |
468 | struct hns3_nic_priv *nic_priv = (struct hns3_nic_priv *)handle->priv; | |
469 | struct hnae3_knic_private_info *kinfo = &handle->kinfo; | |
470 | struct hns3_enet_ring *ring; | |
471 | u8 *stat; | |
94bfaafa | 472 | int i, j; |
496d03e9 S |
473 | |
474 | /* get stats for Tx */ | |
475 | for (i = 0; i < kinfo->num_tqps; i++) { | |
476 | ring = nic_priv->ring_data[i].ring; | |
94bfaafa JS |
477 | for (j = 0; j < HNS3_TXQ_STATS_COUNT; j++) { |
478 | stat = (u8 *)ring + hns3_txq_stats[j].stats_offset; | |
496d03e9 S |
479 | *data++ = *(u64 *)stat; |
480 | } | |
481 | } | |
482 | ||
483 | /* get stats for Rx */ | |
484 | for (i = 0; i < kinfo->num_tqps; i++) { | |
485 | ring = nic_priv->ring_data[i + kinfo->num_tqps].ring; | |
94bfaafa JS |
486 | for (j = 0; j < HNS3_RXQ_STATS_COUNT; j++) { |
487 | stat = (u8 *)ring + hns3_rxq_stats[j].stats_offset; | |
496d03e9 S |
488 | *data++ = *(u64 *)stat; |
489 | } | |
490 | } | |
491 | ||
492 | return data; | |
493 | } | |
494 | ||
495 | /* hns3_get_stats - get detail statistics. | |
496 | * @netdev: net device | |
497 | * @stats: statistics info. | |
498 | * @data: statistics data. | |
499 | */ | |
1db9b1bf YL |
500 | static void hns3_get_stats(struct net_device *netdev, |
501 | struct ethtool_stats *stats, u64 *data) | |
496d03e9 | 502 | { |
9780cb97 | 503 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 S |
504 | u64 *p = data; |
505 | ||
c4e401e5 HT |
506 | if (hns3_nic_resetting(netdev)) { |
507 | netdev_err(netdev, "dev resetting, could not get stats\n"); | |
508 | return; | |
509 | } | |
510 | ||
496d03e9 S |
511 | if (!h->ae_algo->ops->get_stats || !h->ae_algo->ops->update_stats) { |
512 | netdev_err(netdev, "could not get any statistics\n"); | |
513 | return; | |
514 | } | |
515 | ||
bf909456 | 516 | h->ae_algo->ops->update_stats(h, &netdev->stats); |
496d03e9 S |
517 | |
518 | /* get per-queue stats */ | |
519 | p = hns3_get_stats_tqps(h, p); | |
520 | ||
521 | /* get MAC & other misc hardware stats */ | |
522 | h->ae_algo->ops->get_stats(h, p); | |
523 | } | |
524 | ||
525 | static void hns3_get_drvinfo(struct net_device *netdev, | |
526 | struct ethtool_drvinfo *drvinfo) | |
527 | { | |
528 | struct hns3_nic_priv *priv = netdev_priv(netdev); | |
529 | struct hnae3_handle *h = priv->ae_handle; | |
530 | ||
63cbf7a9 YM |
531 | if (!h->ae_algo->ops->get_fw_version) { |
532 | netdev_err(netdev, "could not get fw version!\n"); | |
533 | return; | |
534 | } | |
535 | ||
496d03e9 S |
536 | strncpy(drvinfo->version, hns3_driver_version, |
537 | sizeof(drvinfo->version)); | |
538 | drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; | |
539 | ||
540 | strncpy(drvinfo->driver, h->pdev->driver->name, | |
541 | sizeof(drvinfo->driver)); | |
542 | drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; | |
543 | ||
544 | strncpy(drvinfo->bus_info, pci_name(h->pdev), | |
545 | sizeof(drvinfo->bus_info)); | |
546 | drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; | |
547 | ||
548 | snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "0x%08x", | |
549 | priv->ae_handle->ae_algo->ops->get_fw_version(h)); | |
550 | } | |
551 | ||
552 | static u32 hns3_get_link(struct net_device *netdev) | |
553 | { | |
9780cb97 | 554 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 | 555 | |
63cbf7a9 | 556 | if (h->ae_algo->ops->get_status) |
496d03e9 S |
557 | return h->ae_algo->ops->get_status(h); |
558 | else | |
559 | return 0; | |
560 | } | |
561 | ||
562 | static void hns3_get_ringparam(struct net_device *netdev, | |
563 | struct ethtool_ringparam *param) | |
564 | { | |
565 | struct hns3_nic_priv *priv = netdev_priv(netdev); | |
9780cb97 YL |
566 | struct hnae3_handle *h = priv->ae_handle; |
567 | int queue_num = h->kinfo.num_tqps; | |
496d03e9 | 568 | |
257e4f29 HT |
569 | if (hns3_nic_resetting(netdev)) { |
570 | netdev_err(netdev, "dev resetting!"); | |
571 | return; | |
572 | } | |
573 | ||
496d03e9 S |
574 | param->tx_max_pending = HNS3_RING_MAX_PENDING; |
575 | param->rx_max_pending = HNS3_RING_MAX_PENDING; | |
576 | ||
577 | param->tx_pending = priv->ring_data[0].ring->desc_num; | |
578 | param->rx_pending = priv->ring_data[queue_num].ring->desc_num; | |
579 | } | |
580 | ||
581 | static void hns3_get_pauseparam(struct net_device *netdev, | |
582 | struct ethtool_pauseparam *param) | |
583 | { | |
9780cb97 | 584 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 | 585 | |
63cbf7a9 | 586 | if (h->ae_algo->ops->get_pauseparam) |
496d03e9 S |
587 | h->ae_algo->ops->get_pauseparam(h, ¶m->autoneg, |
588 | ¶m->rx_pause, ¶m->tx_pause); | |
589 | } | |
590 | ||
61387774 PL |
591 | static int hns3_set_pauseparam(struct net_device *netdev, |
592 | struct ethtool_pauseparam *param) | |
593 | { | |
594 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
595 | ||
596 | if (h->ae_algo->ops->set_pauseparam) | |
597 | return h->ae_algo->ops->set_pauseparam(h, param->autoneg, | |
598 | param->rx_pause, | |
599 | param->tx_pause); | |
600 | return -EOPNOTSUPP; | |
601 | } | |
602 | ||
5f373b15 FL |
603 | static void hns3_get_ksettings(struct hnae3_handle *h, |
604 | struct ethtool_link_ksettings *cmd) | |
605 | { | |
606 | const struct hnae3_ae_ops *ops = h->ae_algo->ops; | |
607 | ||
608 | /* 1.auto_neg & speed & duplex from cmd */ | |
609 | if (ops->get_ksettings_an_result) | |
610 | ops->get_ksettings_an_result(h, | |
611 | &cmd->base.autoneg, | |
612 | &cmd->base.speed, | |
613 | &cmd->base.duplex); | |
614 | ||
615 | /* 2.get link mode*/ | |
616 | if (ops->get_link_mode) | |
617 | ops->get_link_mode(h, | |
618 | cmd->link_modes.supported, | |
619 | cmd->link_modes.advertising); | |
620 | ||
621 | /* 3.mdix_ctrl&mdix get from phy reg */ | |
622 | if (ops->get_mdix_mode) | |
623 | ops->get_mdix_mode(h, &cmd->base.eth_tp_mdix_ctrl, | |
624 | &cmd->base.eth_tp_mdix); | |
625 | } | |
626 | ||
496d03e9 S |
627 | static int hns3_get_link_ksettings(struct net_device *netdev, |
628 | struct ethtool_link_ksettings *cmd) | |
629 | { | |
9780cb97 | 630 | struct hnae3_handle *h = hns3_get_handle(netdev); |
12f46bc1 | 631 | const struct hnae3_ae_ops *ops; |
88d10bd6 | 632 | u8 module_type; |
5f373b15 | 633 | u8 media_type; |
496d03e9 | 634 | u8 link_stat; |
496d03e9 | 635 | |
12f46bc1 | 636 | ops = h->ae_algo->ops; |
5f373b15 | 637 | if (ops->get_media_type) |
88d10bd6 | 638 | ops->get_media_type(h, &media_type, &module_type); |
12f46bc1 JS |
639 | else |
640 | return -EOPNOTSUPP; | |
641 | ||
5f373b15 FL |
642 | switch (media_type) { |
643 | case HNAE3_MEDIA_TYPE_NONE: | |
644 | cmd->base.port = PORT_NONE; | |
645 | hns3_get_ksettings(h, cmd); | |
646 | break; | |
647 | case HNAE3_MEDIA_TYPE_FIBER: | |
88d10bd6 JS |
648 | if (module_type == HNAE3_MODULE_TYPE_CR) |
649 | cmd->base.port = PORT_DA; | |
650 | else | |
651 | cmd->base.port = PORT_FIBRE; | |
652 | ||
653 | hns3_get_ksettings(h, cmd); | |
654 | break; | |
655 | case HNAE3_MEDIA_TYPE_BACKPLANE: | |
656 | cmd->base.port = PORT_NONE; | |
5f373b15 | 657 | hns3_get_ksettings(h, cmd); |
12f46bc1 | 658 | break; |
5f373b15 | 659 | case HNAE3_MEDIA_TYPE_COPPER: |
5f373b15 | 660 | cmd->base.port = PORT_TP; |
f18635d5 JS |
661 | if (!netdev->phydev) |
662 | hns3_get_ksettings(h, cmd); | |
663 | else | |
664 | phy_ethtool_ksettings_get(netdev->phydev, cmd); | |
12f46bc1 JS |
665 | break; |
666 | default: | |
5f373b15 FL |
667 | |
668 | netdev_warn(netdev, "Unknown media type"); | |
1931dc20 FL |
669 | return 0; |
670 | } | |
671 | ||
12f46bc1 JS |
672 | /* mdio_support */ |
673 | cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22; | |
16b5e501 FL |
674 | |
675 | link_stat = hns3_get_link(netdev); | |
676 | if (!link_stat) { | |
677 | cmd->base.speed = SPEED_UNKNOWN; | |
678 | cmd->base.duplex = DUPLEX_UNKNOWN; | |
496d03e9 S |
679 | } |
680 | ||
496d03e9 S |
681 | return 0; |
682 | } | |
683 | ||
22f48e24 JS |
684 | static int hns3_check_ksettings_param(struct net_device *netdev, |
685 | const struct ethtool_link_ksettings *cmd) | |
686 | { | |
687 | struct hnae3_handle *handle = hns3_get_handle(netdev); | |
688 | const struct hnae3_ae_ops *ops = handle->ae_algo->ops; | |
689 | u8 module_type = HNAE3_MODULE_TYPE_UNKNOWN; | |
690 | u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN; | |
691 | u8 autoneg; | |
692 | u32 speed; | |
693 | u8 duplex; | |
694 | int ret; | |
695 | ||
696 | if (ops->get_ksettings_an_result) { | |
697 | ops->get_ksettings_an_result(handle, &autoneg, &speed, &duplex); | |
698 | if (cmd->base.autoneg == autoneg && cmd->base.speed == speed && | |
699 | cmd->base.duplex == duplex) | |
700 | return 0; | |
701 | } | |
702 | ||
703 | if (ops->get_media_type) | |
704 | ops->get_media_type(handle, &media_type, &module_type); | |
705 | ||
706 | if (cmd->base.duplex != DUPLEX_FULL && | |
707 | media_type != HNAE3_MEDIA_TYPE_COPPER) { | |
708 | netdev_err(netdev, | |
709 | "only copper port supports half duplex!"); | |
710 | return -EINVAL; | |
711 | } | |
712 | ||
713 | if (ops->check_port_speed) { | |
714 | ret = ops->check_port_speed(handle, cmd->base.speed); | |
715 | if (ret) { | |
716 | netdev_err(netdev, "unsupported speed\n"); | |
717 | return ret; | |
718 | } | |
719 | } | |
720 | ||
721 | return 0; | |
722 | } | |
723 | ||
80cb5f3d FL |
724 | static int hns3_set_link_ksettings(struct net_device *netdev, |
725 | const struct ethtool_link_ksettings *cmd) | |
726 | { | |
22f48e24 JS |
727 | struct hnae3_handle *handle = hns3_get_handle(netdev); |
728 | const struct hnae3_ae_ops *ops = handle->ae_algo->ops; | |
729 | int ret = 0; | |
730 | ||
731 | /* Chip don't support this mode. */ | |
95dbab9f PL |
732 | if (cmd->base.speed == SPEED_1000 && cmd->base.duplex == DUPLEX_HALF) |
733 | return -EINVAL; | |
734 | ||
80cb5f3d FL |
735 | /* Only support ksettings_set for netdev with phy attached for now */ |
736 | if (netdev->phydev) | |
737 | return phy_ethtool_ksettings_set(netdev->phydev, cmd); | |
738 | ||
22f48e24 JS |
739 | if (handle->pdev->revision == 0x20) |
740 | return -EOPNOTSUPP; | |
741 | ||
742 | ret = hns3_check_ksettings_param(netdev, cmd); | |
743 | if (ret) | |
744 | return ret; | |
745 | ||
746 | if (ops->set_autoneg) { | |
747 | ret = ops->set_autoneg(handle, cmd->base.autoneg); | |
748 | if (ret) | |
749 | return ret; | |
750 | } | |
751 | ||
752 | if (ops->cfg_mac_speed_dup_h) | |
753 | ret = ops->cfg_mac_speed_dup_h(handle, cmd->base.speed, | |
754 | cmd->base.duplex); | |
755 | ||
756 | return ret; | |
80cb5f3d FL |
757 | } |
758 | ||
496d03e9 S |
759 | static u32 hns3_get_rss_key_size(struct net_device *netdev) |
760 | { | |
9780cb97 | 761 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 | 762 | |
63cbf7a9 | 763 | if (!h->ae_algo->ops->get_rss_key_size) |
3bd6d258 | 764 | return 0; |
496d03e9 S |
765 | |
766 | return h->ae_algo->ops->get_rss_key_size(h); | |
767 | } | |
768 | ||
769 | static u32 hns3_get_rss_indir_size(struct net_device *netdev) | |
770 | { | |
9780cb97 | 771 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 | 772 | |
63cbf7a9 | 773 | if (!h->ae_algo->ops->get_rss_indir_size) |
da44a00f | 774 | return 0; |
496d03e9 S |
775 | |
776 | return h->ae_algo->ops->get_rss_indir_size(h); | |
777 | } | |
778 | ||
779 | static int hns3_get_rss(struct net_device *netdev, u32 *indir, u8 *key, | |
780 | u8 *hfunc) | |
781 | { | |
9780cb97 | 782 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 | 783 | |
63cbf7a9 | 784 | if (!h->ae_algo->ops->get_rss) |
496d03e9 S |
785 | return -EOPNOTSUPP; |
786 | ||
787 | return h->ae_algo->ops->get_rss(h, indir, key, hfunc); | |
788 | } | |
789 | ||
790 | static int hns3_set_rss(struct net_device *netdev, const u32 *indir, | |
791 | const u8 *key, const u8 hfunc) | |
792 | { | |
9780cb97 | 793 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 | 794 | |
63cbf7a9 | 795 | if (!h->ae_algo->ops->set_rss) |
496d03e9 S |
796 | return -EOPNOTSUPP; |
797 | ||
775501a1 JS |
798 | if ((h->pdev->revision == 0x20 && |
799 | hfunc != ETH_RSS_HASH_TOP) || (hfunc != ETH_RSS_HASH_NO_CHANGE && | |
800 | hfunc != ETH_RSS_HASH_TOP && hfunc != ETH_RSS_HASH_XOR)) { | |
801 | netdev_err(netdev, "hash func not supported\n"); | |
496d03e9 S |
802 | return -EOPNOTSUPP; |
803 | } | |
775501a1 | 804 | |
496d03e9 S |
805 | if (!indir) { |
806 | netdev_err(netdev, | |
807 | "set rss failed for indir is empty\n"); | |
808 | return -EOPNOTSUPP; | |
809 | } | |
810 | ||
811 | return h->ae_algo->ops->set_rss(h, indir, key, hfunc); | |
812 | } | |
813 | ||
814 | static int hns3_get_rxnfc(struct net_device *netdev, | |
815 | struct ethtool_rxnfc *cmd, | |
816 | u32 *rule_locs) | |
817 | { | |
9780cb97 | 818 | struct hnae3_handle *h = hns3_get_handle(netdev); |
496d03e9 | 819 | |
496d03e9 S |
820 | switch (cmd->cmd) { |
821 | case ETHTOOL_GRXRINGS: | |
05c2314f JS |
822 | cmd->data = h->kinfo.num_tqps; |
823 | return 0; | |
07d29954 | 824 | case ETHTOOL_GRXFH: |
05c2314f JS |
825 | if (h->ae_algo->ops->get_rss_tuple) |
826 | return h->ae_algo->ops->get_rss_tuple(h, cmd); | |
827 | return -EOPNOTSUPP; | |
828 | case ETHTOOL_GRXCLSRLCNT: | |
829 | if (h->ae_algo->ops->get_fd_rule_cnt) | |
830 | return h->ae_algo->ops->get_fd_rule_cnt(h, cmd); | |
831 | return -EOPNOTSUPP; | |
832 | case ETHTOOL_GRXCLSRULE: | |
833 | if (h->ae_algo->ops->get_fd_rule_info) | |
834 | return h->ae_algo->ops->get_fd_rule_info(h, cmd); | |
835 | return -EOPNOTSUPP; | |
836 | case ETHTOOL_GRXCLSRLALL: | |
837 | if (h->ae_algo->ops->get_fd_all_rules) | |
838 | return h->ae_algo->ops->get_fd_all_rules(h, cmd, | |
839 | rule_locs); | |
840 | return -EOPNOTSUPP; | |
496d03e9 S |
841 | default: |
842 | return -EOPNOTSUPP; | |
843 | } | |
496d03e9 S |
844 | } |
845 | ||
7822b083 | 846 | static int hns3_change_all_ring_bd_num(struct hns3_nic_priv *priv, |
c0425944 | 847 | u32 tx_desc_num, u32 rx_desc_num) |
5668abda L |
848 | { |
849 | struct hnae3_handle *h = priv->ae_handle; | |
850 | int i; | |
851 | ||
c0425944 PL |
852 | h->kinfo.num_tx_desc = tx_desc_num; |
853 | h->kinfo.num_rx_desc = rx_desc_num; | |
5668abda | 854 | |
c0425944 PL |
855 | for (i = 0; i < h->kinfo.num_tqps; i++) { |
856 | priv->ring_data[i].ring->desc_num = tx_desc_num; | |
857 | priv->ring_data[i + h->kinfo.num_tqps].ring->desc_num = | |
858 | rx_desc_num; | |
859 | } | |
5668abda L |
860 | |
861 | return hns3_init_all_ring(priv); | |
862 | } | |
863 | ||
7822b083 WY |
864 | static int hns3_set_ringparam(struct net_device *ndev, |
865 | struct ethtool_ringparam *param) | |
5668abda L |
866 | { |
867 | struct hns3_nic_priv *priv = netdev_priv(ndev); | |
868 | struct hnae3_handle *h = priv->ae_handle; | |
869 | bool if_running = netif_running(ndev); | |
c0425944 PL |
870 | u32 old_tx_desc_num, new_tx_desc_num; |
871 | u32 old_rx_desc_num, new_rx_desc_num; | |
872 | int queue_num = h->kinfo.num_tqps; | |
5668abda L |
873 | int ret; |
874 | ||
257e4f29 HT |
875 | if (hns3_nic_resetting(ndev)) |
876 | return -EBUSY; | |
877 | ||
5668abda L |
878 | if (param->rx_mini_pending || param->rx_jumbo_pending) |
879 | return -EINVAL; | |
880 | ||
5668abda | 881 | if (param->tx_pending > HNS3_RING_MAX_PENDING || |
c0425944 PL |
882 | param->tx_pending < HNS3_RING_MIN_PENDING || |
883 | param->rx_pending > HNS3_RING_MAX_PENDING || | |
884 | param->rx_pending < HNS3_RING_MIN_PENDING) { | |
885 | netdev_err(ndev, "Queue depth out of range [%d-%d]\n", | |
886 | HNS3_RING_MIN_PENDING, HNS3_RING_MAX_PENDING); | |
5668abda L |
887 | return -EINVAL; |
888 | } | |
889 | ||
5668abda | 890 | /* Hardware requires that its descriptors must be multiple of eight */ |
c0425944 PL |
891 | new_tx_desc_num = ALIGN(param->tx_pending, HNS3_RING_BD_MULTIPLE); |
892 | new_rx_desc_num = ALIGN(param->rx_pending, HNS3_RING_BD_MULTIPLE); | |
893 | old_tx_desc_num = priv->ring_data[0].ring->desc_num; | |
894 | old_rx_desc_num = priv->ring_data[queue_num].ring->desc_num; | |
895 | if (old_tx_desc_num == new_tx_desc_num && | |
896 | old_rx_desc_num == new_rx_desc_num) | |
5668abda L |
897 | return 0; |
898 | ||
899 | netdev_info(ndev, | |
c0425944 PL |
900 | "Changing Tx/Rx ring depth from %d/%d to %d/%d\n", |
901 | old_tx_desc_num, old_rx_desc_num, | |
902 | new_tx_desc_num, new_rx_desc_num); | |
5668abda L |
903 | |
904 | if (if_running) | |
fc0c174f | 905 | ndev->netdev_ops->ndo_stop(ndev); |
5668abda L |
906 | |
907 | ret = hns3_uninit_all_ring(priv); | |
908 | if (ret) | |
909 | return ret; | |
910 | ||
c0425944 PL |
911 | ret = hns3_change_all_ring_bd_num(priv, new_tx_desc_num, |
912 | new_rx_desc_num); | |
5668abda | 913 | if (ret) { |
c0425944 PL |
914 | ret = hns3_change_all_ring_bd_num(priv, old_tx_desc_num, |
915 | old_rx_desc_num); | |
5668abda L |
916 | if (ret) { |
917 | netdev_err(ndev, | |
918 | "Revert to old bd num fail, ret=%d.\n", ret); | |
919 | return ret; | |
920 | } | |
921 | } | |
922 | ||
923 | if (if_running) | |
fc0c174f | 924 | ret = ndev->netdev_ops->ndo_open(ndev); |
5668abda L |
925 | |
926 | return ret; | |
927 | } | |
928 | ||
f7db940a L |
929 | static int hns3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) |
930 | { | |
931 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
932 | ||
f7db940a L |
933 | switch (cmd->cmd) { |
934 | case ETHTOOL_SRXFH: | |
dd74f815 JS |
935 | if (h->ae_algo->ops->set_rss_tuple) |
936 | return h->ae_algo->ops->set_rss_tuple(h, cmd); | |
937 | return -EOPNOTSUPP; | |
938 | case ETHTOOL_SRXCLSRLINS: | |
939 | if (h->ae_algo->ops->add_fd_entry) | |
940 | return h->ae_algo->ops->add_fd_entry(h, cmd); | |
941 | return -EOPNOTSUPP; | |
942 | case ETHTOOL_SRXCLSRLDEL: | |
943 | if (h->ae_algo->ops->del_fd_entry) | |
944 | return h->ae_algo->ops->del_fd_entry(h, cmd); | |
945 | return -EOPNOTSUPP; | |
f7db940a L |
946 | default: |
947 | return -EOPNOTSUPP; | |
948 | } | |
949 | } | |
950 | ||
d63671d2 FL |
951 | static int hns3_nway_reset(struct net_device *netdev) |
952 | { | |
22f48e24 JS |
953 | struct hnae3_handle *handle = hns3_get_handle(netdev); |
954 | const struct hnae3_ae_ops *ops = handle->ae_algo->ops; | |
d63671d2 | 955 | struct phy_device *phy = netdev->phydev; |
22f48e24 | 956 | int autoneg; |
d63671d2 FL |
957 | |
958 | if (!netif_running(netdev)) | |
959 | return 0; | |
960 | ||
22f48e24 JS |
961 | if (hns3_nic_resetting(netdev)) { |
962 | netdev_err(netdev, "dev resetting!"); | |
963 | return -EBUSY; | |
964 | } | |
965 | ||
966 | if (!ops->get_autoneg || !ops->restart_autoneg) | |
d63671d2 FL |
967 | return -EOPNOTSUPP; |
968 | ||
22f48e24 JS |
969 | autoneg = ops->get_autoneg(handle); |
970 | if (autoneg != AUTONEG_ENABLE) { | |
971 | netdev_err(netdev, | |
972 | "Autoneg is off, don't support to restart it\n"); | |
d63671d2 | 973 | return -EINVAL; |
22f48e24 JS |
974 | } |
975 | ||
976 | if (phy) | |
977 | return genphy_restart_aneg(phy); | |
978 | ||
979 | if (handle->pdev->revision == 0x20) | |
980 | return -EOPNOTSUPP; | |
d63671d2 | 981 | |
22f48e24 | 982 | return ops->restart_autoneg(handle); |
d63671d2 FL |
983 | } |
984 | ||
836df24a | 985 | static void hns3_get_channels(struct net_device *netdev, |
986 | struct ethtool_channels *ch) | |
482d2e9c PL |
987 | { |
988 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
989 | ||
990 | if (h->ae_algo->ops->get_channels) | |
991 | h->ae_algo->ops->get_channels(h, ch); | |
992 | } | |
993 | ||
7e96adc4 FL |
994 | static int hns3_get_coalesce_per_queue(struct net_device *netdev, u32 queue, |
995 | struct ethtool_coalesce *cmd) | |
996 | { | |
997 | struct hns3_enet_tqp_vector *tx_vector, *rx_vector; | |
998 | struct hns3_nic_priv *priv = netdev_priv(netdev); | |
999 | struct hnae3_handle *h = priv->ae_handle; | |
1000 | u16 queue_num = h->kinfo.num_tqps; | |
1001 | ||
257e4f29 HT |
1002 | if (hns3_nic_resetting(netdev)) |
1003 | return -EBUSY; | |
1004 | ||
7e96adc4 FL |
1005 | if (queue >= queue_num) { |
1006 | netdev_err(netdev, | |
1007 | "Invalid queue value %d! Queue max id=%d\n", | |
1008 | queue, queue_num - 1); | |
1009 | return -EINVAL; | |
1010 | } | |
1011 | ||
1012 | tx_vector = priv->ring_data[queue].ring->tqp_vector; | |
1013 | rx_vector = priv->ring_data[queue_num + queue].ring->tqp_vector; | |
1014 | ||
9bc727a9 YL |
1015 | cmd->use_adaptive_tx_coalesce = |
1016 | tx_vector->tx_group.coal.gl_adapt_enable; | |
1017 | cmd->use_adaptive_rx_coalesce = | |
1018 | rx_vector->rx_group.coal.gl_adapt_enable; | |
7e96adc4 | 1019 | |
9bc727a9 YL |
1020 | cmd->tx_coalesce_usecs = tx_vector->tx_group.coal.int_gl; |
1021 | cmd->rx_coalesce_usecs = rx_vector->rx_group.coal.int_gl; | |
7e96adc4 FL |
1022 | |
1023 | cmd->tx_coalesce_usecs_high = h->kinfo.int_rl_setting; | |
1024 | cmd->rx_coalesce_usecs_high = h->kinfo.int_rl_setting; | |
1025 | ||
1026 | return 0; | |
1027 | } | |
1028 | ||
1029 | static int hns3_get_coalesce(struct net_device *netdev, | |
1030 | struct ethtool_coalesce *cmd) | |
1031 | { | |
1032 | return hns3_get_coalesce_per_queue(netdev, 0, cmd); | |
1033 | } | |
1034 | ||
434776a5 FL |
1035 | static int hns3_check_gl_coalesce_para(struct net_device *netdev, |
1036 | struct ethtool_coalesce *cmd) | |
1037 | { | |
1038 | u32 rx_gl, tx_gl; | |
1039 | ||
1040 | if (cmd->rx_coalesce_usecs > HNS3_INT_GL_MAX) { | |
1041 | netdev_err(netdev, | |
1042 | "Invalid rx-usecs value, rx-usecs range is 0-%d\n", | |
1043 | HNS3_INT_GL_MAX); | |
1044 | return -EINVAL; | |
1045 | } | |
1046 | ||
1047 | if (cmd->tx_coalesce_usecs > HNS3_INT_GL_MAX) { | |
1048 | netdev_err(netdev, | |
1049 | "Invalid tx-usecs value, tx-usecs range is 0-%d\n", | |
1050 | HNS3_INT_GL_MAX); | |
1051 | return -EINVAL; | |
1052 | } | |
1053 | ||
1054 | rx_gl = hns3_gl_round_down(cmd->rx_coalesce_usecs); | |
1055 | if (rx_gl != cmd->rx_coalesce_usecs) { | |
1056 | netdev_info(netdev, | |
1057 | "rx_usecs(%d) rounded down to %d, because it must be multiple of 2.\n", | |
1058 | cmd->rx_coalesce_usecs, rx_gl); | |
1059 | } | |
1060 | ||
1061 | tx_gl = hns3_gl_round_down(cmd->tx_coalesce_usecs); | |
1062 | if (tx_gl != cmd->tx_coalesce_usecs) { | |
1063 | netdev_info(netdev, | |
1064 | "tx_usecs(%d) rounded down to %d, because it must be multiple of 2.\n", | |
1065 | cmd->tx_coalesce_usecs, tx_gl); | |
1066 | } | |
1067 | ||
1068 | return 0; | |
1069 | } | |
1070 | ||
1071 | static int hns3_check_rl_coalesce_para(struct net_device *netdev, | |
1072 | struct ethtool_coalesce *cmd) | |
1073 | { | |
1074 | u32 rl; | |
1075 | ||
1076 | if (cmd->tx_coalesce_usecs_high != cmd->rx_coalesce_usecs_high) { | |
1077 | netdev_err(netdev, | |
1078 | "tx_usecs_high must be same as rx_usecs_high.\n"); | |
1079 | return -EINVAL; | |
1080 | } | |
1081 | ||
1082 | if (cmd->rx_coalesce_usecs_high > HNS3_INT_RL_MAX) { | |
1083 | netdev_err(netdev, | |
1084 | "Invalid usecs_high value, usecs_high range is 0-%d\n", | |
1085 | HNS3_INT_RL_MAX); | |
1086 | return -EINVAL; | |
1087 | } | |
1088 | ||
1089 | rl = hns3_rl_round_down(cmd->rx_coalesce_usecs_high); | |
1090 | if (rl != cmd->rx_coalesce_usecs_high) { | |
1091 | netdev_info(netdev, | |
1092 | "usecs_high(%d) rounded down to %d, because it must be multiple of 4.\n", | |
1093 | cmd->rx_coalesce_usecs_high, rl); | |
1094 | } | |
1095 | ||
1096 | return 0; | |
1097 | } | |
1098 | ||
1099 | static int hns3_check_coalesce_para(struct net_device *netdev, | |
1100 | struct ethtool_coalesce *cmd) | |
1101 | { | |
1102 | int ret; | |
1103 | ||
1104 | ret = hns3_check_gl_coalesce_para(netdev, cmd); | |
1105 | if (ret) { | |
1106 | netdev_err(netdev, | |
1107 | "Check gl coalesce param fail. ret = %d\n", ret); | |
1108 | return ret; | |
1109 | } | |
1110 | ||
1111 | ret = hns3_check_rl_coalesce_para(netdev, cmd); | |
1112 | if (ret) { | |
1113 | netdev_err(netdev, | |
1114 | "Check rl coalesce param fail. ret = %d\n", ret); | |
1115 | return ret; | |
1116 | } | |
1117 | ||
1118 | if (cmd->use_adaptive_tx_coalesce == 1 || | |
1119 | cmd->use_adaptive_rx_coalesce == 1) { | |
1120 | netdev_info(netdev, | |
1121 | "adaptive-tx=%d and adaptive-rx=%d, tx_usecs or rx_usecs will changed dynamically.\n", | |
1122 | cmd->use_adaptive_tx_coalesce, | |
1123 | cmd->use_adaptive_rx_coalesce); | |
1124 | } | |
1125 | ||
1126 | return 0; | |
1127 | } | |
1128 | ||
1129 | static void hns3_set_coalesce_per_queue(struct net_device *netdev, | |
1130 | struct ethtool_coalesce *cmd, | |
1131 | u32 queue) | |
1132 | { | |
1133 | struct hns3_enet_tqp_vector *tx_vector, *rx_vector; | |
1134 | struct hns3_nic_priv *priv = netdev_priv(netdev); | |
1135 | struct hnae3_handle *h = priv->ae_handle; | |
1136 | int queue_num = h->kinfo.num_tqps; | |
1137 | ||
1138 | tx_vector = priv->ring_data[queue].ring->tqp_vector; | |
1139 | rx_vector = priv->ring_data[queue_num + queue].ring->tqp_vector; | |
1140 | ||
9bc727a9 YL |
1141 | tx_vector->tx_group.coal.gl_adapt_enable = |
1142 | cmd->use_adaptive_tx_coalesce; | |
1143 | rx_vector->rx_group.coal.gl_adapt_enable = | |
1144 | cmd->use_adaptive_rx_coalesce; | |
434776a5 | 1145 | |
9bc727a9 YL |
1146 | tx_vector->tx_group.coal.int_gl = cmd->tx_coalesce_usecs; |
1147 | rx_vector->rx_group.coal.int_gl = cmd->rx_coalesce_usecs; | |
434776a5 | 1148 | |
9bc727a9 YL |
1149 | hns3_set_vector_coalesce_tx_gl(tx_vector, |
1150 | tx_vector->tx_group.coal.int_gl); | |
1151 | hns3_set_vector_coalesce_rx_gl(rx_vector, | |
1152 | rx_vector->rx_group.coal.int_gl); | |
434776a5 FL |
1153 | |
1154 | hns3_set_vector_coalesce_rl(tx_vector, h->kinfo.int_rl_setting); | |
1155 | hns3_set_vector_coalesce_rl(rx_vector, h->kinfo.int_rl_setting); | |
1156 | } | |
1157 | ||
1158 | static int hns3_set_coalesce(struct net_device *netdev, | |
1159 | struct ethtool_coalesce *cmd) | |
1160 | { | |
1161 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
1162 | u16 queue_num = h->kinfo.num_tqps; | |
1163 | int ret; | |
1164 | int i; | |
1165 | ||
257e4f29 HT |
1166 | if (hns3_nic_resetting(netdev)) |
1167 | return -EBUSY; | |
1168 | ||
434776a5 FL |
1169 | ret = hns3_check_coalesce_para(netdev, cmd); |
1170 | if (ret) | |
1171 | return ret; | |
1172 | ||
1173 | h->kinfo.int_rl_setting = | |
1174 | hns3_rl_round_down(cmd->rx_coalesce_usecs_high); | |
1175 | ||
1176 | for (i = 0; i < queue_num; i++) | |
1177 | hns3_set_coalesce_per_queue(netdev, cmd, i); | |
1178 | ||
1179 | return 0; | |
1180 | } | |
1181 | ||
77b34110 FL |
1182 | static int hns3_get_regs_len(struct net_device *netdev) |
1183 | { | |
1184 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
1185 | ||
1186 | if (!h->ae_algo->ops->get_regs_len) | |
1187 | return -EOPNOTSUPP; | |
1188 | ||
1189 | return h->ae_algo->ops->get_regs_len(h); | |
1190 | } | |
1191 | ||
1192 | static void hns3_get_regs(struct net_device *netdev, | |
1193 | struct ethtool_regs *cmd, void *data) | |
1194 | { | |
1195 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
1196 | ||
1197 | if (!h->ae_algo->ops->get_regs) | |
1198 | return; | |
1199 | ||
1200 | h->ae_algo->ops->get_regs(h, &cmd->version, data); | |
1201 | } | |
1202 | ||
07f8e940 JS |
1203 | static int hns3_set_phys_id(struct net_device *netdev, |
1204 | enum ethtool_phys_id_state state) | |
1205 | { | |
1206 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
1207 | ||
63cbf7a9 | 1208 | if (!h->ae_algo->ops->set_led_id) |
07f8e940 JS |
1209 | return -EOPNOTSUPP; |
1210 | ||
1211 | return h->ae_algo->ops->set_led_id(h, state); | |
1212 | } | |
1213 | ||
bb87be87 YL |
1214 | static u32 hns3_get_msglevel(struct net_device *netdev) |
1215 | { | |
1216 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
1217 | ||
1218 | return h->msg_enable; | |
1219 | } | |
1220 | ||
1221 | static void hns3_set_msglevel(struct net_device *netdev, u32 msg_level) | |
1222 | { | |
1223 | struct hnae3_handle *h = hns3_get_handle(netdev); | |
1224 | ||
1225 | h->msg_enable = msg_level; | |
1226 | } | |
1227 | ||
7e6ec914 JS |
1228 | /* Translate local fec value into ethtool value. */ |
1229 | static unsigned int loc_to_eth_fec(u8 loc_fec) | |
1230 | { | |
1231 | u32 eth_fec = 0; | |
1232 | ||
1233 | if (loc_fec & BIT(HNAE3_FEC_AUTO)) | |
1234 | eth_fec |= ETHTOOL_FEC_AUTO; | |
1235 | if (loc_fec & BIT(HNAE3_FEC_RS)) | |
1236 | eth_fec |= ETHTOOL_FEC_RS; | |
1237 | if (loc_fec & BIT(HNAE3_FEC_BASER)) | |
1238 | eth_fec |= ETHTOOL_FEC_BASER; | |
1239 | ||
1240 | /* if nothing is set, then FEC is off */ | |
1241 | if (!eth_fec) | |
1242 | eth_fec = ETHTOOL_FEC_OFF; | |
1243 | ||
1244 | return eth_fec; | |
1245 | } | |
1246 | ||
1247 | /* Translate ethtool fec value into local value. */ | |
1248 | static unsigned int eth_to_loc_fec(unsigned int eth_fec) | |
1249 | { | |
1250 | u32 loc_fec = 0; | |
1251 | ||
1252 | if (eth_fec & ETHTOOL_FEC_OFF) | |
1253 | return loc_fec; | |
1254 | ||
1255 | if (eth_fec & ETHTOOL_FEC_AUTO) | |
1256 | loc_fec |= BIT(HNAE3_FEC_AUTO); | |
1257 | if (eth_fec & ETHTOOL_FEC_RS) | |
1258 | loc_fec |= BIT(HNAE3_FEC_RS); | |
1259 | if (eth_fec & ETHTOOL_FEC_BASER) | |
1260 | loc_fec |= BIT(HNAE3_FEC_BASER); | |
1261 | ||
1262 | return loc_fec; | |
1263 | } | |
1264 | ||
1265 | static int hns3_get_fecparam(struct net_device *netdev, | |
1266 | struct ethtool_fecparam *fec) | |
1267 | { | |
1268 | struct hnae3_handle *handle = hns3_get_handle(netdev); | |
1269 | const struct hnae3_ae_ops *ops = handle->ae_algo->ops; | |
1270 | u8 fec_ability; | |
1271 | u8 fec_mode; | |
1272 | ||
1273 | if (handle->pdev->revision == 0x20) | |
1274 | return -EOPNOTSUPP; | |
1275 | ||
1276 | if (!ops->get_fec) | |
1277 | return -EOPNOTSUPP; | |
1278 | ||
1279 | ops->get_fec(handle, &fec_ability, &fec_mode); | |
1280 | ||
1281 | fec->fec = loc_to_eth_fec(fec_ability); | |
1282 | fec->active_fec = loc_to_eth_fec(fec_mode); | |
1283 | ||
1284 | return 0; | |
1285 | } | |
1286 | ||
1287 | static int hns3_set_fecparam(struct net_device *netdev, | |
1288 | struct ethtool_fecparam *fec) | |
1289 | { | |
1290 | struct hnae3_handle *handle = hns3_get_handle(netdev); | |
1291 | const struct hnae3_ae_ops *ops = handle->ae_algo->ops; | |
1292 | u32 fec_mode; | |
1293 | ||
1294 | if (handle->pdev->revision == 0x20) | |
1295 | return -EOPNOTSUPP; | |
1296 | ||
1297 | if (!ops->set_fec) | |
1298 | return -EOPNOTSUPP; | |
1299 | fec_mode = eth_to_loc_fec(fec->fec); | |
1300 | return ops->set_fec(handle, fec_mode); | |
1301 | } | |
1302 | ||
424eb834 SM |
1303 | static const struct ethtool_ops hns3vf_ethtool_ops = { |
1304 | .get_drvinfo = hns3_get_drvinfo, | |
1305 | .get_ringparam = hns3_get_ringparam, | |
1306 | .set_ringparam = hns3_set_ringparam, | |
1307 | .get_strings = hns3_get_strings, | |
1308 | .get_ethtool_stats = hns3_get_stats, | |
1309 | .get_sset_count = hns3_get_sset_count, | |
1310 | .get_rxnfc = hns3_get_rxnfc, | |
d97b3072 | 1311 | .set_rxnfc = hns3_set_rxnfc, |
424eb834 SM |
1312 | .get_rxfh_key_size = hns3_get_rss_key_size, |
1313 | .get_rxfh_indir_size = hns3_get_rss_indir_size, | |
1314 | .get_rxfh = hns3_get_rss, | |
1315 | .set_rxfh = hns3_set_rss, | |
1316 | .get_link_ksettings = hns3_get_link_ksettings, | |
849e4607 | 1317 | .get_channels = hns3_get_channels, |
ad31c732 FL |
1318 | .get_coalesce = hns3_get_coalesce, |
1319 | .set_coalesce = hns3_set_coalesce, | |
8e1445a6 JS |
1320 | .get_regs_len = hns3_get_regs_len, |
1321 | .get_regs = hns3_get_regs, | |
175ec96b | 1322 | .get_link = hns3_get_link, |
bb87be87 YL |
1323 | .get_msglevel = hns3_get_msglevel, |
1324 | .set_msglevel = hns3_set_msglevel, | |
424eb834 SM |
1325 | }; |
1326 | ||
496d03e9 | 1327 | static const struct ethtool_ops hns3_ethtool_ops = { |
c39c4d98 | 1328 | .self_test = hns3_self_test, |
496d03e9 S |
1329 | .get_drvinfo = hns3_get_drvinfo, |
1330 | .get_link = hns3_get_link, | |
1331 | .get_ringparam = hns3_get_ringparam, | |
5668abda | 1332 | .set_ringparam = hns3_set_ringparam, |
496d03e9 | 1333 | .get_pauseparam = hns3_get_pauseparam, |
61387774 | 1334 | .set_pauseparam = hns3_set_pauseparam, |
496d03e9 S |
1335 | .get_strings = hns3_get_strings, |
1336 | .get_ethtool_stats = hns3_get_stats, | |
1337 | .get_sset_count = hns3_get_sset_count, | |
1338 | .get_rxnfc = hns3_get_rxnfc, | |
f7db940a | 1339 | .set_rxnfc = hns3_set_rxnfc, |
496d03e9 S |
1340 | .get_rxfh_key_size = hns3_get_rss_key_size, |
1341 | .get_rxfh_indir_size = hns3_get_rss_indir_size, | |
1342 | .get_rxfh = hns3_get_rss, | |
1343 | .set_rxfh = hns3_set_rss, | |
1344 | .get_link_ksettings = hns3_get_link_ksettings, | |
80cb5f3d | 1345 | .set_link_ksettings = hns3_set_link_ksettings, |
d63671d2 | 1346 | .nway_reset = hns3_nway_reset, |
482d2e9c | 1347 | .get_channels = hns3_get_channels, |
09f2af64 | 1348 | .set_channels = hns3_set_channels, |
7e96adc4 | 1349 | .get_coalesce = hns3_get_coalesce, |
434776a5 | 1350 | .set_coalesce = hns3_set_coalesce, |
77b34110 FL |
1351 | .get_regs_len = hns3_get_regs_len, |
1352 | .get_regs = hns3_get_regs, | |
07f8e940 | 1353 | .set_phys_id = hns3_set_phys_id, |
bb87be87 YL |
1354 | .get_msglevel = hns3_get_msglevel, |
1355 | .set_msglevel = hns3_set_msglevel, | |
7e6ec914 JS |
1356 | .get_fecparam = hns3_get_fecparam, |
1357 | .set_fecparam = hns3_set_fecparam, | |
496d03e9 S |
1358 | }; |
1359 | ||
1360 | void hns3_ethtool_set_ops(struct net_device *netdev) | |
1361 | { | |
424eb834 SM |
1362 | struct hnae3_handle *h = hns3_get_handle(netdev); |
1363 | ||
1364 | if (h->flags & HNAE3_SUPPORT_VF) | |
1365 | netdev->ethtool_ops = &hns3vf_ethtool_ops; | |
1366 | else | |
1367 | netdev->ethtool_ops = &hns3_ethtool_ops; | |
496d03e9 | 1368 | } |