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 * Copyright 2023 Oxide Computer Company
15 */
16
17 /*
18 * illumos specific bnxe related functions.
19 */
20
21 #include "bnxe.h"
22
23 /*
24 * Try to figure out which phy we should be using at this time based on the
25 * requested transceiver.
26 */
27 static uint_t
bnxe_get_phy_id(um_device_t * um)28 bnxe_get_phy_id(um_device_t *um)
29 {
30 if (um->lm_dev.params.link.num_phys <= 1)
31 return (ELINK_INT_PHY);
32
33 if (um->lm_dev.vars.link.link_up) {
34 if ((um->lm_dev.vars.link.link_status &
35 LINK_STATUS_SERDES_LINK) &&
36 (um->lm_dev.params.link.phy[ELINK_EXT_PHY2].supported &
37 ELINK_SUPPORTED_FIBRE))
38 return (ELINK_EXT_PHY2);
39 return (ELINK_EXT_PHY1);
40 } else {
41 switch (elink_phy_selection(&um->lm_dev.params.link)) {
42 case PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT:
43 case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY:
44 case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY:
45 return (ELINK_EXT_PHY1);
46 case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY:
47 case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY:
48 return (ELINK_EXT_PHY2);
49 /*
50 * The above hardware types are the only ones currently defined
51 * by the specification and common code. If we end up with an
52 * unknown value, then we default to what the hardware considers
53 * the default, which is PHY1.
54 */
55 default:
56 return (ELINK_EXT_PHY1);
57 }
58 }
59 }
60
61 /*
62 * This media map table and structure is shared across the different pluggable
63 * modules. The driver doesn't really look carefully at the difference between
64 * the various multi-speed modules which is why for 10G based pieces we also
65 * have lower speed based checks.
66 */
67 typedef struct {
68 uint32_t bmm_sfp;
69 uint32_t bmm_speed;
70 mac_ether_media_t bmm_media;
71 } bnxe_media_map_t;
72
73 static const bnxe_media_map_t bnxe_media_map[] = {
74 { ELINK_ETH_SFP_10GBASE_SR, 10000, ETHER_MEDIA_10GBASE_SR },
75 { ELINK_ETH_SFP_10GBASE_SR, 1000, ETHER_MEDIA_1000BASE_SX },
76 { ELINK_ETH_SFP_1GBASE_SX, 1000, ETHER_MEDIA_1000BASE_SX },
77 { ELINK_ETH_SFP_10GBASE_LR, 10000, ETHER_MEDIA_10GBASE_LR },
78 { ELINK_ETH_SFP_10GBASE_LR, 1000, ETHER_MEDIA_1000BASE_LX },
79 { ELINK_ETH_SFP_1GBASE_LX, 1000, ETHER_MEDIA_1000BASE_LX },
80 { ELINK_ETH_SFP_10GBASE_LRM, 10000, ETHER_MEDIA_10GBASE_LRM },
81 { ELINK_ETH_SFP_10GBASE_ER, 10000, ETHER_MEDIA_10GBASE_ER },
82 { ELINK_ETH_SFP_1GBASE_T, 1000, ETHER_MEDIA_1000BASE_T },
83 { ELINK_ETH_SFP_1GBASE_CX, 1000, ETHER_MEDIA_1000BASE_CX },
84 { ELINK_ETH_SFP_DAC, 10000, ETHER_MEDIA_10GBASE_CR },
85 { ELINK_ETH_SFP_ACC, 10000, ETHER_MEDIA_10GBASE_ACC },
86 };
87
88 mac_ether_media_t
bnxe_phy_to_media(um_device_t * um)89 bnxe_phy_to_media(um_device_t *um)
90 {
91 uint_t phyid;
92 struct elink_params *params;
93 struct elink_phy *phy;
94 mac_ether_media_t media = ETHER_MEDIA_UNKNOWN;
95
96 BNXE_LOCK_ENTER_PHY(um);
97 phyid = bnxe_get_phy_id(um);
98 params = &um->lm_dev.params.link;
99 phy = ¶ms->phy[phyid];
100
101 switch (phy->media_type) {
102 /*
103 * Right now the driver does not ask the XFP i2c entity to determine the
104 * media information. If we encounter someone with an XFP device then we
105 * can add logic to the driver to cover proper detection, but otherwise
106 * it would fit into this same set of modes.
107 */
108 case ELINK_ETH_PHY_SFPP_10G_FIBER:
109 case ELINK_ETH_PHY_XFP_FIBER:
110 case ELINK_ETH_PHY_DA_TWINAX:
111 case ELINK_ETH_PHY_SFP_1G_FIBER:
112 for (size_t i = 0; i < ARRAY_SIZE(bnxe_media_map); i++) {
113 const bnxe_media_map_t *map = &bnxe_media_map[i];
114 if (phy->sfp_media == map->bmm_sfp &&
115 um->props.link_speed == map->bmm_speed) {
116 media = map->bmm_media;
117 break;
118 }
119 }
120 break;
121 case ELINK_ETH_PHY_BASE_T:
122 switch (um->props.link_speed) {
123 case 10:
124 media = ETHER_MEDIA_10BASE_T;
125 break;
126 case 100:
127 media = ETHER_MEDIA_100BASE_TX;
128 break;
129 case 1000:
130 media = ETHER_MEDIA_1000BASE_T;
131 break;
132 case 10000:
133 media = ETHER_MEDIA_10GBASE_T;
134 break;
135 default:
136 break;
137 }
138 break;
139 case ELINK_ETH_PHY_KR:
140 switch (um->props.link_speed) {
141 case 1000:
142 media = ETHER_MEDIA_1000BASE_KX;
143 break;
144 case 10000:
145 media = ETHER_MEDIA_10GBASE_KR;
146 break;
147 default:
148 break;
149 }
150 break;
151 case ELINK_ETH_PHY_CX4:
152 if (um->props.link_speed == 10000) {
153 media = ETHER_MEDIA_10GBASE_CX4;
154 }
155 break;
156 case ELINK_ETH_PHY_NOT_PRESENT:
157 media = ETHER_MEDIA_NONE;
158 break;
159 case ELINK_ETH_PHY_UNSPECIFIED:
160 default:
161 media = ETHER_MEDIA_UNKNOWN;
162 break;
163 }
164
165 BNXE_LOCK_EXIT_PHY(um);
166 return (media);
167 }
168
169
170 static int
bnxe_transceiver_info(void * arg,uint_t id,mac_transceiver_info_t * infop)171 bnxe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
172 {
173 uint_t phyid;
174 um_device_t *um = arg;
175 struct elink_params *params;
176 struct elink_phy *phy;
177 boolean_t present = B_FALSE, usable = B_FALSE;
178 elink_status_t ret;
179 uint8_t buf;
180
181 if (id != 0 || arg == NULL || infop == NULL)
182 return (EINVAL);
183
184 BNXE_LOCK_ENTER_PHY(um);
185 phyid = bnxe_get_phy_id(um);
186 params = &um->lm_dev.params.link;
187 phy = ¶ms->phy[phyid];
188
189 switch (phy->media_type) {
190 case ELINK_ETH_PHY_SFPP_10G_FIBER:
191 case ELINK_ETH_PHY_DA_TWINAX:
192 case ELINK_ETH_PHY_SFP_1G_FIBER:
193 break;
194 default:
195 BNXE_LOCK_EXIT_PHY(um);
196 return (ENOTSUP);
197 }
198
199 /*
200 * Right now, the core OS-independent code from QLogic doesn't quite
201 * track whether or not the phy is plugged in, though it easily could.
202 * As such, the best way to determine whether or not the phy is present
203 * is to see if we can read the first byte from page 0xa0. We expect to
204 * get an explicit timeout if the device isn't present. We'll propagate
205 * EIO on any other error as we're not in a good state to understand
206 * what happened.
207 */
208 PHY_HW_LOCK(&um->lm_dev);
209 ret = elink_read_sfp_module_eeprom(phy, params, 0xa0, 0, sizeof (buf),
210 &buf);
211 PHY_HW_UNLOCK(&um->lm_dev);
212 if (ret != ELINK_STATUS_OK && ret != ELINK_STATUS_TIMEOUT) {
213 BNXE_LOCK_EXIT_PHY(um);
214 return (EIO);
215 }
216 if (ret == ELINK_STATUS_OK) {
217 present = B_TRUE;
218 if ((phy->flags & ELINK_FLAGS_SFP_NOT_APPROVED) == 0)
219 usable = B_TRUE;
220 }
221 BNXE_LOCK_EXIT_PHY(um);
222
223 mac_transceiver_info_set_present(infop, present);
224 mac_transceiver_info_set_usable(infop, usable);
225
226 return (0);
227 }
228
229 static int
bnxe_transceiver_read(void * arg,uint_t id,uint_t page,void * bp,size_t nbytes,off_t offset,size_t * nread)230 bnxe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp,
231 size_t nbytes, off_t offset, size_t *nread)
232 {
233 uint_t phyid;
234 um_device_t *um = arg;
235 struct elink_phy *phy;
236 struct elink_params *params;
237 elink_status_t ret;
238
239 if (id != 0 || bp == NULL || nbytes == 0 || nread == NULL ||
240 (page != 0xa0 && page != 0xa2) || offset < 0)
241 return (EINVAL);
242
243 /*
244 * Sanity check length params.
245 */
246 if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
247 return (EINVAL);
248 }
249
250 BNXE_LOCK_ENTER_PHY(um);
251 phyid = bnxe_get_phy_id(um);
252 params = &um->lm_dev.params.link;
253 phy = &um->lm_dev.params.link.phy[phyid];
254
255 switch (phy->media_type) {
256 case ELINK_ETH_PHY_SFPP_10G_FIBER:
257 case ELINK_ETH_PHY_DA_TWINAX:
258 case ELINK_ETH_PHY_SFP_1G_FIBER:
259 break;
260 default:
261 BNXE_LOCK_EXIT_PHY(um);
262 return (ENOTSUP);
263 }
264
265 PHY_HW_LOCK(&um->lm_dev);
266 ret = elink_read_sfp_module_eeprom(phy, params, (uint8_t)page,
267 (uint16_t)offset, (uint16_t)nbytes, bp);
268 PHY_HW_UNLOCK(&um->lm_dev);
269
270 BNXE_LOCK_EXIT_PHY(um);
271
272 switch (ret) {
273 case ELINK_STATUS_OK:
274 *nread = nbytes;
275 return (0);
276 case ELINK_OP_NOT_SUPPORTED:
277 return (ENOTSUP);
278 default:
279 return (EIO);
280 }
281 }
282
283 boolean_t
bnxe_fill_transceiver(um_device_t * um,void * arg)284 bnxe_fill_transceiver(um_device_t *um, void *arg)
285 {
286 uint_t ntran = 1;
287 mac_capab_transceiver_t *mct = arg;
288
289 mct->mct_flags = 0;
290 /*
291 * While there is nominally a dual-phy version of bnxe out there (see
292 * ELINK_DUAL_MEDIA and related macros), these haven't been seen in the
293 * wild. For now, only assume that we have a single phy.
294 */
295 mct->mct_ntransceivers = 1;
296 mct->mct_info = bnxe_transceiver_info;
297 mct->mct_read = bnxe_transceiver_read;
298
299 return (B_TRUE);
300 }
301