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 (c) 2017, Joyent, Inc.
14  */
15 
16 /*
17  * Routines to get access to the phy and transceiver that require routines and
18  * definitions that aren't part of the common ixgbe API.
19  */
20 
21 #include "ixgbe_sw.h"
22 #include "ixgbe_phy.h"
23 
24 static int
25 ixgbe_transceiver_is_8472(ixgbe_t *ixgbe, boolean_t *valp)
26 {
27 	int32_t ret;
28 	uint8_t rev, swap;
29 	struct ixgbe_hw *hw = &ixgbe->hw;
30 
31 	ASSERT(MUTEX_HELD(&ixgbe->gen_lock));
32 	if (hw->phy.ops.read_i2c_eeprom == NULL)
33 		return (ENOTSUP);
34 
35 	ret = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_SFF_8472_COMP, &rev);
36 	if (ret != 0)
37 		return (EIO);
38 
39 	ret = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_SFF_8472_SWAP, &swap);
40 	if (ret != 0)
41 		return (EIO);
42 
43 	if (swap & IXGBE_SFF_ADDRESSING_MODE) {
44 		ixgbe_log(ixgbe, "transceiver requires unsupported address "
45 		    "change for page 0xa2. Access will only be allowed to "
46 		    "page 0xa0.");
47 	}
48 
49 	if (rev == IXGBE_SFF_SFF_8472_UNSUP ||
50 	    (swap & IXGBE_SFF_ADDRESSING_MODE)) {
51 		*valp = B_FALSE;
52 	} else {
53 		*valp = B_TRUE;
54 	}
55 
56 	return (0);
57 }
58 
59 /*
60  * Note, we presume that the mac perimeter is held during these calls. As such,
61  * we rely on that for guaranteeing that only one thread is calling the i2c
62  * routines at any time.
63  */
64 int
65 ixgbe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
66 {
67 	ixgbe_t *ixgbe = arg;
68 	struct ixgbe_hw *hw = &ixgbe->hw;
69 	boolean_t present, usable;
70 
71 	if (id != 0 || infop == NULL)
72 		return (EINVAL);
73 
74 	mutex_enter(&ixgbe->gen_lock);
75 	if (ixgbe_get_media_type(&ixgbe->hw) == ixgbe_media_type_copper) {
76 		mutex_exit(&ixgbe->gen_lock);
77 		return (ENOTSUP);
78 	}
79 
80 	/*
81 	 * Make sure we have the latest sfp information. This is especially
82 	 * important if the SFP is removed as that doesn't trigger interrupts in
83 	 * our current configuration.
84 	 */
85 	(void) hw->phy.ops.identify_sfp(hw);
86 	if (hw->phy.type == ixgbe_phy_none ||
87 	    (hw->phy.type == ixgbe_phy_unknown &&
88 	    hw->phy.sfp_type == ixgbe_sfp_type_not_present)) {
89 		present = B_FALSE;
90 		usable = B_FALSE;
91 	} else {
92 		present = B_TRUE;
93 		usable = hw->phy.type != ixgbe_phy_sfp_unsupported;
94 	}
95 
96 	mutex_exit(&ixgbe->gen_lock);
97 
98 	mac_transceiver_info_set_present(infop, present);
99 	mac_transceiver_info_set_usable(infop, usable);
100 
101 	return (0);
102 }
103 
104 /*
105  * Note, we presume that the mac perimeter is held during these calls. As such,
106  * we rely on that for guaranteeing that only one thread is calling the i2c
107  * routines at any time.
108  */
109 int
110 ixgbe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp,
111     size_t nbytes, off_t offset, size_t *nread)
112 {
113 	ixgbe_t *ixgbe = arg;
114 	struct ixgbe_hw *hw = &ixgbe->hw;
115 	uint8_t *buf = bp;
116 	size_t i;
117 	boolean_t is8472;
118 
119 	if (id != 0 || buf == NULL || nbytes == 0 || nread == NULL ||
120 	    (page != 0xa0 && page != 0xa2) || offset < 0)
121 		return (EINVAL);
122 
123 	/*
124 	 * Both supported pages have a length of 256 bytes, ensure nothing asks
125 	 * us to go beyond that.
126 	 */
127 	if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
128 		return (EINVAL);
129 	}
130 
131 	mutex_enter(&ixgbe->gen_lock);
132 	if (ixgbe_get_media_type(&ixgbe->hw) == ixgbe_media_type_copper) {
133 		mutex_exit(&ixgbe->gen_lock);
134 		return (ENOTSUP);
135 	}
136 
137 	if (hw->phy.ops.read_i2c_eeprom == NULL) {
138 		mutex_exit(&ixgbe->gen_lock);
139 		return (ENOTSUP);
140 	}
141 
142 	if (ixgbe_transceiver_is_8472(ixgbe, &is8472) != 0) {
143 		mutex_exit(&ixgbe->gen_lock);
144 		return (EIO);
145 	}
146 
147 	if (!is8472 && page == 0xa2) {
148 		mutex_exit(&ixgbe->gen_lock);
149 		return (EINVAL);
150 	}
151 
152 	for (i = 0; i < nbytes; i++, offset++, buf++) {
153 		int32_t ret;
154 
155 		if (page == 0xa0) {
156 			ret = hw->phy.ops.read_i2c_eeprom(hw, offset, buf);
157 		} else {
158 			ret = hw->phy.ops.read_i2c_sff8472(hw, offset, buf);
159 		}
160 		if (ret != 0) {
161 			mutex_exit(&ixgbe->gen_lock);
162 			return (EIO);
163 		}
164 	}
165 	mutex_exit(&ixgbe->gen_lock);
166 	*nread = i;
167 
168 	return (0);
169 }
170