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