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 * illumos specific bnxe related functions. 18 */ 19 20 #include "bnxe.h" 21 22 /* 23 * Try to figure out which phy we should be using at this time based on the 24 * requested transceiver. 25 */ 26 static uint_t 27 bnxe_get_phy_id(um_device_t *um) 28 { 29 if (um->lm_dev.params.link.num_phys <= 1) 30 return (ELINK_INT_PHY); 31 32 if (um->lm_dev.vars.link.link_up) { 33 if ((um->lm_dev.vars.link.link_status & 34 LINK_STATUS_SERDES_LINK) && 35 (um->lm_dev.params.link.phy[ELINK_EXT_PHY2].supported & 36 ELINK_SUPPORTED_FIBRE)) 37 return (ELINK_EXT_PHY2); 38 return (ELINK_EXT_PHY1); 39 } else { 40 switch (elink_phy_selection(&um->lm_dev.params.link)) { 41 case PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT: 42 case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY: 43 case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY: 44 return (ELINK_EXT_PHY1); 45 case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY: 46 case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY: 47 return (ELINK_EXT_PHY2); 48 /* 49 * The above hardware types are the only ones currently defined 50 * by the specification and common code. If we end up with an 51 * unknown value, then we default to what the hardware considers 52 * the default, which is PHY1. 53 */ 54 default: 55 return (ELINK_EXT_PHY1); 56 } 57 } 58 } 59 60 static int 61 bnxe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop) 62 { 63 uint_t phyid; 64 um_device_t *um = arg; 65 struct elink_params *params; 66 struct elink_phy *phy; 67 boolean_t present = B_FALSE, usable = B_FALSE; 68 elink_status_t ret; 69 uint8_t buf; 70 71 if (id != 0 || arg == NULL || infop == NULL) 72 return (EINVAL); 73 74 BNXE_LOCK_ENTER_PHY(um); 75 phyid = bnxe_get_phy_id(um); 76 params = &um->lm_dev.params.link; 77 phy = ¶ms->phy[phyid]; 78 if (phy->media_type == ELINK_ETH_PHY_BASE_T) { 79 BNXE_LOCK_EXIT_PHY(um); 80 return (ENOTSUP); 81 } 82 83 /* 84 * Right now, the core OS-independent code from QLogic doesn't quite 85 * track whether or not the phy is plugged in, though it easily could. 86 * As such, the best way to determine whether or not the phy is present 87 * is to see if we can read the first byte from page 0xa0. We expect to 88 * get an explicit timeout if the device isn't present. We'll propagate 89 * EIO on any other error as we're not in a good state to understand 90 * what happened. 91 */ 92 PHY_HW_LOCK(&um->lm_dev); 93 ret = elink_read_sfp_module_eeprom(phy, params, 0xa0, 0, sizeof (buf), 94 &buf); 95 PHY_HW_UNLOCK(&um->lm_dev); 96 if (ret != ELINK_STATUS_OK && ret != ELINK_STATUS_TIMEOUT) { 97 BNXE_LOCK_EXIT_PHY(um); 98 return (EIO); 99 } 100 if (ret == ELINK_STATUS_OK) { 101 present = B_TRUE; 102 if ((phy->flags & ELINK_FLAGS_SFP_NOT_APPROVED) == 0) 103 usable = B_TRUE; 104 } 105 BNXE_LOCK_EXIT_PHY(um); 106 107 mac_transceiver_info_set_present(infop, present); 108 mac_transceiver_info_set_usable(infop, usable); 109 110 return (0); 111 } 112 113 static int 114 bnxe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp, 115 size_t nbytes, off_t offset, size_t *nread) 116 { 117 uint_t phyid; 118 um_device_t *um = arg; 119 struct elink_phy *phy; 120 struct elink_params *params; 121 elink_status_t ret; 122 123 if (id != 0 || bp == NULL || nbytes == 0 || nread == NULL || 124 (page != 0xa0 && page != 0xa2) || offset < 0) 125 return (EINVAL); 126 127 /* 128 * Sanity check length params. 129 */ 130 if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) { 131 return (EINVAL); 132 } 133 134 BNXE_LOCK_ENTER_PHY(um); 135 phyid = bnxe_get_phy_id(um); 136 params = &um->lm_dev.params.link; 137 phy = &um->lm_dev.params.link.phy[phyid]; 138 139 if (phy->media_type == ELINK_ETH_PHY_BASE_T) { 140 BNXE_LOCK_EXIT_PHY(um); 141 return (ENOTSUP); 142 } 143 144 145 PHY_HW_LOCK(&um->lm_dev); 146 ret = elink_read_sfp_module_eeprom(phy, params, (uint8_t)page, 147 (uint16_t)offset, (uint16_t)nbytes, bp); 148 PHY_HW_UNLOCK(&um->lm_dev); 149 150 BNXE_LOCK_EXIT_PHY(um); 151 152 switch (ret) { 153 case ELINK_STATUS_OK: 154 *nread = nbytes; 155 return (0); 156 case ELINK_OP_NOT_SUPPORTED: 157 return (ENOTSUP); 158 default: 159 return (EIO); 160 } 161 } 162 163 boolean_t 164 bnxe_fill_transceiver(um_device_t *um, void *arg) 165 { 166 uint_t ntran = 1; 167 mac_capab_transceiver_t *mct = arg; 168 169 mct->mct_flags = 0; 170 /* 171 * While there is nominally a dual-phy version of bnxe out there (see 172 * ELINK_DUAL_MEDIA and related macros), these haven't been seen in the 173 * wild. For now, only assume that we have a single phy. 174 */ 175 mct->mct_ntransceivers = 1; 176 mct->mct_info = bnxe_transceiver_info; 177 mct->mct_read = bnxe_transceiver_read; 178 179 return (B_TRUE); 180 } 181