1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved.
14 * Copyright (c) 2018, Joyent, Inc.
15 * Copyright 2017 Tegile Systems, Inc. All rights reserved.
16 * Copyright 2020 Ryan Zezeski
17 * Copyright 2020 RackTop Systems, Inc.
18 * Copyright 2021 Oxide Computer Company
19 */
20
21 /*
22 * For more information, please see the big theory statement in i40e_main.c.
23 */
24
25 #include "i40e_sw.h"
26
27 #define I40E_PROP_RX_DMA_THRESH "_rx_dma_threshold"
28 #define I40E_PROP_TX_DMA_THRESH "_tx_dma_threshold"
29 #define I40E_PROP_RX_ITR "_rx_intr_throttle"
30 #define I40E_PROP_TX_ITR "_tx_intr_throttle"
31 #define I40E_PROP_OTHER_ITR "_other_intr_throttle"
32
33 char *i40e_priv_props[] = {
34 I40E_PROP_RX_DMA_THRESH,
35 I40E_PROP_TX_DMA_THRESH,
36 I40E_PROP_RX_ITR,
37 I40E_PROP_TX_ITR,
38 I40E_PROP_OTHER_ITR,
39 NULL
40 };
41
42 static int
43 i40e_group_remove_mac(void *arg, const uint8_t *mac_addr)
44 {
45 i40e_rx_group_t *rxg = arg;
46 i40e_t *i40e = rxg->irg_i40e;
47 struct i40e_aqc_remove_macvlan_element_data filt;
48 struct i40e_hw *hw = &i40e->i40e_hw_space;
49 int ret, i, last;
50 i40e_uaddr_t *iua;
51
52 if (I40E_IS_MULTICAST(mac_addr))
53 return (EINVAL);
54
55 mutex_enter(&i40e->i40e_general_lock);
56
57 if (i40e->i40e_state & I40E_SUSPENDED) {
58 ret = ECANCELED;
59 goto done;
60 }
61
62 for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
63 if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
64 ETHERADDRL) == 0)
65 break;
66 }
67
68 if (i == i40e->i40e_resources.ifr_nmacfilt_used) {
69 ret = ENOENT;
70 goto done;
71 }
72
73 iua = &i40e->i40e_uaddrs[i];
74 ASSERT(i40e->i40e_resources.ifr_nmacfilt_used > 0);
75
76 bzero(&filt, sizeof (filt));
77 bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
78 filt.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
79 I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
80
81 if (i40e_aq_remove_macvlan(hw, iua->iua_vsi, &filt, 1, NULL) !=
82 I40E_SUCCESS) {
83 i40e_error(i40e, "failed to remove mac address "
84 "%2x:%2x:%2x:%2x:%2x:%2x from unicast filter: %d",
85 mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
86 mac_addr[4], mac_addr[5], filt.error_code);
87 ret = EIO;
88 goto done;
89 }
90
91 last = i40e->i40e_resources.ifr_nmacfilt_used - 1;
92 if (i != last) {
93 i40e_uaddr_t *src = &i40e->i40e_uaddrs[last];
94 bcopy(src, iua, sizeof (i40e_uaddr_t));
95 }
96
97 /*
98 * Set the multicast bit in the last one to indicate to ourselves that
99 * it's invalid.
100 */
101 bzero(&i40e->i40e_uaddrs[last], sizeof (i40e_uaddr_t));
102 i40e->i40e_uaddrs[last].iua_mac[0] = 0x01;
103 i40e->i40e_resources.ifr_nmacfilt_used--;
104 ret = 0;
105 done:
106 mutex_exit(&i40e->i40e_general_lock);
107
108 return (ret);
109 }
110
111 static int
112 i40e_group_add_mac(void *arg, const uint8_t *mac_addr)
113 {
114 i40e_rx_group_t *rxg = arg;
115 i40e_t *i40e = rxg->irg_i40e;
116 struct i40e_hw *hw = &i40e->i40e_hw_space;
117 int i, ret;
118 i40e_uaddr_t *iua;
119 struct i40e_aqc_add_macvlan_element_data filt;
120
121 if (I40E_IS_MULTICAST(mac_addr))
122 return (EINVAL);
123
124 mutex_enter(&i40e->i40e_general_lock);
125 if (i40e->i40e_state & I40E_SUSPENDED) {
126 ret = ECANCELED;
127 goto done;
128 }
129
130 if (i40e->i40e_resources.ifr_nmacfilt ==
131 i40e->i40e_resources.ifr_nmacfilt_used) {
132 ret = ENOSPC;
133 goto done;
134 }
135
136 for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
137 if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
138 ETHERADDRL) == 0) {
139 ret = EEXIST;
140 goto done;
141 }
142 }
143
144 bzero(&filt, sizeof (filt));
145 bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
146 filt.flags = I40E_AQC_MACVLAN_ADD_PERFECT_MATCH |
147 I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
148
149 if ((ret = i40e_aq_add_macvlan(hw, rxg->irg_vsi_seid, &filt, 1,
150 NULL)) != I40E_SUCCESS) {
151 i40e_error(i40e, "failed to add mac address "
152 "%2x:%2x:%2x:%2x:%2x:%2x to unicast filter: %d",
153 mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
154 mac_addr[4], mac_addr[5], ret);
155 ret = EIO;
156 goto done;
157 }
158
159 iua = &i40e->i40e_uaddrs[i40e->i40e_resources.ifr_nmacfilt_used];
160 bcopy(mac_addr, iua->iua_mac, ETHERADDRL);
161 iua->iua_vsi = rxg->irg_vsi_seid;
162 i40e->i40e_resources.ifr_nmacfilt_used++;
163 ASSERT(i40e->i40e_resources.ifr_nmacfilt_used <=
164 i40e->i40e_resources.ifr_nmacfilt);
165 ret = 0;
166 done:
167 mutex_exit(&i40e->i40e_general_lock);
168 return (ret);
169 }
170
171 static int
172 i40e_m_start(void *arg)
173 {
174 i40e_t *i40e = arg;
175 int rc = 0;
176
177 mutex_enter(&i40e->i40e_general_lock);
178 if (i40e->i40e_state & I40E_SUSPENDED) {
179 rc = ECANCELED;
180 goto done;
181 }
182
183 if (!i40e_start(i40e)) {
184 rc = EIO;
185 goto done;
186 }
187
188 atomic_or_32(&i40e->i40e_state, I40E_STARTED);
189 done:
190 mutex_exit(&i40e->i40e_general_lock);
191
192 return (rc);
193 }
194
195 static void
196 i40e_m_stop(void *arg)
197 {
198 i40e_t *i40e = arg;
199
200 mutex_enter(&i40e->i40e_general_lock);
201
202 if (i40e->i40e_state & I40E_SUSPENDED)
203 goto done;
204
205 atomic_and_32(&i40e->i40e_state, ~I40E_STARTED);
206 i40e_stop(i40e);
207 done:
208 mutex_exit(&i40e->i40e_general_lock);
209 }
210
211 /*
212 * Enable and disable promiscuous mode as requested. We have to toggle both
213 * unicast and multicast. Note that multicast may already be enabled due to the
214 * i40e_m_multicast may toggle it itself. See i40e_main.c for more information
215 * on this.
216 */
217 static int
218 i40e_m_promisc(void *arg, boolean_t on)
219 {
220 i40e_t *i40e = arg;
221 struct i40e_hw *hw = &i40e->i40e_hw_space;
222 int ret = 0, err = 0;
223
224 mutex_enter(&i40e->i40e_general_lock);
225 if (i40e->i40e_state & I40E_SUSPENDED) {
226 ret = ECANCELED;
227 goto done;
228 }
229
230
231 #if 1
232 /*
233 * XXX KEBE SAYS try this from Linux...
234 *
235 * Their comment says:
236 *
237 * set defport ON for Main VSI instead of true promisc
238 * this way we will get all unicast/multicast and VLAN
239 * promisc behavior but will not get VF or VMDq traffic
240 * replicated on the Main VSI.
241 */
242
243 ret = on ? i40e_aq_set_default_vsi(hw, I40E_DEF_VSI_SEID(i40e), NULL) :
244 i40e_aq_clear_default_vsi(hw, I40E_DEF_VSI_SEID(i40e), NULL);
245 if (ret == I40E_SUCCESS)
246 i40e->i40e_promisc_on = on;
247 else
248 err = EIO;
249 #else
250 ret = i40e_aq_set_vsi_unicast_promiscuous(hw, I40E_DEF_VSI_SEID(i40e),
251 on, NULL, B_FALSE);
252 if (ret != I40E_SUCCESS) {
253 i40e_error(i40e, "failed to %s unicast promiscuity on "
254 "the default VSI: %d", on == B_TRUE ? "enable" : "disable",
255 ret);
256 err = EIO;
257 goto done;
258 }
259
260 /*
261 * If we have a non-zero mcast_promisc_count, then it has already been
262 * enabled or we need to leave it that way and not touch it.
263 */
264 if (i40e->i40e_mcast_promisc_count > 0) {
265 i40e->i40e_promisc_on = on;
266 goto done;
267 }
268
269 ret = i40e_aq_set_vsi_multicast_promiscuous(hw, I40E_DEF_VSI_SEID(i40e),
270 on, NULL);
271 if (ret != I40E_SUCCESS) {
272 i40e_error(i40e, "failed to %s multicast promiscuity on "
273 "the default VSI: %d", on == B_TRUE ? "enable" : "disable",
274 ret);
275
276 /*
277 * Try our best to put us back into a state that MAC expects us
278 * to be in.
279 */
280 ret = i40e_aq_set_vsi_unicast_promiscuous(hw,
281 I40E_DEF_VSI_SEID(i40e), !on, NULL, B_FALSE);
282 if (ret != I40E_SUCCESS) {
283 i40e_error(i40e, "failed to %s unicast promiscuity on "
284 "the default VSI after toggling multicast failed: "
285 "%d", on == B_TRUE ? "disable" : "enable", ret);
286 }
287
288 err = EIO;
289 goto done;
290 } else {
291 i40e->i40e_promisc_on = on;
292 }
293 #endif
294
295 done:
296 mutex_exit(&i40e->i40e_general_lock);
297 return (err);
298 }
299
300 /*
301 * See the big theory statement in i40e_main.c for multicast address management.
302 */
303 static int
304 i40e_multicast_add(i40e_t *i40e, const uint8_t *multicast_address)
305 {
306 struct i40e_hw *hw = &i40e->i40e_hw_space;
307 struct i40e_aqc_add_macvlan_element_data filt;
308 i40e_maddr_t *mc;
309 int ret;
310
311 ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
312
313 if (i40e->i40e_resources.ifr_nmcastfilt_used ==
314 i40e->i40e_resources.ifr_nmcastfilt) {
315 if (i40e->i40e_mcast_promisc_count == 0 &&
316 i40e->i40e_promisc_on == B_FALSE) {
317 ret = i40e_aq_set_vsi_multicast_promiscuous(hw,
318 I40E_DEF_VSI_SEID(i40e), B_TRUE, NULL);
319 if (ret != I40E_SUCCESS) {
320 i40e_error(i40e, "failed to enable multicast "
321 "promiscuous mode on VSI %d: %d",
322 I40E_DEF_VSI_SEID(i40e), ret);
323 return (EIO);
324 }
325 }
326 i40e->i40e_mcast_promisc_count++;
327 return (0);
328 }
329
330 mc = &i40e->i40e_maddrs[i40e->i40e_resources.ifr_nmcastfilt_used];
331 bzero(&filt, sizeof (filt));
332 bcopy(multicast_address, filt.mac_addr, ETHERADDRL);
333 filt.flags = I40E_AQC_MACVLAN_ADD_HASH_MATCH |
334 I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
335
336 if ((ret = i40e_aq_add_macvlan(hw, I40E_DEF_VSI_SEID(i40e), &filt, 1,
337 NULL)) != I40E_SUCCESS) {
338 i40e_error(i40e, "failed to add mac address "
339 "%2x:%2x:%2x:%2x:%2x:%2x to multicast filter: %d",
340 multicast_address[0], multicast_address[1],
341 multicast_address[2], multicast_address[3],
342 multicast_address[4], multicast_address[5],
343 ret);
344 return (EIO);
345 }
346
347 bcopy(multicast_address, mc->ima_mac, ETHERADDRL);
348 i40e->i40e_resources.ifr_nmcastfilt_used++;
349 return (0);
350 }
351
352 /*
353 * See the big theory statement in i40e_main.c for multicast address management.
354 */
355 static int
356 i40e_multicast_remove(i40e_t *i40e, const uint8_t *multicast_address)
357 {
358 int i, ret;
359 struct i40e_hw *hw = &i40e->i40e_hw_space;
360
361 ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
362
363 for (i = 0; i < i40e->i40e_resources.ifr_nmcastfilt_used; i++) {
364 struct i40e_aqc_remove_macvlan_element_data filt;
365 int last;
366
367 if (bcmp(multicast_address, i40e->i40e_maddrs[i].ima_mac,
368 ETHERADDRL) != 0) {
369 continue;
370 }
371
372 bzero(&filt, sizeof (filt));
373 bcopy(multicast_address, filt.mac_addr, ETHERADDRL);
374 filt.flags = I40E_AQC_MACVLAN_DEL_HASH_MATCH |
375 I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
376
377 if (i40e_aq_remove_macvlan(hw, I40E_DEF_VSI_SEID(i40e), &filt,
378 1, NULL) != I40E_SUCCESS) {
379 i40e_error(i40e, "failed to remove mac address "
380 "%2x:%2x:%2x:%2x:%2x:%2x from multicast "
381 "filter: %d",
382 multicast_address[0], multicast_address[1],
383 multicast_address[2], multicast_address[3],
384 multicast_address[4], multicast_address[5],
385 filt.error_code);
386 return (EIO);
387 }
388
389 last = i40e->i40e_resources.ifr_nmcastfilt_used - 1;
390 if (i != last) {
391 bcopy(&i40e->i40e_maddrs[last], &i40e->i40e_maddrs[i],
392 sizeof (i40e_maddr_t));
393 bzero(&i40e->i40e_maddrs[last], sizeof (i40e_maddr_t));
394 }
395
396 ASSERT(i40e->i40e_resources.ifr_nmcastfilt_used > 0);
397 i40e->i40e_resources.ifr_nmcastfilt_used--;
398 return (0);
399 }
400
401 if (i40e->i40e_mcast_promisc_count > 0) {
402 if (i40e->i40e_mcast_promisc_count == 1 &&
403 i40e->i40e_promisc_on == B_FALSE) {
404 ret = i40e_aq_set_vsi_multicast_promiscuous(hw,
405 I40E_DEF_VSI_SEID(i40e), B_FALSE, NULL);
406 if (ret != I40E_SUCCESS) {
407 i40e_error(i40e, "failed to disable "
408 "multicast promiscuous mode on VSI %d: %d",
409 I40E_DEF_VSI_SEID(i40e), ret);
410 return (EIO);
411 }
412 }
413 i40e->i40e_mcast_promisc_count--;
414
415 return (0);
416 }
417
418 return (ENOENT);
419 }
420
421 static int
422 i40e_m_multicast(void *arg, boolean_t add, const uint8_t *multicast_address)
423 {
424 i40e_t *i40e = arg;
425 int rc;
426
427 mutex_enter(&i40e->i40e_general_lock);
428
429 if (i40e->i40e_state & I40E_SUSPENDED) {
430 mutex_exit(&i40e->i40e_general_lock);
431 return (ECANCELED);
432 }
433
434 if (add == B_TRUE) {
435 rc = i40e_multicast_add(i40e, multicast_address);
436 } else {
437 rc = i40e_multicast_remove(i40e, multicast_address);
438 }
439
440 mutex_exit(&i40e->i40e_general_lock);
441 return (rc);
442 }
443
444 /* ARGSUSED */
445 static void
446 i40e_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
447 {
448 /*
449 * At this time, we don't support toggling i40e into loopback mode. It's
450 * questionable how much value this has when there's no clear way to
451 * toggle this behavior from a supported way in userland.
452 */
453 miocnak(q, mp, 0, EINVAL);
454 }
455
456 static int
457 i40e_ring_start(mac_ring_driver_t rh, uint64_t gen_num)
458 {
459 i40e_trqpair_t *itrq = (i40e_trqpair_t *)rh;
460 int rv;
461
462 if ((rv = i40e_setup_ring(itrq)) != 0)
463 return (rv);
464
465 /*
466 * GLDv3 requires we keep track of a generation number, as it uses
467 * that number to keep track of whether or not a ring is active.
468 */
469 mutex_enter(&itrq->itrq_rx_lock);
470 itrq->itrq_rxgen = gen_num;
471 mutex_exit(&itrq->itrq_rx_lock);
472 return (0);
473 }
474
475 static void
476 i40e_ring_stop(mac_ring_driver_t rh)
477 {
478 i40e_trqpair_t *itrq = (i40e_trqpair_t *)rh;
479
480 if (!i40e_shutdown_ring(itrq)) {
481 i40e_t *i40e = itrq->itrq_i40e;
482
483 ddi_fm_service_impact(i40e->i40e_dip, DDI_SERVICE_LOST);
484 i40e_error(i40e, "Failed to stop ring %u", itrq->itrq_index);
485 }
486 }
487
488 /* ARGSUSED */
489 static int
490 i40e_rx_ring_intr_enable(mac_intr_handle_t intrh)
491 {
492 i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
493
494 mutex_enter(&itrq->itrq_rx_lock);
495 ASSERT(itrq->itrq_intr_poll == B_TRUE);
496 i40e_intr_rx_queue_enable(itrq);
497 itrq->itrq_intr_poll = B_FALSE;
498 mutex_exit(&itrq->itrq_rx_lock);
499
500 return (0);
501 }
502
503 /* ARGSUSED */
504 static int
505 i40e_rx_ring_intr_disable(mac_intr_handle_t intrh)
506 {
507 i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
508
509 mutex_enter(&itrq->itrq_rx_lock);
510 i40e_intr_rx_queue_disable(itrq);
511 itrq->itrq_intr_poll = B_TRUE;
512 mutex_exit(&itrq->itrq_rx_lock);
513
514 return (0);
515 }
516
517 /* ARGSUSED */
518 static void
519 i40e_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
520 const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
521 {
522 i40e_t *i40e = arg;
523 mac_intr_t *mintr = &infop->mri_intr;
524 i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
525
526 /*
527 * Note the group index here is expected to be -1 due to the fact that
528 * we're not actually grouping things tx-wise at this time.
529 */
530 ASSERT(group_index == -1);
531 ASSERT(ring_index < i40e->i40e_num_trqpairs_per_vsi);
532
533 itrq->itrq_mactxring = rh;
534 infop->mri_driver = (mac_ring_driver_t)itrq;
535 infop->mri_start = NULL;
536 infop->mri_stop = NULL;
537 infop->mri_tx = i40e_ring_tx;
538 infop->mri_stat = i40e_tx_ring_stat;
539
540 /*
541 * We only provide the handle in cases where we have MSI-X interrupts,
542 * to indicate that we'd actually support retargetting.
543 */
544 if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
545 mintr->mi_ddi_handle =
546 i40e->i40e_intr_handles[itrq->itrq_tx_intrvec];
547 }
548 }
549
550 /* ARGSUSED */
551 static void
552 i40e_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
553 const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
554 {
555 i40e_t *i40e = arg;
556 mac_intr_t *mintr = &infop->mri_intr;
557 uint_t trqpair_index;
558 i40e_trqpair_t *itrq;
559
560 /* This assumes static groups. */
561 ASSERT3S(group_index, >=, 0);
562 ASSERT3S(ring_index, >=, 0);
563 trqpair_index = (group_index * i40e->i40e_num_trqpairs_per_vsi) +
564 ring_index;
565 ASSERT3U(trqpair_index, <, i40e->i40e_num_trqpairs);
566 itrq = &i40e->i40e_trqpairs[trqpair_index];
567
568 itrq->itrq_macrxring = rh;
569 infop->mri_driver = (mac_ring_driver_t)itrq;
570 infop->mri_start = i40e_ring_start;
571 infop->mri_stop = i40e_ring_stop;
572 infop->mri_poll = i40e_ring_rx_poll;
573 infop->mri_stat = i40e_rx_ring_stat;
574 mintr->mi_handle = (mac_intr_handle_t)itrq;
575 mintr->mi_enable = i40e_rx_ring_intr_enable;
576 mintr->mi_disable = i40e_rx_ring_intr_disable;
577
578 /*
579 * We only provide the handle in cases where we have MSI-X interrupts,
580 * to indicate that we'd actually support retargetting.
581 */
582 if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
583 mintr->mi_ddi_handle =
584 i40e->i40e_intr_handles[itrq->itrq_rx_intrvec];
585 }
586 }
587
588 /* ARGSUSED */
589 static void
590 i40e_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
591 mac_group_info_t *infop, mac_group_handle_t gh)
592 {
593 i40e_t *i40e = arg;
594 i40e_rx_group_t *rxg;
595
596 if (rtype != MAC_RING_TYPE_RX)
597 return;
598
599 rxg = &i40e->i40e_rx_groups[index];
600 rxg->irg_grp_hdl = gh;
601
602 infop->mgi_driver = (mac_group_driver_t)rxg;
603 infop->mgi_start = NULL;
604 infop->mgi_stop = NULL;
605 infop->mgi_addmac = i40e_group_add_mac;
606 infop->mgi_remmac = i40e_group_remove_mac;
607
608 ASSERT3U(i40e->i40e_num_rx_groups, <=, I40E_MAX_NUM_RX_GROUPS);
609 infop->mgi_count = i40e->i40e_num_trqpairs_per_vsi;
610 }
611
612 static int
613 i40e_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
614 {
615 boolean_t present, usable;
616 i40e_t *i40e = arg;
617
618 if (id != 0 || infop == NULL)
619 return (EINVAL);
620
621 mutex_enter(&i40e->i40e_general_lock);
622 switch (i40e->i40e_hw_space.phy.link_info.module_type[0]) {
623 case I40E_MODULE_TYPE_SFP:
624 case I40E_MODULE_TYPE_QSFP:
625 break;
626 default:
627 mutex_exit(&i40e->i40e_general_lock);
628 return (ENOTSUP);
629 }
630
631 present = !!(i40e->i40e_hw_space.phy.link_info.link_info &
632 I40E_AQ_MEDIA_AVAILABLE);
633 if (present) {
634 usable = !!(i40e->i40e_hw_space.phy.link_info.an_info &
635 I40E_AQ_QUALIFIED_MODULE);
636 } else {
637 usable = B_FALSE;
638 }
639 mutex_exit(&i40e->i40e_general_lock);
640
641 mac_transceiver_info_set_usable(infop, usable);
642 mac_transceiver_info_set_present(infop, present);
643
644 return (0);
645 }
646
647 static int
648 i40e_transceiver_read(void *arg, uint_t id, uint_t page, void *buf,
649 size_t nbytes, off_t offset, size_t *nread)
650 {
651 i40e_t *i40e = arg;
652 struct i40e_hw *hw = &i40e->i40e_hw_space;
653 uint8_t *buf8 = buf;
654 size_t i;
655
656 if (id != 0 || buf == NULL || nbytes == 0 || nread == NULL ||
657 (page != 0xa0 && page != 0xa2) || offset < 0)
658 return (EINVAL);
659
660 /*
661 * Both supported pages have a length of 256 bytes, ensure nothing asks
662 * us to go beyond that.
663 */
664 if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
665 return (EINVAL);
666 }
667
668 mutex_enter(&i40e->i40e_general_lock);
669 switch (i40e->i40e_hw_space.phy.link_info.module_type[0]) {
670 case I40E_MODULE_TYPE_SFP:
671 case I40E_MODULE_TYPE_QSFP:
672 break;
673 default:
674 mutex_exit(&i40e->i40e_general_lock);
675 return (ENOTSUP);
676 }
677
678 /*
679 * Make sure we have a sufficiently new firmware version to run this
680 * command. This was introduced in firmware API 1.7. This is apparently
681 * only supported on the XL710 MAC, not the XL722.
682 */
683 if (hw->mac.type != I40E_MAC_XL710 || hw->aq.api_maj_ver != 1 ||
684 hw->aq.api_min_ver < 7) {
685 mutex_exit(&i40e->i40e_general_lock);
686 return (ENOTSUP);
687 }
688
689 for (i = 0; i < nbytes; i++, offset++) {
690 enum i40e_status_code status;
691 uint32_t val;
692
693 status = i40e_aq_get_phy_register(hw,
694 I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE, page, TRUE, offset,
695 &val, NULL);
696 if (status != I40E_SUCCESS) {
697 mutex_exit(&i40e->i40e_general_lock);
698 return (EIO);
699 }
700
701 buf8[i] = (uint8_t)val;
702 }
703
704 mutex_exit(&i40e->i40e_general_lock);
705 *nread = nbytes;
706
707 return (0);
708 }
709
710 static int
711 i40e_gld_led_set(void *arg, mac_led_mode_t mode, uint_t flags)
712 {
713 i40e_t *i40e = arg;
714 struct i40e_hw *hw = &i40e->i40e_hw_space;
715
716 if (flags != 0)
717 return (EINVAL);
718
719 if (mode != MAC_LED_DEFAULT &&
720 mode != MAC_LED_IDENT &&
721 mode != MAC_LED_OFF &&
722 mode != MAC_LED_ON)
723 return (ENOTSUP);
724
725 if (mode != MAC_LED_DEFAULT && !i40e->i40e_led_saved) {
726 i40e->i40e_led_status = i40e_led_get(hw);
727 i40e->i40e_led_saved = B_TRUE;
728 }
729
730 switch (mode) {
731 case MAC_LED_DEFAULT:
732 if (i40e->i40e_led_saved) {
733 i40e_led_set(hw, i40e->i40e_led_status, B_FALSE);
734 i40e->i40e_led_status = 0;
735 i40e->i40e_led_saved = B_FALSE;
736 }
737 break;
738 case MAC_LED_IDENT:
739 i40e_led_set(hw, 0xf, B_TRUE);
740 break;
741 case MAC_LED_OFF:
742 i40e_led_set(hw, 0x0, B_FALSE);
743 break;
744 case MAC_LED_ON:
745 i40e_led_set(hw, 0xf, B_FALSE);
746 break;
747 default:
748 return (ENOTSUP);
749 }
750
751 return (0);
752 }
753
754 static boolean_t
755 i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
756 {
757 i40e_t *i40e = arg;
758 mac_capab_rings_t *cap_rings;
759 mac_capab_transceiver_t *mct;
760 mac_capab_led_t *mcl;
761
762 switch (cap) {
763 case MAC_CAPAB_HCKSUM: {
764 uint32_t *txflags = cap_data;
765
766 *txflags = 0;
767 if (i40e->i40e_tx_hcksum_enable == B_TRUE)
768 *txflags = HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
769 break;
770 }
771
772 case MAC_CAPAB_LSO: {
773 mac_capab_lso_t *cap_lso = cap_data;
774
775 if (i40e->i40e_tx_lso_enable == B_TRUE) {
776 cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4 |
777 LSO_TX_BASIC_TCP_IPV6;
778 cap_lso->lso_basic_tcp_ipv4.lso_max = I40E_LSO_MAXLEN;
779 cap_lso->lso_basic_tcp_ipv6.lso_max = I40E_LSO_MAXLEN;
780 } else {
781 return (B_FALSE);
782 }
783 break;
784 }
785
786 case MAC_CAPAB_RINGS:
787 cap_rings = cap_data;
788 cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
789 switch (cap_rings->mr_type) {
790 case MAC_RING_TYPE_TX:
791 /*
792 * Note, saying we have no groups, but some
793 * number of rings indicates to MAC that it
794 * should create psuedo-groups with one for
795 * each TX ring. This may not be the long term
796 * behavior we want, but it'll work for now.
797 */
798 cap_rings->mr_gnum = 0;
799 cap_rings->mr_rnum = i40e->i40e_num_trqpairs_per_vsi;
800 cap_rings->mr_rget = i40e_fill_tx_ring;
801 cap_rings->mr_gget = NULL;
802 cap_rings->mr_gaddring = NULL;
803 cap_rings->mr_gremring = NULL;
804 break;
805 case MAC_RING_TYPE_RX:
806 cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
807 cap_rings->mr_rget = i40e_fill_rx_ring;
808 cap_rings->mr_gnum = i40e->i40e_num_rx_groups;
809 cap_rings->mr_gget = i40e_fill_rx_group;
810 cap_rings->mr_gaddring = NULL;
811 cap_rings->mr_gremring = NULL;
812 break;
813 default:
814 return (B_FALSE);
815 }
816 break;
817 case MAC_CAPAB_TRANSCEIVER:
818 mct = cap_data;
819
820 /*
821 * Firmware doesn't have a great way of telling us in advance
822 * whether we'd expect a SFF transceiver. As such, we always
823 * advertise the support for this capability.
824 */
825 mct->mct_flags = 0;
826 mct->mct_ntransceivers = 1;
827 mct->mct_info = i40e_transceiver_info;
828 mct->mct_read = i40e_transceiver_read;
829
830 return (B_TRUE);
831 case MAC_CAPAB_LED:
832 mcl = cap_data;
833
834 mcl->mcl_flags = 0;
835 mcl->mcl_modes = MAC_LED_DEFAULT | MAC_LED_IDENT | MAC_LED_OFF |
836 MAC_LED_ON;
837 mcl->mcl_set = i40e_gld_led_set;
838 break;
839
840 default:
841 return (B_FALSE);
842 }
843
844 return (B_TRUE);
845 }
846
847 /* ARGSUSED */
848 static int
849 i40e_m_setprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
850 const void *pr_val)
851 {
852 int ret;
853 long val;
854 char *eptr;
855
856 ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
857
858 if ((ret = ddi_strtol(pr_val, &eptr, 10, &val)) != 0 ||
859 *eptr != '\0') {
860 return (ret);
861 }
862
863 if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
864 if (val < I40E_MIN_RX_DMA_THRESH ||
865 val > I40E_MAX_RX_DMA_THRESH) {
866 return (EINVAL);
867 }
868 i40e->i40e_rx_dma_min = (uint32_t)val;
869 return (0);
870 }
871
872 if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
873 if (val < I40E_MIN_TX_DMA_THRESH ||
874 val > I40E_MAX_TX_DMA_THRESH) {
875 return (EINVAL);
876 }
877 i40e->i40e_tx_dma_min = (uint32_t)val;
878 return (0);
879 }
880
881 if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
882 if (val < I40E_MIN_ITR ||
883 val > I40E_MAX_ITR) {
884 return (EINVAL);
885 }
886 i40e->i40e_rx_itr = (uint32_t)val;
887 i40e_intr_set_itr(i40e, I40E_ITR_INDEX_RX, i40e->i40e_rx_itr);
888 return (0);
889 }
890
891 if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
892 if (val < I40E_MIN_ITR ||
893 val > I40E_MAX_ITR) {
894 return (EINVAL);
895 }
896 i40e->i40e_tx_itr = (uint32_t)val;
897 i40e_intr_set_itr(i40e, I40E_ITR_INDEX_TX, i40e->i40e_tx_itr);
898 return (0);
899 }
900
901 if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
902 if (val < I40E_MIN_ITR ||
903 val > I40E_MAX_ITR) {
904 return (EINVAL);
905 }
906 i40e->i40e_tx_itr = (uint32_t)val;
907 i40e_intr_set_itr(i40e, I40E_ITR_INDEX_OTHER,
908 i40e->i40e_other_itr);
909 return (0);
910 }
911
912 return (ENOTSUP);
913 }
914
915 static int
916 i40e_m_getprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
917 void *pr_val)
918 {
919 uint32_t val;
920
921 ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
922
923 if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
924 val = i40e->i40e_rx_dma_min;
925 } else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
926 val = i40e->i40e_tx_dma_min;
927 } else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
928 val = i40e->i40e_rx_itr;
929 } else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
930 val = i40e->i40e_tx_itr;
931 } else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
932 val = i40e->i40e_other_itr;
933 } else {
934 return (ENOTSUP);
935 }
936
937 if (snprintf(pr_val, pr_valsize, "%d", val) >= pr_valsize)
938 return (ERANGE);
939 return (0);
940 }
941
942 /*
943 * Annoyingly for private properties MAC seems to ignore default values that
944 * aren't strings. That means that we have to translate all of these into
945 * uint32_t's and instead we size the buffer to be large enough to hold a
946 * uint32_t.
947 */
948 /* ARGSUSED */
949 static void
950 i40e_m_propinfo_private(i40e_t *i40e, const char *pr_name,
951 mac_prop_info_handle_t prh)
952 {
953 char buf[64];
954 uint32_t def;
955
956 if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
957 mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
958 def = I40E_DEF_RX_DMA_THRESH;
959 mac_prop_info_set_range_uint32(prh,
960 I40E_MIN_RX_DMA_THRESH,
961 I40E_MAX_RX_DMA_THRESH);
962 } else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
963 mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
964 def = I40E_DEF_TX_DMA_THRESH;
965 mac_prop_info_set_range_uint32(prh,
966 I40E_MIN_TX_DMA_THRESH,
967 I40E_MAX_TX_DMA_THRESH);
968 } else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
969 mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
970 def = I40E_DEF_RX_ITR;
971 mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
972 } else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
973 mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
974 def = I40E_DEF_TX_ITR;
975 mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
976 } else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
977 mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
978 def = I40E_DEF_OTHER_ITR;
979 mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
980 } else {
981 return;
982 }
983
984 (void) snprintf(buf, sizeof (buf), "%d", def);
985 mac_prop_info_set_default_str(prh, buf);
986 }
987
988 static int
989 i40e_update_fec(i40e_t *i40e, link_fec_t fec)
990 {
991 struct i40e_hw *hw = &i40e->i40e_hw_space;
992 struct i40e_aq_get_phy_abilities_resp abilities;
993 struct i40e_aq_set_phy_config config;
994 link_fec_t fec_requested;
995 int req_fec;
996
997 ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
998
999 if (fec == i40e->i40e_fec_requested)
1000 return (0);
1001
1002 fec_requested = fec;
1003 if ((fec & LINK_FEC_AUTO) != 0) {
1004 req_fec = I40E_AQ_SET_FEC_AUTO;
1005 fec &= ~LINK_FEC_AUTO;
1006 } else if ((fec & LINK_FEC_NONE) != 0) {
1007 req_fec = 0;
1008 fec &= ~LINK_FEC_NONE;
1009 } else {
1010 req_fec = 0;
1011 if ((fec & LINK_FEC_BASE_R) != 0) {
1012 req_fec |= I40E_AQ_SET_FEC_ABILITY_KR |
1013 I40E_AQ_SET_FEC_REQUEST_KR;
1014 fec &= ~LINK_FEC_BASE_R;
1015 }
1016 if ((fec & LINK_FEC_RS) != 0) {
1017 req_fec |= I40E_AQ_SET_FEC_ABILITY_RS |
1018 I40E_AQ_SET_FEC_REQUEST_RS;
1019 fec &= ~LINK_FEC_RS;
1020 }
1021 if (req_fec == 0)
1022 return (EINVAL);
1023 }
1024
1025 /*
1026 * if fec is not zero now, then there is an invalid fec or
1027 * combination of settings.
1028 */
1029 if (fec != 0)
1030 return (EINVAL);
1031
1032 if (i40e_aq_get_phy_capabilities(hw, B_FALSE, B_FALSE, &abilities,
1033 NULL) != I40E_SUCCESS)
1034 return (EIO);
1035
1036 bzero(&config, sizeof (config));
1037 config.abilities = abilities.abilities;
1038 /* Restart the link */
1039 config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
1040 config.phy_type = abilities.phy_type;
1041 config.phy_type_ext = abilities.phy_type_ext;
1042 config.link_speed = abilities.link_speed;
1043 config.eee_capability = abilities.eee_capability;
1044 config.eeer = abilities.eeer_val;
1045 config.low_power_ctrl = abilities.d3_lpan;
1046 config.fec_config = req_fec & I40E_AQ_PHY_FEC_CONFIG_MASK;
1047 if (i40e_aq_set_phy_config(hw, &config, NULL) != I40E_SUCCESS)
1048 return (EIO);
1049
1050 if (i40e_update_link_info(hw) != I40E_SUCCESS)
1051 return (EIO);
1052
1053 i40e->i40e_fec_requested = fec_requested;
1054
1055 return (0);
1056 }
1057 static int
1058 i40e_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1059 uint_t pr_valsize, const void *pr_val)
1060 {
1061 uint32_t new_mtu;
1062 link_fec_t fec;
1063 i40e_t *i40e = arg;
1064 int ret = 0;
1065
1066 mutex_enter(&i40e->i40e_general_lock);
1067 if (i40e->i40e_state & I40E_SUSPENDED) {
1068 mutex_exit(&i40e->i40e_general_lock);
1069 return (ECANCELED);
1070 }
1071
1072 switch (pr_num) {
1073 /*
1074 * These properties are always read-only across every device.
1075 */
1076 case MAC_PROP_DUPLEX:
1077 case MAC_PROP_SPEED:
1078 case MAC_PROP_STATUS:
1079 case MAC_PROP_ADV_100FDX_CAP:
1080 case MAC_PROP_ADV_1000FDX_CAP:
1081 case MAC_PROP_ADV_2500FDX_CAP:
1082 case MAC_PROP_ADV_5000FDX_CAP:
1083 case MAC_PROP_ADV_10GFDX_CAP:
1084 case MAC_PROP_ADV_25GFDX_CAP:
1085 case MAC_PROP_ADV_40GFDX_CAP:
1086 ret = ENOTSUP;
1087 break;
1088 /*
1089 * These are read-only at this time as we don't support configuring
1090 * auto-negotiation. See the theory statement in i40e_main.c.
1091 */
1092 case MAC_PROP_EN_100FDX_CAP:
1093 case MAC_PROP_EN_1000FDX_CAP:
1094 case MAC_PROP_EN_2500FDX_CAP:
1095 case MAC_PROP_EN_5000FDX_CAP:
1096 case MAC_PROP_EN_10GFDX_CAP:
1097 case MAC_PROP_EN_25GFDX_CAP:
1098 case MAC_PROP_EN_40GFDX_CAP:
1099 case MAC_PROP_AUTONEG:
1100 case MAC_PROP_FLOWCTRL:
1101 ret = ENOTSUP;
1102 break;
1103
1104 case MAC_PROP_MTU:
1105 bcopy(pr_val, &new_mtu, sizeof (new_mtu));
1106 if (new_mtu == i40e->i40e_sdu)
1107 break;
1108
1109 if (new_mtu < I40E_MIN_MTU ||
1110 new_mtu > I40E_MAX_MTU) {
1111 ret = EINVAL;
1112 break;
1113 }
1114
1115 if (i40e->i40e_state & I40E_STARTED) {
1116 ret = EBUSY;
1117 break;
1118 }
1119
1120 ret = mac_maxsdu_update(i40e->i40e_mac_hdl, new_mtu);
1121 if (ret == 0) {
1122 i40e->i40e_sdu = new_mtu;
1123 i40e_update_mtu(i40e);
1124 }
1125 break;
1126
1127 case MAC_PROP_EN_FEC_CAP:
1128 bcopy(pr_val, &fec, sizeof (fec));
1129
1130 ret = i40e_update_fec(i40e, fec);
1131 break;
1132
1133 case MAC_PROP_PRIVATE:
1134 ret = i40e_m_setprop_private(i40e, pr_name, pr_valsize, pr_val);
1135 break;
1136 default:
1137 ret = ENOTSUP;
1138 break;
1139 }
1140
1141 mutex_exit(&i40e->i40e_general_lock);
1142 return (ret);
1143 }
1144
1145 static link_fec_t
1146 i40e_fec_to_linkfec(struct i40e_hw *hw)
1147 {
1148 struct i40e_link_status *ls = &hw->phy.link_info;
1149
1150 if ((ls->fec_info & I40E_AQ_CONFIG_FEC_KR_ENA) != 0)
1151 return (LINK_FEC_BASE_R);
1152
1153 if ((ls->fec_info & I40E_AQ_CONFIG_FEC_RS_ENA) != 0)
1154 return (LINK_FEC_RS);
1155
1156 return (LINK_FEC_NONE);
1157 }
1158
1159 static int
1160 i40e_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1161 uint_t pr_valsize, void *pr_val)
1162 {
1163 i40e_t *i40e = arg;
1164 uint64_t speed;
1165 int ret = 0;
1166 uint8_t *u8;
1167 link_flowctrl_t fctl;
1168
1169 mutex_enter(&i40e->i40e_general_lock);
1170
1171 switch (pr_num) {
1172 case MAC_PROP_DUPLEX:
1173 if (pr_valsize < sizeof (link_duplex_t)) {
1174 ret = EOVERFLOW;
1175 break;
1176 }
1177 bcopy(&i40e->i40e_link_duplex, pr_val, sizeof (link_duplex_t));
1178 break;
1179 case MAC_PROP_SPEED:
1180 if (pr_valsize < sizeof (uint64_t)) {
1181 ret = EOVERFLOW;
1182 break;
1183 }
1184 speed = i40e->i40e_link_speed * 1000000ULL;
1185 bcopy(&speed, pr_val, sizeof (speed));
1186 break;
1187 case MAC_PROP_STATUS:
1188 if (pr_valsize < sizeof (link_state_t)) {
1189 ret = EOVERFLOW;
1190 break;
1191 }
1192 bcopy(&i40e->i40e_link_state, pr_val, sizeof (link_state_t));
1193 break;
1194 case MAC_PROP_AUTONEG:
1195 if (pr_valsize < sizeof (uint8_t)) {
1196 ret = EOVERFLOW;
1197 break;
1198 }
1199 u8 = pr_val;
1200 *u8 = 1;
1201 break;
1202 case MAC_PROP_FLOWCTRL:
1203 /*
1204 * Because we don't currently support hardware flow control, we
1205 * just hardcode this to be none.
1206 */
1207 if (pr_valsize < sizeof (link_flowctrl_t)) {
1208 ret = EOVERFLOW;
1209 break;
1210 }
1211 fctl = LINK_FLOWCTRL_NONE;
1212 bcopy(&fctl, pr_val, sizeof (link_flowctrl_t));
1213 break;
1214 case MAC_PROP_MTU:
1215 if (pr_valsize < sizeof (uint32_t)) {
1216 ret = EOVERFLOW;
1217 break;
1218 }
1219 bcopy(&i40e->i40e_sdu, pr_val, sizeof (uint32_t));
1220 break;
1221 case MAC_PROP_ADV_FEC_CAP:
1222 if (pr_valsize < sizeof (link_fec_t)) {
1223 ret = EOVERFLOW;
1224 break;
1225 }
1226 *(link_fec_t *)pr_val =
1227 i40e_fec_to_linkfec(&i40e->i40e_hw_space);
1228 break;
1229 case MAC_PROP_EN_FEC_CAP:
1230 if (pr_valsize < sizeof (link_fec_t)) {
1231 ret = EOVERFLOW;
1232 break;
1233 }
1234 *(link_fec_t *)pr_val = i40e->i40e_fec_requested;
1235 break;
1236
1237 /*
1238 * Because we don't let users control the speeds we may auto-negotiate
1239 * to, the values of the ADV_ and EN_ will always be the same.
1240 */
1241 case MAC_PROP_ADV_100FDX_CAP:
1242 case MAC_PROP_EN_100FDX_CAP:
1243 if (pr_valsize < sizeof (uint8_t)) {
1244 ret = EOVERFLOW;
1245 break;
1246 }
1247 u8 = pr_val;
1248 *u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0;
1249 break;
1250 case MAC_PROP_ADV_1000FDX_CAP:
1251 case MAC_PROP_EN_1000FDX_CAP:
1252 if (pr_valsize < sizeof (uint8_t)) {
1253 ret = EOVERFLOW;
1254 break;
1255 }
1256 u8 = pr_val;
1257 *u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0;
1258 break;
1259 case MAC_PROP_ADV_2500FDX_CAP:
1260 case MAC_PROP_EN_2500FDX_CAP:
1261 if (pr_valsize < sizeof (uint8_t)) {
1262 ret = EOVERFLOW;
1263 break;
1264 }
1265 u8 = pr_val;
1266 *u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_2_5GB) != 0;
1267 break;
1268 case MAC_PROP_ADV_5000FDX_CAP:
1269 case MAC_PROP_EN_5000FDX_CAP:
1270 if (pr_valsize < sizeof (uint8_t)) {
1271 ret = EOVERFLOW;
1272 break;
1273 }
1274 u8 = pr_val;
1275 *u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_5GB) != 0;
1276 break;
1277 case MAC_PROP_ADV_10GFDX_CAP:
1278 case MAC_PROP_EN_10GFDX_CAP:
1279 if (pr_valsize < sizeof (uint8_t)) {
1280 ret = EOVERFLOW;
1281 break;
1282 }
1283 u8 = pr_val;
1284 *u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0;
1285 break;
1286 case MAC_PROP_ADV_25GFDX_CAP:
1287 case MAC_PROP_EN_25GFDX_CAP:
1288 if (pr_valsize < sizeof (uint8_t)) {
1289 ret = EOVERFLOW;
1290 break;
1291 }
1292 u8 = pr_val;
1293 *u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0;
1294 break;
1295 case MAC_PROP_ADV_40GFDX_CAP:
1296 case MAC_PROP_EN_40GFDX_CAP:
1297 if (pr_valsize < sizeof (uint8_t)) {
1298 ret = EOVERFLOW;
1299 break;
1300 }
1301 u8 = pr_val;
1302 *u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0;
1303 break;
1304 case MAC_PROP_PRIVATE:
1305 ret = i40e_m_getprop_private(i40e, pr_name, pr_valsize, pr_val);
1306 break;
1307 default:
1308 ret = ENOTSUP;
1309 break;
1310 }
1311
1312 mutex_exit(&i40e->i40e_general_lock);
1313
1314 return (ret);
1315 }
1316
1317 static void
1318 i40e_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1319 mac_prop_info_handle_t prh)
1320 {
1321 i40e_t *i40e = arg;
1322
1323 mutex_enter(&i40e->i40e_general_lock);
1324
1325 switch (pr_num) {
1326 case MAC_PROP_DUPLEX:
1327 case MAC_PROP_SPEED:
1328 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1329 break;
1330 case MAC_PROP_FLOWCTRL:
1331 /*
1332 * At the moment, the driver doesn't support flow control, hence
1333 * why this is set to read-only and none.
1334 */
1335 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1336 mac_prop_info_set_default_link_flowctrl(prh,
1337 LINK_FLOWCTRL_NONE);
1338 break;
1339 case MAC_PROP_MTU:
1340 mac_prop_info_set_range_uint32(prh, I40E_MIN_MTU, I40E_MAX_MTU);
1341 break;
1342 case MAC_PROP_ADV_FEC_CAP:
1343 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1344 if (i40e_is_25G_device(i40e->i40e_hw_space.device_id))
1345 mac_prop_info_set_default_fec(prh, LINK_FEC_AUTO);
1346 break;
1347 case MAC_PROP_EN_FEC_CAP:
1348 if (i40e_is_25G_device(i40e->i40e_hw_space.device_id)) {
1349 mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
1350 mac_prop_info_set_default_fec(prh, LINK_FEC_AUTO);
1351 } else {
1352 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1353 }
1354 break;
1355
1356 /*
1357 * We set the defaults for these based upon the phy's ability to
1358 * support the speeds. Note, auto-negotiation is required for fiber,
1359 * hence it is read-only and always enabled. When we have access to
1360 * copper phys we can revisit this.
1361 */
1362 case MAC_PROP_AUTONEG:
1363 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1364 mac_prop_info_set_default_uint8(prh, 1);
1365 break;
1366 case MAC_PROP_ADV_100FDX_CAP:
1367 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1368 mac_prop_info_set_default_uint8(prh,
1369 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1370 break;
1371 case MAC_PROP_EN_100FDX_CAP:
1372 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1373 mac_prop_info_set_default_uint8(prh,
1374 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1375 break;
1376 case MAC_PROP_ADV_1000FDX_CAP:
1377 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1378 mac_prop_info_set_default_uint8(prh,
1379 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1380 break;
1381 case MAC_PROP_EN_1000FDX_CAP:
1382 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1383 mac_prop_info_set_default_uint8(prh,
1384 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1385 break;
1386 case MAC_PROP_ADV_10GFDX_CAP:
1387 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1388 mac_prop_info_set_default_uint8(prh,
1389 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1390 break;
1391 case MAC_PROP_EN_10GFDX_CAP:
1392 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1393 mac_prop_info_set_default_uint8(prh,
1394 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1395 break;
1396 case MAC_PROP_ADV_25GFDX_CAP:
1397 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1398 mac_prop_info_set_default_uint8(prh,
1399 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0);
1400 break;
1401 case MAC_PROP_EN_25GFDX_CAP:
1402 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1403 mac_prop_info_set_default_uint8(prh,
1404 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0);
1405 break;
1406 case MAC_PROP_ADV_40GFDX_CAP:
1407 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1408 mac_prop_info_set_default_uint8(prh,
1409 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0);
1410 break;
1411 case MAC_PROP_EN_40GFDX_CAP:
1412 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1413 mac_prop_info_set_default_uint8(prh,
1414 (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0);
1415 break;
1416 case MAC_PROP_PRIVATE:
1417 i40e_m_propinfo_private(i40e, pr_name, prh);
1418 break;
1419 default:
1420 break;
1421 }
1422
1423 mutex_exit(&i40e->i40e_general_lock);
1424 }
1425
1426 #define I40E_M_CALLBACK_FLAGS \
1427 (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
1428
1429 static mac_callbacks_t i40e_m_callbacks = {
1430 I40E_M_CALLBACK_FLAGS,
1431 i40e_m_stat,
1432 i40e_m_start,
1433 i40e_m_stop,
1434 i40e_m_promisc,
1435 i40e_m_multicast,
1436 NULL,
1437 NULL,
1438 NULL,
1439 i40e_m_ioctl,
1440 i40e_m_getcapab,
1441 NULL,
1442 NULL,
1443 i40e_m_setprop,
1444 i40e_m_getprop,
1445 i40e_m_propinfo
1446 };
1447
1448 boolean_t
1449 i40e_register_mac(i40e_t *i40e)
1450 {
1451 struct i40e_hw *hw = &i40e->i40e_hw_space;
1452 int status;
1453 mac_register_t *mac = mac_alloc(MAC_VERSION);
1454
1455 if (mac == NULL)
1456 return (B_FALSE);
1457
1458 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
1459 mac->m_driver = i40e;
1460 mac->m_dip = i40e->i40e_dip;
1461 mac->m_src_addr = hw->mac.addr;
1462 mac->m_callbacks = &i40e_m_callbacks;
1463 mac->m_min_sdu = 0;
1464 mac->m_max_sdu = i40e->i40e_sdu;
1465 mac->m_margin = VLAN_TAGSZ;
1466 mac->m_priv_props = i40e_priv_props;
1467 mac->m_v12n = MAC_VIRT_LEVEL1;
1468
1469 status = mac_register(mac, &i40e->i40e_mac_hdl);
1470 if (status != 0)
1471 i40e_error(i40e, "mac_register() returned %d", status);
1472 mac_free(mac);
1473
1474 return (status == 0);
1475 }