145d3dd98SRobert Mustacchi /*
245d3dd98SRobert Mustacchi  * This file and its contents are supplied under the terms of the
345d3dd98SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
445d3dd98SRobert Mustacchi  * You may only use this file in accordance with the terms of version
545d3dd98SRobert Mustacchi  * 1.0 of the CDDL.
645d3dd98SRobert Mustacchi  *
745d3dd98SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
845d3dd98SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
945d3dd98SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
1045d3dd98SRobert Mustacchi  */
1145d3dd98SRobert Mustacchi 
1245d3dd98SRobert Mustacchi /*
1345d3dd98SRobert Mustacchi  * Copyright (c) 2017, Joyent, Inc.
14*633c5e5cSRobert Mustacchi  * Copyright 2023 Oxide Computer Company
1545d3dd98SRobert Mustacchi  */
1645d3dd98SRobert Mustacchi 
1745d3dd98SRobert Mustacchi /*
1845d3dd98SRobert Mustacchi  * Routines to get access to the phy and transceiver that require routines and
1945d3dd98SRobert Mustacchi  * definitions that aren't part of the common ixgbe API.
2045d3dd98SRobert Mustacchi  */
2145d3dd98SRobert Mustacchi 
2245d3dd98SRobert Mustacchi #include "ixgbe_sw.h"
2345d3dd98SRobert Mustacchi #include "ixgbe_phy.h"
2445d3dd98SRobert Mustacchi 
25*633c5e5cSRobert Mustacchi /*
26*633c5e5cSRobert Mustacchi  * This is a table that maps various link types, speeds, and physical media
27*633c5e5cSRobert Mustacchi  * types together to something that can be used. We prefer to use the supported
28*633c5e5cSRobert Mustacchi  * physical layer types so we can attempt to abstract around the various PHY and
29*633c5e5cSRobert Mustacchi  * media types and try to create a single coherent place for these.
30*633c5e5cSRobert Mustacchi  */
31*633c5e5cSRobert Mustacchi typedef struct {
32*633c5e5cSRobert Mustacchi 	uint64_t ipm_phys;
33*633c5e5cSRobert Mustacchi 	uint32_t ipm_speed;
34*633c5e5cSRobert Mustacchi 	mac_ether_media_t ipm_media;
35*633c5e5cSRobert Mustacchi } ixgbe_phys_map_t;
36*633c5e5cSRobert Mustacchi 
37*633c5e5cSRobert Mustacchi const ixgbe_phys_map_t ixgbe_phys_map[] = {
38*633c5e5cSRobert Mustacchi 	/*
39*633c5e5cSRobert Mustacchi 	 * First we lead off with all copper based speeds. Note, some of these
40*633c5e5cSRobert Mustacchi 	 * may be used through an SFP or similar. SPEED_10 is listed here for
41*633c5e5cSRobert Mustacchi 	 * completeness sake, as other drivers list them, though it is is hard
42*633c5e5cSRobert Mustacchi 	 * to figure out how it is possible to get to 10 Mb/s because the X540 /
43*633c5e5cSRobert Mustacchi 	 * X550 do not support 10BASE-T.
44*633c5e5cSRobert Mustacchi 	 */
45*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_T, SPEED_10GB, ETHER_MEDIA_10GBASE_T },
46*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_T, SPEED_5GB, ETHER_MEDIA_5000BASE_T },
47*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_T, SPEED_2_5GB, ETHER_MEDIA_2500BASE_T },
48*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_T, SPEED_1GB, ETHER_MEDIA_1000BASE_T },
49*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_1000BASE_T, SPEED_1GB, ETHER_MEDIA_1000BASE_T },
50*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_T, SPEED_100, ETHER_MEDIA_100BASE_TX },
51*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_1000BASE_T, SPEED_100, ETHER_MEDIA_100BASE_TX },
52*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_100BASE_TX, SPEED_100, ETHER_MEDIA_100BASE_TX },
53*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_T, SPEED_10, ETHER_MEDIA_10BASE_T },
54*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_1000BASE_T, SPEED_10, ETHER_MEDIA_10BASE_T },
55*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_100BASE_TX, SPEED_10, ETHER_MEDIA_10BASE_T },
56*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10BASE_T, SPEED_10, ETHER_MEDIA_10BASE_T },
57*633c5e5cSRobert Mustacchi 	/*
58*633c5e5cSRobert Mustacchi 	 * After this point we mostly are in backplane or SFP based formats. In
59*633c5e5cSRobert Mustacchi 	 * general there is a 1:1 mapping between a physical ability and a
60*633c5e5cSRobert Mustacchi 	 * speed. However, a few allow multiple speeds to be set and we have to
61*633c5e5cSRobert Mustacchi 	 * derive this from the common code. Example of this nuance in
62*633c5e5cSRobert Mustacchi 	 * particular are around KR/KX.
63*633c5e5cSRobert Mustacchi 	 */
64*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU, SPEED_10GB,
65*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10GBASE_CR },
66*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_LR, SPEED_10GB,
67*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10GBASE_LR },
68*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_LR, SPEED_1GB,
69*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_1000BASE_LX },
70*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_LRM, SPEED_10GB,
71*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10GBASE_LRM },
72*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_LRM, SPEED_1GB,
73*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_1000BASE_LX },
74*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_SR, SPEED_10GB,
75*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10GBASE_SR },
76*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_SR, SPEED_1GB,
77*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_1000BASE_SX },
78*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_KX4, SPEED_10GB,
79*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10GBASE_KX4 },
80*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_KX4, SPEED_1GB,
81*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_1000BASE_KX },
82*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_CX4, SPEED_10GB,
83*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10GBASE_CX4 },
84*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_1000BASE_KX, SPEED_1GB,
85*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_1000BASE_KX },
86*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_1000BASE_BX, SPEED_1GB,
87*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_1000BASE_BX },
88*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_KR, SPEED_10GB,
89*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10GBASE_KR },
90*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_KR, SPEED_2_5GB,
91*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_2500BASE_KX },
92*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_10GBASE_XAUI, SPEED_10GB,
93*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10G_XAUI },
94*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_SFP_ACTIVE_DA, SPEED_10GB,
95*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_10GBASE_ACC },
96*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_1000BASE_SX, SPEED_1GB,
97*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_1000BASE_SX },
98*633c5e5cSRobert Mustacchi 	{ IXGBE_PHYSICAL_LAYER_2500BASE_KX, SPEED_2_5GB,
99*633c5e5cSRobert Mustacchi 	    ETHER_MEDIA_2500BASE_KX }
100*633c5e5cSRobert Mustacchi };
101*633c5e5cSRobert Mustacchi 
102*633c5e5cSRobert Mustacchi mac_ether_media_t
ixgbe_phy_to_media(ixgbe_t * ixgbe)103*633c5e5cSRobert Mustacchi ixgbe_phy_to_media(ixgbe_t *ixgbe)
104*633c5e5cSRobert Mustacchi {
105*633c5e5cSRobert Mustacchi 	struct ixgbe_hw *hw = &ixgbe->hw;
106*633c5e5cSRobert Mustacchi 
107*633c5e5cSRobert Mustacchi 	ASSERT(MUTEX_HELD(&ixgbe->gen_lock));
108*633c5e5cSRobert Mustacchi 	switch (hw->phy.media_type) {
109*633c5e5cSRobert Mustacchi 	case ixgbe_media_type_copper:
110*633c5e5cSRobert Mustacchi 	case ixgbe_media_type_fiber:
111*633c5e5cSRobert Mustacchi 	case ixgbe_media_type_fiber_fixed:
112*633c5e5cSRobert Mustacchi 	case ixgbe_media_type_fiber_qsfp:
113*633c5e5cSRobert Mustacchi 	case ixgbe_media_type_backplane:
114*633c5e5cSRobert Mustacchi 	case ixgbe_media_type_cx4:
115*633c5e5cSRobert Mustacchi 		for (size_t i = 0; i < ARRAY_SIZE(ixgbe_phys_map); i++) {
116*633c5e5cSRobert Mustacchi 			const ixgbe_phys_map_t *map = &ixgbe_phys_map[i];
117*633c5e5cSRobert Mustacchi 			if ((ixgbe->phys_supported & map->ipm_phys) != 0 &&
118*633c5e5cSRobert Mustacchi 			    ixgbe->link_speed == map->ipm_speed) {
119*633c5e5cSRobert Mustacchi 				return (map->ipm_media);
120*633c5e5cSRobert Mustacchi 			}
121*633c5e5cSRobert Mustacchi 		}
122*633c5e5cSRobert Mustacchi 
123*633c5e5cSRobert Mustacchi 		if (ixgbe->link_state != LINK_STATE_DOWN) {
124*633c5e5cSRobert Mustacchi 			return (ETHER_MEDIA_UNKNOWN);
125*633c5e5cSRobert Mustacchi 		} else {
126*633c5e5cSRobert Mustacchi 			return (ETHER_MEDIA_NONE);
127*633c5e5cSRobert Mustacchi 		}
128*633c5e5cSRobert Mustacchi 		break;
129*633c5e5cSRobert Mustacchi 	/*
130*633c5e5cSRobert Mustacchi 	 * We don't bother trying to make up anything for a VF.
131*633c5e5cSRobert Mustacchi 	 */
132*633c5e5cSRobert Mustacchi 	case ixgbe_media_type_virtual:
133*633c5e5cSRobert Mustacchi 		return (ETHER_MEDIA_NONE);
134*633c5e5cSRobert Mustacchi 	default:
135*633c5e5cSRobert Mustacchi 		return (ETHER_MEDIA_UNKNOWN);
136*633c5e5cSRobert Mustacchi 	}
137*633c5e5cSRobert Mustacchi }
138*633c5e5cSRobert Mustacchi 
13945d3dd98SRobert Mustacchi static int
ixgbe_transceiver_is_8472(ixgbe_t * ixgbe,boolean_t * valp)14045d3dd98SRobert Mustacchi ixgbe_transceiver_is_8472(ixgbe_t *ixgbe, boolean_t *valp)
14145d3dd98SRobert Mustacchi {
14245d3dd98SRobert Mustacchi 	int32_t ret;
14345d3dd98SRobert Mustacchi 	uint8_t rev, swap;
14445d3dd98SRobert Mustacchi 	struct ixgbe_hw *hw = &ixgbe->hw;
14545d3dd98SRobert Mustacchi 
14645d3dd98SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ixgbe->gen_lock));
14745d3dd98SRobert Mustacchi 	if (hw->phy.ops.read_i2c_eeprom == NULL)
14845d3dd98SRobert Mustacchi 		return (ENOTSUP);
14945d3dd98SRobert Mustacchi 
15045d3dd98SRobert Mustacchi 	ret = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_SFF_8472_COMP, &rev);
15145d3dd98SRobert Mustacchi 	if (ret != 0)
15245d3dd98SRobert Mustacchi 		return (EIO);
15345d3dd98SRobert Mustacchi 
15445d3dd98SRobert Mustacchi 	ret = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_SFF_8472_SWAP, &swap);
15545d3dd98SRobert Mustacchi 	if (ret != 0)
15645d3dd98SRobert Mustacchi 		return (EIO);
15745d3dd98SRobert Mustacchi 
15845d3dd98SRobert Mustacchi 	if (swap & IXGBE_SFF_ADDRESSING_MODE) {
15945d3dd98SRobert Mustacchi 		ixgbe_log(ixgbe, "transceiver requires unsupported address "
16045d3dd98SRobert Mustacchi 		    "change for page 0xa2. Access will only be allowed to "
16145d3dd98SRobert Mustacchi 		    "page 0xa0.");
16245d3dd98SRobert Mustacchi 	}
16345d3dd98SRobert Mustacchi 
16445d3dd98SRobert Mustacchi 	if (rev == IXGBE_SFF_SFF_8472_UNSUP ||
16545d3dd98SRobert Mustacchi 	    (swap & IXGBE_SFF_ADDRESSING_MODE)) {
16645d3dd98SRobert Mustacchi 		*valp = B_FALSE;
16745d3dd98SRobert Mustacchi 	} else {
16845d3dd98SRobert Mustacchi 		*valp = B_TRUE;
16945d3dd98SRobert Mustacchi 	}
17045d3dd98SRobert Mustacchi 
17145d3dd98SRobert Mustacchi 	return (0);
17245d3dd98SRobert Mustacchi }
17345d3dd98SRobert Mustacchi 
17445d3dd98SRobert Mustacchi /*
17545d3dd98SRobert Mustacchi  * Note, we presume that the mac perimeter is held during these calls. As such,
17645d3dd98SRobert Mustacchi  * we rely on that for guaranteeing that only one thread is calling the i2c
17745d3dd98SRobert Mustacchi  * routines at any time.
17845d3dd98SRobert Mustacchi  */
17945d3dd98SRobert Mustacchi int
ixgbe_transceiver_info(void * arg,uint_t id,mac_transceiver_info_t * infop)18045d3dd98SRobert Mustacchi ixgbe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
18145d3dd98SRobert Mustacchi {
18245d3dd98SRobert Mustacchi 	ixgbe_t *ixgbe = arg;
18345d3dd98SRobert Mustacchi 	struct ixgbe_hw *hw = &ixgbe->hw;
18445d3dd98SRobert Mustacchi 	boolean_t present, usable;
18545d3dd98SRobert Mustacchi 
18645d3dd98SRobert Mustacchi 	if (id != 0 || infop == NULL)
18745d3dd98SRobert Mustacchi 		return (EINVAL);
18845d3dd98SRobert Mustacchi 
18945d3dd98SRobert Mustacchi 	mutex_enter(&ixgbe->gen_lock);
19045d3dd98SRobert Mustacchi 	if (ixgbe_get_media_type(&ixgbe->hw) == ixgbe_media_type_copper) {
19145d3dd98SRobert Mustacchi 		mutex_exit(&ixgbe->gen_lock);
19245d3dd98SRobert Mustacchi 		return (ENOTSUP);
19345d3dd98SRobert Mustacchi 	}
19445d3dd98SRobert Mustacchi 
19545d3dd98SRobert Mustacchi 	/*
19645d3dd98SRobert Mustacchi 	 * Make sure we have the latest sfp information. This is especially
19745d3dd98SRobert Mustacchi 	 * important if the SFP is removed as that doesn't trigger interrupts in
19845d3dd98SRobert Mustacchi 	 * our current configuration.
19945d3dd98SRobert Mustacchi 	 */
20045d3dd98SRobert Mustacchi 	(void) hw->phy.ops.identify_sfp(hw);
20145d3dd98SRobert Mustacchi 	if (hw->phy.type == ixgbe_phy_none ||
20245d3dd98SRobert Mustacchi 	    (hw->phy.type == ixgbe_phy_unknown &&
20345d3dd98SRobert Mustacchi 	    hw->phy.sfp_type == ixgbe_sfp_type_not_present)) {
20445d3dd98SRobert Mustacchi 		present = B_FALSE;
20545d3dd98SRobert Mustacchi 		usable = B_FALSE;
20645d3dd98SRobert Mustacchi 	} else {
20745d3dd98SRobert Mustacchi 		present = B_TRUE;
20845d3dd98SRobert Mustacchi 		usable = hw->phy.type != ixgbe_phy_sfp_unsupported;
20945d3dd98SRobert Mustacchi 	}
21045d3dd98SRobert Mustacchi 
21145d3dd98SRobert Mustacchi 	mutex_exit(&ixgbe->gen_lock);
21245d3dd98SRobert Mustacchi 
21345d3dd98SRobert Mustacchi 	mac_transceiver_info_set_present(infop, present);
21445d3dd98SRobert Mustacchi 	mac_transceiver_info_set_usable(infop, usable);
21545d3dd98SRobert Mustacchi 
21645d3dd98SRobert Mustacchi 	return (0);
21745d3dd98SRobert Mustacchi }
21845d3dd98SRobert Mustacchi 
21945d3dd98SRobert Mustacchi /*
22045d3dd98SRobert Mustacchi  * Note, we presume that the mac perimeter is held during these calls. As such,
22145d3dd98SRobert Mustacchi  * we rely on that for guaranteeing that only one thread is calling the i2c
22245d3dd98SRobert Mustacchi  * routines at any time.
22345d3dd98SRobert Mustacchi  */
22445d3dd98SRobert Mustacchi int
ixgbe_transceiver_read(void * arg,uint_t id,uint_t page,void * bp,size_t nbytes,off_t offset,size_t * nread)22545d3dd98SRobert Mustacchi ixgbe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp,
22645d3dd98SRobert Mustacchi     size_t nbytes, off_t offset, size_t *nread)
22745d3dd98SRobert Mustacchi {
22845d3dd98SRobert Mustacchi 	ixgbe_t *ixgbe = arg;
22945d3dd98SRobert Mustacchi 	struct ixgbe_hw *hw = &ixgbe->hw;
23045d3dd98SRobert Mustacchi 	uint8_t *buf = bp;
23145d3dd98SRobert Mustacchi 	size_t i;
23245d3dd98SRobert Mustacchi 	boolean_t is8472;
23345d3dd98SRobert Mustacchi 
23445d3dd98SRobert Mustacchi 	if (id != 0 || buf == NULL || nbytes == 0 || nread == NULL ||
23545d3dd98SRobert Mustacchi 	    (page != 0xa0 && page != 0xa2) || offset < 0)
23645d3dd98SRobert Mustacchi 		return (EINVAL);
23745d3dd98SRobert Mustacchi 
23845d3dd98SRobert Mustacchi 	/*
23945d3dd98SRobert Mustacchi 	 * Both supported pages have a length of 256 bytes, ensure nothing asks
24045d3dd98SRobert Mustacchi 	 * us to go beyond that.
24145d3dd98SRobert Mustacchi 	 */
24245d3dd98SRobert Mustacchi 	if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
24345d3dd98SRobert Mustacchi 		return (EINVAL);
24445d3dd98SRobert Mustacchi 	}
24545d3dd98SRobert Mustacchi 
24645d3dd98SRobert Mustacchi 	mutex_enter(&ixgbe->gen_lock);
24745d3dd98SRobert Mustacchi 	if (ixgbe_get_media_type(&ixgbe->hw) == ixgbe_media_type_copper) {
24845d3dd98SRobert Mustacchi 		mutex_exit(&ixgbe->gen_lock);
24945d3dd98SRobert Mustacchi 		return (ENOTSUP);
25045d3dd98SRobert Mustacchi 	}
25145d3dd98SRobert Mustacchi 
25245d3dd98SRobert Mustacchi 	if (hw->phy.ops.read_i2c_eeprom == NULL) {
25345d3dd98SRobert Mustacchi 		mutex_exit(&ixgbe->gen_lock);
25445d3dd98SRobert Mustacchi 		return (ENOTSUP);
25545d3dd98SRobert Mustacchi 	}
25645d3dd98SRobert Mustacchi 
25745d3dd98SRobert Mustacchi 	if (ixgbe_transceiver_is_8472(ixgbe, &is8472) != 0) {
25845d3dd98SRobert Mustacchi 		mutex_exit(&ixgbe->gen_lock);
25945d3dd98SRobert Mustacchi 		return (EIO);
26045d3dd98SRobert Mustacchi 	}
26145d3dd98SRobert Mustacchi 
26245d3dd98SRobert Mustacchi 	if (!is8472 && page == 0xa2) {
26345d3dd98SRobert Mustacchi 		mutex_exit(&ixgbe->gen_lock);
26445d3dd98SRobert Mustacchi 		return (EINVAL);
26545d3dd98SRobert Mustacchi 	}
26645d3dd98SRobert Mustacchi 
26745d3dd98SRobert Mustacchi 	for (i = 0; i < nbytes; i++, offset++, buf++) {
26845d3dd98SRobert Mustacchi 		int32_t ret;
26945d3dd98SRobert Mustacchi 
27045d3dd98SRobert Mustacchi 		if (page == 0xa0) {
27145d3dd98SRobert Mustacchi 			ret = hw->phy.ops.read_i2c_eeprom(hw, offset, buf);
27245d3dd98SRobert Mustacchi 		} else {
27345d3dd98SRobert Mustacchi 			ret = hw->phy.ops.read_i2c_sff8472(hw, offset, buf);
27445d3dd98SRobert Mustacchi 		}
27545d3dd98SRobert Mustacchi 		if (ret != 0) {
27645d3dd98SRobert Mustacchi 			mutex_exit(&ixgbe->gen_lock);
27745d3dd98SRobert Mustacchi 			return (EIO);
27845d3dd98SRobert Mustacchi 		}
27945d3dd98SRobert Mustacchi 	}
28045d3dd98SRobert Mustacchi 	mutex_exit(&ixgbe->gen_lock);
28145d3dd98SRobert Mustacchi 	*nread = i;
28245d3dd98SRobert Mustacchi 
28345d3dd98SRobert Mustacchi 	return (0);
28445d3dd98SRobert Mustacchi }
285