/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright (c) 2017, Joyent, Inc. */ /* * Routines to get access to the phy and transceiver that require routines and * definitions that aren't part of the common ixgbe API. */ #include "ixgbe_sw.h" #include "ixgbe_phy.h" static int ixgbe_transceiver_is_8472(ixgbe_t *ixgbe, boolean_t *valp) { int32_t ret; uint8_t rev, swap; struct ixgbe_hw *hw = &ixgbe->hw; ASSERT(MUTEX_HELD(&ixgbe->gen_lock)); if (hw->phy.ops.read_i2c_eeprom == NULL) return (ENOTSUP); ret = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_SFF_8472_COMP, &rev); if (ret != 0) return (EIO); ret = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_SFF_8472_SWAP, &swap); if (ret != 0) return (EIO); if (swap & IXGBE_SFF_ADDRESSING_MODE) { ixgbe_log(ixgbe, "transceiver requires unsupported address " "change for page 0xa2. Access will only be allowed to " "page 0xa0."); } if (rev == IXGBE_SFF_SFF_8472_UNSUP || (swap & IXGBE_SFF_ADDRESSING_MODE)) { *valp = B_FALSE; } else { *valp = B_TRUE; } return (0); } /* * Note, we presume that the mac perimeter is held during these calls. As such, * we rely on that for guaranteeing that only one thread is calling the i2c * routines at any time. */ int ixgbe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop) { ixgbe_t *ixgbe = arg; struct ixgbe_hw *hw = &ixgbe->hw; boolean_t present, usable; if (id != 0 || infop == NULL) return (EINVAL); mutex_enter(&ixgbe->gen_lock); if (ixgbe_get_media_type(&ixgbe->hw) == ixgbe_media_type_copper) { mutex_exit(&ixgbe->gen_lock); return (ENOTSUP); } /* * Make sure we have the latest sfp information. This is especially * important if the SFP is removed as that doesn't trigger interrupts in * our current configuration. */ (void) hw->phy.ops.identify_sfp(hw); if (hw->phy.type == ixgbe_phy_none || (hw->phy.type == ixgbe_phy_unknown && hw->phy.sfp_type == ixgbe_sfp_type_not_present)) { present = B_FALSE; usable = B_FALSE; } else { present = B_TRUE; usable = hw->phy.type != ixgbe_phy_sfp_unsupported; } mutex_exit(&ixgbe->gen_lock); mac_transceiver_info_set_present(infop, present); mac_transceiver_info_set_usable(infop, usable); return (0); } /* * Note, we presume that the mac perimeter is held during these calls. As such, * we rely on that for guaranteeing that only one thread is calling the i2c * routines at any time. */ int ixgbe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp, size_t nbytes, off_t offset, size_t *nread) { ixgbe_t *ixgbe = arg; struct ixgbe_hw *hw = &ixgbe->hw; uint8_t *buf = bp; size_t i; boolean_t is8472; if (id != 0 || buf == NULL || nbytes == 0 || nread == NULL || (page != 0xa0 && page != 0xa2) || offset < 0) return (EINVAL); /* * Both supported pages have a length of 256 bytes, ensure nothing asks * us to go beyond that. */ if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) { return (EINVAL); } mutex_enter(&ixgbe->gen_lock); if (ixgbe_get_media_type(&ixgbe->hw) == ixgbe_media_type_copper) { mutex_exit(&ixgbe->gen_lock); return (ENOTSUP); } if (hw->phy.ops.read_i2c_eeprom == NULL) { mutex_exit(&ixgbe->gen_lock); return (ENOTSUP); } if (ixgbe_transceiver_is_8472(ixgbe, &is8472) != 0) { mutex_exit(&ixgbe->gen_lock); return (EIO); } if (!is8472 && page == 0xa2) { mutex_exit(&ixgbe->gen_lock); return (EINVAL); } for (i = 0; i < nbytes; i++, offset++, buf++) { int32_t ret; if (page == 0xa0) { ret = hw->phy.ops.read_i2c_eeprom(hw, offset, buf); } else { ret = hw->phy.ops.read_i2c_sff8472(hw, offset, buf); } if (ret != 0) { mutex_exit(&ixgbe->gen_lock); return (EIO); } } mutex_exit(&ixgbe->gen_lock); *nread = i; return (0); }