xref: /illumos-gate/usr/src/uts/common/io/ixgbe/ixgbe_gld.c (revision bf002425f517afdc1d8b6a9602e59910eeee05aa)
1 /*
2  * CDDL HEADER START
3  *
4  * Copyright(c) 2007-2009 Intel Corporation. All rights reserved.
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include "ixgbe_sw.h"
29 
30 /*
31  * Bring the device out of the reset/quiesced state that it
32  * was in when the interface was registered.
33  */
34 int
35 ixgbe_m_start(void *arg)
36 {
37 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
38 
39 	mutex_enter(&ixgbe->gen_lock);
40 
41 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
42 		mutex_exit(&ixgbe->gen_lock);
43 		return (ECANCELED);
44 	}
45 
46 	if (ixgbe_start(ixgbe, B_TRUE) != IXGBE_SUCCESS) {
47 		mutex_exit(&ixgbe->gen_lock);
48 		return (EIO);
49 	}
50 
51 	atomic_or_32(&ixgbe->ixgbe_state, IXGBE_STARTED);
52 
53 	mutex_exit(&ixgbe->gen_lock);
54 
55 	/*
56 	 * Enable and start the watchdog timer
57 	 */
58 	ixgbe_enable_watchdog_timer(ixgbe);
59 
60 	return (0);
61 }
62 
63 /*
64  * Stop the device and put it in a reset/quiesced state such
65  * that the interface can be unregistered.
66  */
67 void
68 ixgbe_m_stop(void *arg)
69 {
70 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
71 
72 	mutex_enter(&ixgbe->gen_lock);
73 
74 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
75 		mutex_exit(&ixgbe->gen_lock);
76 		return;
77 	}
78 
79 	atomic_and_32(&ixgbe->ixgbe_state, ~IXGBE_STARTED);
80 
81 	ixgbe_stop(ixgbe, B_TRUE);
82 
83 	mutex_exit(&ixgbe->gen_lock);
84 
85 	/*
86 	 * Disable and stop the watchdog timer
87 	 */
88 	ixgbe_disable_watchdog_timer(ixgbe);
89 }
90 
91 /*
92  * Set the promiscuity of the device.
93  */
94 int
95 ixgbe_m_promisc(void *arg, boolean_t on)
96 {
97 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
98 	uint32_t reg_val;
99 	struct ixgbe_hw *hw = &ixgbe->hw;
100 
101 	mutex_enter(&ixgbe->gen_lock);
102 
103 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
104 		mutex_exit(&ixgbe->gen_lock);
105 		return (ECANCELED);
106 	}
107 	reg_val = IXGBE_READ_REG(hw, IXGBE_FCTRL);
108 
109 	if (on)
110 		reg_val |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
111 	else
112 		reg_val &= (~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE));
113 
114 	IXGBE_WRITE_REG(&ixgbe->hw, IXGBE_FCTRL, reg_val);
115 
116 	mutex_exit(&ixgbe->gen_lock);
117 
118 	return (0);
119 }
120 
121 /*
122  * Add/remove the addresses to/from the set of multicast
123  * addresses for which the device will receive packets.
124  */
125 int
126 ixgbe_m_multicst(void *arg, boolean_t add, const uint8_t *mcst_addr)
127 {
128 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
129 	int result;
130 
131 	mutex_enter(&ixgbe->gen_lock);
132 
133 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
134 		mutex_exit(&ixgbe->gen_lock);
135 		return (ECANCELED);
136 	}
137 
138 	result = (add) ? ixgbe_multicst_add(ixgbe, mcst_addr)
139 	    : ixgbe_multicst_remove(ixgbe, mcst_addr);
140 
141 	mutex_exit(&ixgbe->gen_lock);
142 
143 	return (result);
144 }
145 
146 /*
147  * Pass on M_IOCTL messages passed to the DLD, and support
148  * private IOCTLs for debugging and ndd.
149  */
150 void
151 ixgbe_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
152 {
153 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
154 	struct iocblk *iocp;
155 	enum ioc_reply status;
156 
157 	iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
158 	iocp->ioc_error = 0;
159 
160 	mutex_enter(&ixgbe->gen_lock);
161 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
162 		mutex_exit(&ixgbe->gen_lock);
163 		miocnak(q, mp, 0, EINVAL);
164 		return;
165 	}
166 	mutex_exit(&ixgbe->gen_lock);
167 
168 	switch (iocp->ioc_cmd) {
169 	case LB_GET_INFO_SIZE:
170 	case LB_GET_INFO:
171 	case LB_GET_MODE:
172 	case LB_SET_MODE:
173 		status = ixgbe_loopback_ioctl(ixgbe, iocp, mp);
174 		break;
175 
176 	default:
177 		status = IOC_INVAL;
178 		break;
179 	}
180 
181 	/*
182 	 * Decide how to reply
183 	 */
184 	switch (status) {
185 	default:
186 	case IOC_INVAL:
187 		/*
188 		 * Error, reply with a NAK and EINVAL or the specified error
189 		 */
190 		miocnak(q, mp, 0, iocp->ioc_error == 0 ?
191 		    EINVAL : iocp->ioc_error);
192 		break;
193 
194 	case IOC_DONE:
195 		/*
196 		 * OK, reply already sent
197 		 */
198 		break;
199 
200 	case IOC_ACK:
201 		/*
202 		 * OK, reply with an ACK
203 		 */
204 		miocack(q, mp, 0, 0);
205 		break;
206 
207 	case IOC_REPLY:
208 		/*
209 		 * OK, send prepared reply as ACK or NAK
210 		 */
211 		mp->b_datap->db_type = iocp->ioc_error == 0 ?
212 		    M_IOCACK : M_IOCNAK;
213 		qreply(q, mp);
214 		break;
215 	}
216 }
217 
218 /*
219  * Obtain the MAC's capabilities and associated data from
220  * the driver.
221  */
222 boolean_t
223 ixgbe_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
224 {
225 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
226 
227 	switch (cap) {
228 	case MAC_CAPAB_HCKSUM: {
229 		uint32_t *tx_hcksum_flags = cap_data;
230 
231 		/*
232 		 * We advertise our capabilities only if tx hcksum offload is
233 		 * enabled.  On receive, the stack will accept checksummed
234 		 * packets anyway, even if we haven't said we can deliver
235 		 * them.
236 		 */
237 		if (!ixgbe->tx_hcksum_enable)
238 			return (B_FALSE);
239 
240 		*tx_hcksum_flags = HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
241 		break;
242 	}
243 	case MAC_CAPAB_LSO: {
244 		mac_capab_lso_t *cap_lso = cap_data;
245 
246 		if (ixgbe->lso_enable) {
247 			cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
248 			cap_lso->lso_basic_tcp_ipv4.lso_max = IXGBE_LSO_MAXLEN;
249 			break;
250 		} else {
251 			return (B_FALSE);
252 		}
253 	}
254 	case MAC_CAPAB_RINGS: {
255 		mac_capab_rings_t *cap_rings = cap_data;
256 
257 		switch (cap_rings->mr_type) {
258 		case MAC_RING_TYPE_RX:
259 			cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
260 			cap_rings->mr_rnum = ixgbe->num_rx_rings;
261 			cap_rings->mr_gnum = ixgbe->num_rx_groups;
262 			cap_rings->mr_rget = ixgbe_fill_ring;
263 			cap_rings->mr_gget = ixgbe_fill_group;
264 			cap_rings->mr_gaddring = NULL;
265 			cap_rings->mr_gremring = NULL;
266 			break;
267 		case MAC_RING_TYPE_TX:
268 			cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
269 			cap_rings->mr_rnum = ixgbe->num_tx_rings;
270 			cap_rings->mr_gnum = 0;
271 			cap_rings->mr_rget = ixgbe_fill_ring;
272 			cap_rings->mr_gget = NULL;
273 			break;
274 		default:
275 			break;
276 		}
277 		break;
278 	}
279 	default:
280 		return (B_FALSE);
281 	}
282 	return (B_TRUE);
283 }
284 
285 int
286 ixgbe_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
287     uint_t pr_valsize, const void *pr_val)
288 {
289 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
290 	struct ixgbe_hw *hw = &ixgbe->hw;
291 	int err = 0;
292 	uint32_t flow_control;
293 	uint32_t cur_mtu, new_mtu;
294 	uint32_t rx_size;
295 	uint32_t tx_size;
296 
297 	mutex_enter(&ixgbe->gen_lock);
298 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
299 		mutex_exit(&ixgbe->gen_lock);
300 		return (ECANCELED);
301 	}
302 
303 	if (ixgbe->loopback_mode != IXGBE_LB_NONE &&
304 	    ixgbe_param_locked(pr_num)) {
305 		/*
306 		 * All en_* parameters are locked (read-only)
307 		 * while the device is in any sort of loopback mode.
308 		 */
309 		mutex_exit(&ixgbe->gen_lock);
310 		return (EBUSY);
311 	}
312 
313 	switch (pr_num) {
314 	case MAC_PROP_EN_10GFDX_CAP:
315 		/* read/write on copper, read-only on serdes */
316 		if (ixgbe->hw.phy.media_type != ixgbe_media_type_copper) {
317 			err = ENOTSUP;
318 			break;
319 		} else {
320 			ixgbe->param_en_10000fdx_cap = *(uint8_t *)pr_val;
321 			ixgbe->param_adv_10000fdx_cap = *(uint8_t *)pr_val;
322 			goto setup_link;
323 		}
324 	case MAC_PROP_EN_1000FDX_CAP:
325 		/* read/write on copper, read-only on serdes */
326 		if (ixgbe->hw.phy.media_type != ixgbe_media_type_copper) {
327 			err = ENOTSUP;
328 			break;
329 		} else {
330 			ixgbe->param_en_1000fdx_cap = *(uint8_t *)pr_val;
331 			ixgbe->param_adv_1000fdx_cap = *(uint8_t *)pr_val;
332 			goto setup_link;
333 		}
334 	case MAC_PROP_EN_100FDX_CAP:
335 		/* read/write on copper, read-only on serdes */
336 		if (ixgbe->hw.phy.media_type != ixgbe_media_type_copper) {
337 			err = ENOTSUP;
338 			break;
339 		} else {
340 			ixgbe->param_en_100fdx_cap = *(uint8_t *)pr_val;
341 			ixgbe->param_adv_100fdx_cap = *(uint8_t *)pr_val;
342 			goto setup_link;
343 		}
344 	case MAC_PROP_AUTONEG:
345 		/* read/write on copper, read-only on serdes */
346 		if (ixgbe->hw.phy.media_type != ixgbe_media_type_copper) {
347 			err = ENOTSUP;
348 			break;
349 		} else {
350 			ixgbe->param_adv_autoneg_cap = *(uint8_t *)pr_val;
351 			goto setup_link;
352 		}
353 	case MAC_PROP_FLOWCTRL:
354 		bcopy(pr_val, &flow_control, sizeof (flow_control));
355 
356 		switch (flow_control) {
357 		default:
358 			err = EINVAL;
359 			break;
360 		case LINK_FLOWCTRL_NONE:
361 			hw->fc.requested_mode = ixgbe_fc_none;
362 			break;
363 		case LINK_FLOWCTRL_RX:
364 			hw->fc.requested_mode = ixgbe_fc_rx_pause;
365 			break;
366 		case LINK_FLOWCTRL_TX:
367 			hw->fc.requested_mode = ixgbe_fc_tx_pause;
368 			break;
369 		case LINK_FLOWCTRL_BI:
370 			hw->fc.requested_mode = ixgbe_fc_full;
371 			break;
372 		}
373 setup_link:
374 		if (err == 0) {
375 			if (ixgbe_driver_setup_link(ixgbe, B_TRUE) !=
376 			    IXGBE_SUCCESS)
377 				err = EINVAL;
378 		}
379 		break;
380 	case MAC_PROP_ADV_10GFDX_CAP:
381 	case MAC_PROP_ADV_1000FDX_CAP:
382 	case MAC_PROP_ADV_100FDX_CAP:
383 	case MAC_PROP_STATUS:
384 	case MAC_PROP_SPEED:
385 	case MAC_PROP_DUPLEX:
386 		err = ENOTSUP; /* read-only prop. Can't set this. */
387 		break;
388 	case MAC_PROP_MTU:
389 		cur_mtu = ixgbe->default_mtu;
390 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
391 		if (new_mtu == cur_mtu) {
392 			err = 0;
393 			break;
394 		}
395 
396 		if (new_mtu < DEFAULT_MTU || new_mtu > ixgbe->capab->max_mtu) {
397 			err = EINVAL;
398 			break;
399 		}
400 
401 		if (ixgbe->ixgbe_state & IXGBE_STARTED) {
402 			err = EBUSY;
403 			break;
404 		}
405 
406 		err = mac_maxsdu_update(ixgbe->mac_hdl, new_mtu);
407 		if (err == 0) {
408 			ixgbe->default_mtu = new_mtu;
409 			ixgbe->max_frame_size = ixgbe->default_mtu +
410 			    sizeof (struct ether_vlan_header) + ETHERFCSL;
411 
412 			/*
413 			 * Set rx buffer size
414 			 */
415 			rx_size = ixgbe->max_frame_size + IPHDR_ALIGN_ROOM;
416 			ixgbe->rx_buf_size = ((rx_size >> 10) + ((rx_size &
417 			    (((uint32_t)1 << 10) - 1)) > 0 ? 1 : 0)) << 10;
418 
419 			/*
420 			 * Set tx buffer size
421 			 */
422 			tx_size = ixgbe->max_frame_size;
423 			ixgbe->tx_buf_size = ((tx_size >> 10) + ((tx_size &
424 			    (((uint32_t)1 << 10) - 1)) > 0 ? 1 : 0)) << 10;
425 		}
426 		break;
427 	case MAC_PROP_PRIVATE:
428 		err = ixgbe_set_priv_prop(ixgbe, pr_name, pr_valsize, pr_val);
429 		break;
430 	default:
431 		err = EINVAL;
432 		break;
433 	}
434 	mutex_exit(&ixgbe->gen_lock);
435 	return (err);
436 }
437 
438 int
439 ixgbe_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
440     uint_t pr_valsize, void *pr_val)
441 {
442 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
443 	struct ixgbe_hw *hw = &ixgbe->hw;
444 	int err = 0;
445 	uint32_t flow_control;
446 	uint64_t tmp = 0;
447 
448 	switch (pr_num) {
449 	case MAC_PROP_DUPLEX:
450 		ASSERT(pr_valsize >= sizeof (link_duplex_t));
451 		bcopy(&ixgbe->link_duplex, pr_val,
452 		    sizeof (link_duplex_t));
453 		break;
454 	case MAC_PROP_SPEED:
455 		ASSERT(pr_valsize >= sizeof (uint64_t));
456 		tmp = ixgbe->link_speed * 1000000ull;
457 		bcopy(&tmp, pr_val, sizeof (tmp));
458 		break;
459 	case MAC_PROP_AUTONEG:
460 		*(uint8_t *)pr_val = ixgbe->param_adv_autoneg_cap;
461 		break;
462 	case MAC_PROP_FLOWCTRL:
463 		ASSERT(pr_valsize >= sizeof (uint32_t));
464 
465 		switch (hw->fc.requested_mode) {
466 			case ixgbe_fc_none:
467 				flow_control = LINK_FLOWCTRL_NONE;
468 				break;
469 			case ixgbe_fc_rx_pause:
470 				flow_control = LINK_FLOWCTRL_RX;
471 				break;
472 			case ixgbe_fc_tx_pause:
473 				flow_control = LINK_FLOWCTRL_TX;
474 				break;
475 			case ixgbe_fc_full:
476 				flow_control = LINK_FLOWCTRL_BI;
477 				break;
478 		}
479 		bcopy(&flow_control, pr_val, sizeof (flow_control));
480 		break;
481 	case MAC_PROP_ADV_10GFDX_CAP:
482 		*(uint8_t *)pr_val = ixgbe->param_adv_10000fdx_cap;
483 		break;
484 	case MAC_PROP_EN_10GFDX_CAP:
485 		*(uint8_t *)pr_val = ixgbe->param_en_10000fdx_cap;
486 		break;
487 	case MAC_PROP_ADV_1000FDX_CAP:
488 		*(uint8_t *)pr_val = ixgbe->param_adv_1000fdx_cap;
489 		break;
490 	case MAC_PROP_EN_1000FDX_CAP:
491 		*(uint8_t *)pr_val = ixgbe->param_en_1000fdx_cap;
492 		break;
493 	case MAC_PROP_ADV_100FDX_CAP:
494 		*(uint8_t *)pr_val = ixgbe->param_adv_100fdx_cap;
495 		break;
496 	case MAC_PROP_EN_100FDX_CAP:
497 		*(uint8_t *)pr_val = ixgbe->param_en_100fdx_cap;
498 		break;
499 	case MAC_PROP_PRIVATE:
500 		err = ixgbe_get_priv_prop(ixgbe, pr_name,
501 		    pr_valsize, pr_val);
502 		break;
503 	default:
504 		err = EINVAL;
505 		break;
506 	}
507 	return (err);
508 }
509 
510 void
511 ixgbe_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
512     mac_prop_info_handle_t prh)
513 {
514 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
515 	uint_t perm;
516 
517 	switch (pr_num) {
518 	case MAC_PROP_DUPLEX:
519 	case MAC_PROP_SPEED:
520 	case MAC_PROP_ADV_100FDX_CAP:
521 	case MAC_PROP_ADV_1000FDX_CAP:
522 	case MAC_PROP_ADV_10GFDX_CAP:
523 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
524 		break;
525 
526 	case MAC_PROP_AUTONEG:
527 	case MAC_PROP_EN_10GFDX_CAP:
528 	case MAC_PROP_EN_1000FDX_CAP:
529 	case MAC_PROP_EN_100FDX_CAP:
530 		perm = (ixgbe->hw.phy.media_type == ixgbe_media_type_copper) ?
531 		    MAC_PROP_PERM_RW : MAC_PROP_PERM_READ;
532 		if (perm == MAC_PROP_PERM_RW)
533 			mac_prop_info_set_default_uint8(prh, 1);
534 		mac_prop_info_set_perm(prh, perm);
535 		break;
536 
537 	case MAC_PROP_FLOWCTRL:
538 		mac_prop_info_set_default_link_flowctrl(prh,
539 		    LINK_FLOWCTRL_NONE);
540 		break;
541 
542 	case MAC_PROP_MTU:
543 		mac_prop_info_set_range_uint32(prh,
544 		    DEFAULT_MTU, ixgbe->capab->max_mtu);
545 		break;
546 
547 	case MAC_PROP_PRIVATE: {
548 		char valstr[64];
549 		int value;
550 
551 		bzero(valstr, sizeof (valstr));
552 
553 		if (strcmp(pr_name, "_adv_pause_cap") == 0 ||
554 		    strcmp(pr_name, "_adv_asym_pause_cap") == 0) {
555 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
556 			return;
557 		}
558 
559 		if (strcmp(pr_name, "_tx_copy_thresh") == 0) {
560 			value = DEFAULT_TX_COPY_THRESHOLD;
561 		} else if (strcmp(pr_name, "_tx_recycle_thresh") == 0) {
562 			value = DEFAULT_TX_RECYCLE_THRESHOLD;
563 		} else if (strcmp(pr_name, "_tx_overload_thresh") == 0) {
564 			value = DEFAULT_TX_OVERLOAD_THRESHOLD;
565 		} else if (strcmp(pr_name, "_tx_resched_thresh") == 0) {
566 			value = DEFAULT_TX_RESCHED_THRESHOLD;
567 		} else 	if (strcmp(pr_name, "_rx_copy_thresh") == 0) {
568 			value = DEFAULT_RX_COPY_THRESHOLD;
569 		} else 	if (strcmp(pr_name, "_rx_limit_per_intr") == 0) {
570 			value = DEFAULT_RX_LIMIT_PER_INTR;
571 		} 	if (strcmp(pr_name, "_intr_throttling") == 0) {
572 			value = ixgbe->capab->def_intr_throttle;
573 		} else {
574 			return;
575 		}
576 
577 		(void) snprintf(valstr, sizeof (valstr), "%x", value);
578 	}
579 	}
580 }
581 
582 boolean_t
583 ixgbe_param_locked(mac_prop_id_t pr_num)
584 {
585 	/*
586 	 * All en_* parameters are locked (read-only) while
587 	 * the device is in any sort of loopback mode ...
588 	 */
589 	switch (pr_num) {
590 		case MAC_PROP_EN_10GFDX_CAP:
591 		case MAC_PROP_EN_1000FDX_CAP:
592 		case MAC_PROP_EN_100FDX_CAP:
593 		case MAC_PROP_AUTONEG:
594 		case MAC_PROP_FLOWCTRL:
595 			return (B_TRUE);
596 	}
597 	return (B_FALSE);
598 }
599 
600 /* ARGSUSED */
601 int
602 ixgbe_set_priv_prop(ixgbe_t *ixgbe, const char *pr_name,
603     uint_t pr_valsize, const void *pr_val)
604 {
605 	int err = 0;
606 	long result;
607 	struct ixgbe_hw *hw = &ixgbe->hw;
608 	int i;
609 
610 	if (strcmp(pr_name, "_tx_copy_thresh") == 0) {
611 		if (pr_val == NULL) {
612 			err = EINVAL;
613 			return (err);
614 		}
615 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
616 		if (result < MIN_TX_COPY_THRESHOLD ||
617 		    result > MAX_TX_COPY_THRESHOLD)
618 			err = EINVAL;
619 		else {
620 			ixgbe->tx_copy_thresh = (uint32_t)result;
621 		}
622 		return (err);
623 	}
624 	if (strcmp(pr_name, "_tx_recycle_thresh") == 0) {
625 		if (pr_val == NULL) {
626 			err = EINVAL;
627 			return (err);
628 		}
629 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
630 		if (result < MIN_TX_RECYCLE_THRESHOLD ||
631 		    result > MAX_TX_RECYCLE_THRESHOLD)
632 			err = EINVAL;
633 		else {
634 			ixgbe->tx_recycle_thresh = (uint32_t)result;
635 		}
636 		return (err);
637 	}
638 	if (strcmp(pr_name, "_tx_overload_thresh") == 0) {
639 		if (pr_val == NULL) {
640 			err = EINVAL;
641 			return (err);
642 		}
643 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
644 		if (result < MIN_TX_OVERLOAD_THRESHOLD ||
645 		    result > MAX_TX_OVERLOAD_THRESHOLD)
646 			err = EINVAL;
647 		else {
648 			ixgbe->tx_overload_thresh = (uint32_t)result;
649 		}
650 		return (err);
651 	}
652 	if (strcmp(pr_name, "_tx_resched_thresh") == 0) {
653 		if (pr_val == NULL) {
654 			err = EINVAL;
655 			return (err);
656 		}
657 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
658 		if (result < MIN_TX_RESCHED_THRESHOLD ||
659 		    result > MAX_TX_RESCHED_THRESHOLD)
660 			err = EINVAL;
661 		else {
662 			ixgbe->tx_resched_thresh = (uint32_t)result;
663 		}
664 		return (err);
665 	}
666 	if (strcmp(pr_name, "_rx_copy_thresh") == 0) {
667 		if (pr_val == NULL) {
668 			err = EINVAL;
669 			return (err);
670 		}
671 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
672 		if (result < MIN_RX_COPY_THRESHOLD ||
673 		    result > MAX_RX_COPY_THRESHOLD)
674 			err = EINVAL;
675 		else {
676 			ixgbe->rx_copy_thresh = (uint32_t)result;
677 		}
678 		return (err);
679 	}
680 	if (strcmp(pr_name, "_rx_limit_per_intr") == 0) {
681 		if (pr_val == NULL) {
682 			err = EINVAL;
683 			return (err);
684 		}
685 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
686 		if (result < MIN_RX_LIMIT_PER_INTR ||
687 		    result > MAX_RX_LIMIT_PER_INTR)
688 			err = EINVAL;
689 		else {
690 			ixgbe->rx_limit_per_intr = (uint32_t)result;
691 		}
692 		return (err);
693 	}
694 	if (strcmp(pr_name, "_intr_throttling") == 0) {
695 		if (pr_val == NULL) {
696 			err = EINVAL;
697 			return (err);
698 		}
699 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
700 
701 		if (result < ixgbe->capab->min_intr_throttle ||
702 		    result > ixgbe->capab->max_intr_throttle)
703 			err = EINVAL;
704 		else {
705 			ixgbe->intr_throttling[0] = (uint32_t)result;
706 
707 			/*
708 			 * 82599 requires the interupt throttling rate is
709 			 * a multiple of 8. This is enforced by the register
710 			 * definiton.
711 			 */
712 			if (hw->mac.type == ixgbe_mac_82599EB)
713 				ixgbe->intr_throttling[0] =
714 				    ixgbe->intr_throttling[0] & 0xFF8;
715 
716 			for (i = 0; i < MAX_INTR_VECTOR; i++)
717 				ixgbe->intr_throttling[i] =
718 				    ixgbe->intr_throttling[0];
719 
720 			/* Set interrupt throttling rate */
721 			for (i = 0; i < ixgbe->intr_cnt; i++)
722 				IXGBE_WRITE_REG(hw, IXGBE_EITR(i),
723 				    ixgbe->intr_throttling[i]);
724 		}
725 		return (err);
726 	}
727 	return (ENOTSUP);
728 }
729 
730 int
731 ixgbe_get_priv_prop(ixgbe_t *ixgbe, const char *pr_name,
732     uint_t pr_valsize, void *pr_val)
733 {
734 	int err = ENOTSUP;
735 	int value;
736 
737 	if (strcmp(pr_name, "_adv_pause_cap") == 0) {
738 		value = ixgbe->param_adv_pause_cap;
739 		err = 0;
740 		goto done;
741 	}
742 	if (strcmp(pr_name, "_adv_asym_pause_cap") == 0) {
743 		value = ixgbe->param_adv_asym_pause_cap;
744 		err = 0;
745 		goto done;
746 	}
747 	if (strcmp(pr_name, "_tx_copy_thresh") == 0) {
748 		value = ixgbe->tx_copy_thresh;
749 		err = 0;
750 		goto done;
751 	}
752 	if (strcmp(pr_name, "_tx_recycle_thresh") == 0) {
753 		value = ixgbe->tx_recycle_thresh;
754 		err = 0;
755 		goto done;
756 	}
757 	if (strcmp(pr_name, "_tx_overload_thresh") == 0) {
758 		value = ixgbe->tx_overload_thresh;
759 		err = 0;
760 		goto done;
761 	}
762 	if (strcmp(pr_name, "_tx_resched_thresh") == 0) {
763 		value = ixgbe->tx_resched_thresh;
764 		err = 0;
765 		goto done;
766 	}
767 	if (strcmp(pr_name, "_rx_copy_thresh") == 0) {
768 		value = ixgbe->rx_copy_thresh;
769 		err = 0;
770 		goto done;
771 	}
772 	if (strcmp(pr_name, "_rx_limit_per_intr") == 0) {
773 		value = ixgbe->rx_limit_per_intr;
774 		err = 0;
775 		goto done;
776 	}
777 	if (strcmp(pr_name, "_intr_throttling") == 0) {
778 		value = ixgbe->intr_throttling[0];
779 		err = 0;
780 		goto done;
781 	}
782 done:
783 	if (err == 0) {
784 		(void) snprintf(pr_val, pr_valsize, "%d", value);
785 	}
786 	return (err);
787 }
788