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
ixgbe_transceiver_is_8472(ixgbe_t * ixgbe,boolean_t * valp)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
ixgbe_transceiver_info(void * arg,uint_t id,mac_transceiver_info_t * infop)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
ixgbe_transceiver_read(void * arg,uint_t id,uint_t page,void * bp,size_t nbytes,off_t offset,size_t * nread)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