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 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 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 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 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 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