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