Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 1 | /******************************************************************************* |
| 2 | |
| 3 | Intel 10 Gigabit PCI Express Linux driver |
Peter P Waskiewicz Jr | 3efac5a | 2009-02-01 01:19:20 -0800 | [diff] [blame^] | 4 | Copyright(c) 1999 - 2009 Intel Corporation. |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 5 | |
| 6 | This program is free software; you can redistribute it and/or modify it |
| 7 | under the terms and conditions of the GNU General Public License, |
| 8 | version 2, as published by the Free Software Foundation. |
| 9 | |
| 10 | This program is distributed in the hope it will be useful, but WITHOUT |
| 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 13 | more details. |
| 14 | |
| 15 | You should have received a copy of the GNU General Public License along with |
| 16 | this program; if not, write to the Free Software Foundation, Inc., |
| 17 | 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | |
| 19 | The full GNU General Public License is included in this distribution in |
| 20 | the file called "COPYING". |
| 21 | |
| 22 | Contact Information: |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 23 | e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> |
| 24 | Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
| 25 | |
| 26 | *******************************************************************************/ |
| 27 | |
| 28 | #include <linux/pci.h> |
| 29 | #include <linux/delay.h> |
| 30 | #include <linux/sched.h> |
| 31 | |
| 32 | #include "ixgbe_common.h" |
| 33 | #include "ixgbe_phy.h" |
| 34 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 35 | static bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 36 | static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id); |
| 37 | static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 38 | |
| 39 | /** |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 40 | * ixgbe_identify_phy_generic - Get physical layer module |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 41 | * @hw: pointer to hardware structure |
| 42 | * |
| 43 | * Determines the physical layer module found on the current adapter. |
| 44 | **/ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 45 | s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw) |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 46 | { |
| 47 | s32 status = IXGBE_ERR_PHY_ADDR_INVALID; |
| 48 | u32 phy_addr; |
| 49 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 50 | if (hw->phy.type == ixgbe_phy_unknown) { |
| 51 | for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) { |
| 52 | if (ixgbe_validate_phy_addr(hw, phy_addr)) { |
| 53 | hw->phy.addr = phy_addr; |
| 54 | ixgbe_get_phy_id(hw); |
| 55 | hw->phy.type = |
| 56 | ixgbe_get_phy_type_from_id(hw->phy.id); |
| 57 | status = 0; |
| 58 | break; |
| 59 | } |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 60 | } |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 61 | } else { |
| 62 | status = 0; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 63 | } |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 64 | |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 65 | return status; |
| 66 | } |
| 67 | |
| 68 | /** |
| 69 | * ixgbe_validate_phy_addr - Determines phy address is valid |
| 70 | * @hw: pointer to hardware structure |
| 71 | * |
| 72 | **/ |
| 73 | static bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr) |
| 74 | { |
| 75 | u16 phy_id = 0; |
| 76 | bool valid = false; |
| 77 | |
| 78 | hw->phy.addr = phy_addr; |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 79 | hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_HIGH, |
| 80 | IXGBE_MDIO_PMA_PMD_DEV_TYPE, &phy_id); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 81 | |
| 82 | if (phy_id != 0xFFFF && phy_id != 0x0) |
| 83 | valid = true; |
| 84 | |
| 85 | return valid; |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * ixgbe_get_phy_id - Get the phy type |
| 90 | * @hw: pointer to hardware structure |
| 91 | * |
| 92 | **/ |
| 93 | static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw) |
| 94 | { |
| 95 | u32 status; |
| 96 | u16 phy_id_high = 0; |
| 97 | u16 phy_id_low = 0; |
| 98 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 99 | status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_HIGH, |
| 100 | IXGBE_MDIO_PMA_PMD_DEV_TYPE, |
| 101 | &phy_id_high); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 102 | |
| 103 | if (status == 0) { |
| 104 | hw->phy.id = (u32)(phy_id_high << 16); |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 105 | status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_LOW, |
| 106 | IXGBE_MDIO_PMA_PMD_DEV_TYPE, |
| 107 | &phy_id_low); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 108 | hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK); |
| 109 | hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK); |
| 110 | } |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 111 | return status; |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * ixgbe_get_phy_type_from_id - Get the phy type |
| 116 | * @hw: pointer to hardware structure |
| 117 | * |
| 118 | **/ |
| 119 | static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id) |
| 120 | { |
| 121 | enum ixgbe_phy_type phy_type; |
| 122 | |
| 123 | switch (phy_id) { |
Jesse Brandeburg | 0befdb3 | 2008-10-31 00:46:40 -0700 | [diff] [blame] | 124 | case TN1010_PHY_ID: |
| 125 | phy_type = ixgbe_phy_tn; |
| 126 | break; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 127 | case QT2022_PHY_ID: |
| 128 | phy_type = ixgbe_phy_qt; |
| 129 | break; |
Donald Skidmore | c4900be | 2008-11-20 21:11:42 -0800 | [diff] [blame] | 130 | case ATH_PHY_ID: |
| 131 | phy_type = ixgbe_phy_nl; |
| 132 | break; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 133 | default: |
| 134 | phy_type = ixgbe_phy_unknown; |
| 135 | break; |
| 136 | } |
| 137 | |
| 138 | return phy_type; |
| 139 | } |
| 140 | |
| 141 | /** |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 142 | * ixgbe_reset_phy_generic - Performs a PHY reset |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 143 | * @hw: pointer to hardware structure |
| 144 | **/ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 145 | s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw) |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 146 | { |
| 147 | /* |
| 148 | * Perform soft PHY reset to the PHY_XS. |
| 149 | * This will cause a soft reset to the PHY |
| 150 | */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 151 | return hw->phy.ops.write_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL, |
| 152 | IXGBE_MDIO_PHY_XS_DEV_TYPE, |
| 153 | IXGBE_MDIO_PHY_XS_RESET); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | /** |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 157 | * ixgbe_read_phy_reg_generic - Reads a value from a specified PHY register |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 158 | * @hw: pointer to hardware structure |
| 159 | * @reg_addr: 32 bit address of PHY register to read |
| 160 | * @phy_data: Pointer to read data from PHY register |
| 161 | **/ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 162 | s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, |
| 163 | u32 device_type, u16 *phy_data) |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 164 | { |
| 165 | u32 command; |
| 166 | u32 i; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 167 | u32 data; |
| 168 | s32 status = 0; |
| 169 | u16 gssr; |
| 170 | |
| 171 | if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1) |
| 172 | gssr = IXGBE_GSSR_PHY1_SM; |
| 173 | else |
| 174 | gssr = IXGBE_GSSR_PHY0_SM; |
| 175 | |
| 176 | if (ixgbe_acquire_swfw_sync(hw, gssr) != 0) |
| 177 | status = IXGBE_ERR_SWFW_SYNC; |
| 178 | |
| 179 | if (status == 0) { |
| 180 | /* Setup and write the address cycle command */ |
| 181 | command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 182 | (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) | |
| 183 | (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) | |
| 184 | (IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND)); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 185 | |
| 186 | IXGBE_WRITE_REG(hw, IXGBE_MSCA, command); |
| 187 | |
| 188 | /* |
| 189 | * Check every 10 usec to see if the address cycle completed. |
| 190 | * The MDI Command bit will clear when the operation is |
| 191 | * complete |
| 192 | */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 193 | for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) { |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 194 | udelay(10); |
| 195 | |
| 196 | command = IXGBE_READ_REG(hw, IXGBE_MSCA); |
| 197 | |
| 198 | if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) |
| 199 | break; |
| 200 | } |
| 201 | |
| 202 | if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) { |
| 203 | hw_dbg(hw, "PHY address command did not complete.\n"); |
| 204 | status = IXGBE_ERR_PHY; |
| 205 | } |
| 206 | |
| 207 | if (status == 0) { |
| 208 | /* |
| 209 | * Address cycle complete, setup and write the read |
| 210 | * command |
| 211 | */ |
| 212 | command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 213 | (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) | |
| 214 | (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) | |
| 215 | (IXGBE_MSCA_READ | IXGBE_MSCA_MDI_COMMAND)); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 216 | |
| 217 | IXGBE_WRITE_REG(hw, IXGBE_MSCA, command); |
| 218 | |
| 219 | /* |
| 220 | * Check every 10 usec to see if the address cycle |
| 221 | * completed. The MDI Command bit will clear when the |
| 222 | * operation is complete |
| 223 | */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 224 | for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) { |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 225 | udelay(10); |
| 226 | |
| 227 | command = IXGBE_READ_REG(hw, IXGBE_MSCA); |
| 228 | |
| 229 | if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) |
| 230 | break; |
| 231 | } |
| 232 | |
| 233 | if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) { |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 234 | hw_dbg(hw, "PHY read command didn't complete\n"); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 235 | status = IXGBE_ERR_PHY; |
| 236 | } else { |
| 237 | /* |
| 238 | * Read operation is complete. Get the data |
| 239 | * from MSRWD |
| 240 | */ |
| 241 | data = IXGBE_READ_REG(hw, IXGBE_MSRWD); |
| 242 | data >>= IXGBE_MSRWD_READ_DATA_SHIFT; |
| 243 | *phy_data = (u16)(data); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | ixgbe_release_swfw_sync(hw, gssr); |
| 248 | } |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 249 | |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 250 | return status; |
| 251 | } |
| 252 | |
| 253 | /** |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 254 | * ixgbe_write_phy_reg_generic - Writes a value to specified PHY register |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 255 | * @hw: pointer to hardware structure |
| 256 | * @reg_addr: 32 bit PHY register to write |
| 257 | * @device_type: 5 bit device type |
| 258 | * @phy_data: Data to write to the PHY register |
| 259 | **/ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 260 | s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, |
| 261 | u32 device_type, u16 phy_data) |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 262 | { |
| 263 | u32 command; |
| 264 | u32 i; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 265 | s32 status = 0; |
| 266 | u16 gssr; |
| 267 | |
| 268 | if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1) |
| 269 | gssr = IXGBE_GSSR_PHY1_SM; |
| 270 | else |
| 271 | gssr = IXGBE_GSSR_PHY0_SM; |
| 272 | |
| 273 | if (ixgbe_acquire_swfw_sync(hw, gssr) != 0) |
| 274 | status = IXGBE_ERR_SWFW_SYNC; |
| 275 | |
| 276 | if (status == 0) { |
| 277 | /* Put the data in the MDI single read and write data register*/ |
| 278 | IXGBE_WRITE_REG(hw, IXGBE_MSRWD, (u32)phy_data); |
| 279 | |
| 280 | /* Setup and write the address cycle command */ |
| 281 | command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 282 | (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) | |
| 283 | (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) | |
| 284 | (IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND)); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 285 | |
| 286 | IXGBE_WRITE_REG(hw, IXGBE_MSCA, command); |
| 287 | |
| 288 | /* |
| 289 | * Check every 10 usec to see if the address cycle completed. |
| 290 | * The MDI Command bit will clear when the operation is |
| 291 | * complete |
| 292 | */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 293 | for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) { |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 294 | udelay(10); |
| 295 | |
| 296 | command = IXGBE_READ_REG(hw, IXGBE_MSCA); |
| 297 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 298 | if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 299 | break; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 300 | } |
| 301 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 302 | if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) { |
| 303 | hw_dbg(hw, "PHY address cmd didn't complete\n"); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 304 | status = IXGBE_ERR_PHY; |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 305 | } |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 306 | |
| 307 | if (status == 0) { |
| 308 | /* |
| 309 | * Address cycle complete, setup and write the write |
| 310 | * command |
| 311 | */ |
| 312 | command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 313 | (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) | |
| 314 | (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) | |
| 315 | (IXGBE_MSCA_WRITE | IXGBE_MSCA_MDI_COMMAND)); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 316 | |
| 317 | IXGBE_WRITE_REG(hw, IXGBE_MSCA, command); |
| 318 | |
| 319 | /* |
| 320 | * Check every 10 usec to see if the address cycle |
| 321 | * completed. The MDI Command bit will clear when the |
| 322 | * operation is complete |
| 323 | */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 324 | for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) { |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 325 | udelay(10); |
| 326 | |
| 327 | command = IXGBE_READ_REG(hw, IXGBE_MSCA); |
| 328 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 329 | if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 330 | break; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 331 | } |
| 332 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 333 | if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) { |
| 334 | hw_dbg(hw, "PHY address cmd didn't complete\n"); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 335 | status = IXGBE_ERR_PHY; |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 336 | } |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | ixgbe_release_swfw_sync(hw, gssr); |
| 340 | } |
| 341 | |
| 342 | return status; |
| 343 | } |
| 344 | |
| 345 | /** |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 346 | * ixgbe_setup_phy_link_generic - Set and restart autoneg |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 347 | * @hw: pointer to hardware structure |
| 348 | * |
| 349 | * Restart autonegotiation and PHY and waits for completion. |
| 350 | **/ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 351 | s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw) |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 352 | { |
| 353 | s32 status = IXGBE_NOT_IMPLEMENTED; |
| 354 | u32 time_out; |
| 355 | u32 max_time_out = 10; |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 356 | u16 autoneg_reg = IXGBE_MII_AUTONEG_REG; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 357 | |
| 358 | /* |
| 359 | * Set advertisement settings in PHY based on autoneg_advertised |
| 360 | * settings. If autoneg_advertised = 0, then advertise default values |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 361 | * tnx devices cannot be "forced" to a autoneg 10G and fail. But can |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 362 | * for a 1G. |
| 363 | */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 364 | hw->phy.ops.read_reg(hw, IXGBE_MII_SPEED_SELECTION_REG, |
| 365 | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &autoneg_reg); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 366 | |
| 367 | if (hw->phy.autoneg_advertised == IXGBE_LINK_SPEED_1GB_FULL) |
| 368 | autoneg_reg &= 0xEFFF; /* 0 in bit 12 is 1G operation */ |
| 369 | else |
| 370 | autoneg_reg |= 0x1000; /* 1 in bit 12 is 10G/1G operation */ |
| 371 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 372 | hw->phy.ops.write_reg(hw, IXGBE_MII_SPEED_SELECTION_REG, |
| 373 | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, autoneg_reg); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 374 | |
| 375 | /* Restart PHY autonegotiation and wait for completion */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 376 | hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_CONTROL, |
| 377 | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &autoneg_reg); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 378 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 379 | autoneg_reg |= IXGBE_MII_RESTART; |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 380 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 381 | hw->phy.ops.write_reg(hw, IXGBE_MDIO_AUTO_NEG_CONTROL, |
| 382 | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, autoneg_reg); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 383 | |
| 384 | /* Wait for autonegotiation to finish */ |
| 385 | for (time_out = 0; time_out < max_time_out; time_out++) { |
| 386 | udelay(10); |
| 387 | /* Restart PHY autonegotiation and wait for completion */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 388 | status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS, |
| 389 | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, |
| 390 | &autoneg_reg); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 391 | |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 392 | autoneg_reg &= IXGBE_MII_AUTONEG_COMPLETE; |
| 393 | if (autoneg_reg == IXGBE_MII_AUTONEG_COMPLETE) { |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 394 | status = 0; |
| 395 | break; |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | if (time_out == max_time_out) |
| 400 | status = IXGBE_ERR_LINK_SETUP; |
| 401 | |
| 402 | return status; |
| 403 | } |
| 404 | |
| 405 | /** |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 406 | * ixgbe_setup_phy_link_speed_generic - Sets the auto advertised capabilities |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 407 | * @hw: pointer to hardware structure |
| 408 | * @speed: new link speed |
| 409 | * @autoneg: true if autonegotiation enabled |
| 410 | **/ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 411 | s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, |
| 412 | ixgbe_link_speed speed, |
| 413 | bool autoneg, |
| 414 | bool autoneg_wait_to_complete) |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 415 | { |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 416 | |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 417 | /* |
| 418 | * Clear autoneg_advertised and set new values based on input link |
| 419 | * speed. |
| 420 | */ |
| 421 | hw->phy.autoneg_advertised = 0; |
| 422 | |
| 423 | if (speed & IXGBE_LINK_SPEED_10GB_FULL) |
| 424 | hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL; |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 425 | |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 426 | if (speed & IXGBE_LINK_SPEED_1GB_FULL) |
| 427 | hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL; |
| 428 | |
| 429 | /* Setup link based on the new speed settings */ |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 430 | hw->phy.ops.setup_link(hw); |
Auke Kok | 9a799d7 | 2007-09-15 14:07:45 -0700 | [diff] [blame] | 431 | |
| 432 | return 0; |
| 433 | } |
Jesse Brandeburg | c44ade9 | 2008-09-11 19:59:59 -0700 | [diff] [blame] | 434 | |
Jesse Brandeburg | 0befdb3 | 2008-10-31 00:46:40 -0700 | [diff] [blame] | 435 | /** |
Donald Skidmore | c4900be | 2008-11-20 21:11:42 -0800 | [diff] [blame] | 436 | * ixgbe_reset_phy_nl - Performs a PHY reset |
| 437 | * @hw: pointer to hardware structure |
| 438 | **/ |
| 439 | s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw) |
| 440 | { |
| 441 | u16 phy_offset, control, eword, edata, block_crc; |
| 442 | bool end_data = false; |
| 443 | u16 list_offset, data_offset; |
| 444 | u16 phy_data = 0; |
| 445 | s32 ret_val = 0; |
| 446 | u32 i; |
| 447 | |
| 448 | hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL, |
| 449 | IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data); |
| 450 | |
| 451 | /* reset the PHY and poll for completion */ |
| 452 | hw->phy.ops.write_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL, |
| 453 | IXGBE_MDIO_PHY_XS_DEV_TYPE, |
| 454 | (phy_data | IXGBE_MDIO_PHY_XS_RESET)); |
| 455 | |
| 456 | for (i = 0; i < 100; i++) { |
| 457 | hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL, |
| 458 | IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data); |
| 459 | if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) == 0) |
| 460 | break; |
| 461 | msleep(10); |
| 462 | } |
| 463 | |
| 464 | if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) != 0) { |
| 465 | hw_dbg(hw, "PHY reset did not complete.\n"); |
| 466 | ret_val = IXGBE_ERR_PHY; |
| 467 | goto out; |
| 468 | } |
| 469 | |
| 470 | /* Get init offsets */ |
| 471 | ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset, |
| 472 | &data_offset); |
| 473 | if (ret_val != 0) |
| 474 | goto out; |
| 475 | |
| 476 | ret_val = hw->eeprom.ops.read(hw, data_offset, &block_crc); |
| 477 | data_offset++; |
| 478 | while (!end_data) { |
| 479 | /* |
| 480 | * Read control word from PHY init contents offset |
| 481 | */ |
| 482 | ret_val = hw->eeprom.ops.read(hw, data_offset, &eword); |
| 483 | control = (eword & IXGBE_CONTROL_MASK_NL) >> |
| 484 | IXGBE_CONTROL_SHIFT_NL; |
| 485 | edata = eword & IXGBE_DATA_MASK_NL; |
| 486 | switch (control) { |
| 487 | case IXGBE_DELAY_NL: |
| 488 | data_offset++; |
| 489 | hw_dbg(hw, "DELAY: %d MS\n", edata); |
| 490 | msleep(edata); |
| 491 | break; |
| 492 | case IXGBE_DATA_NL: |
| 493 | hw_dbg(hw, "DATA: \n"); |
| 494 | data_offset++; |
| 495 | hw->eeprom.ops.read(hw, data_offset++, |
| 496 | &phy_offset); |
| 497 | for (i = 0; i < edata; i++) { |
| 498 | hw->eeprom.ops.read(hw, data_offset, &eword); |
| 499 | hw->phy.ops.write_reg(hw, phy_offset, |
| 500 | IXGBE_TWINAX_DEV, eword); |
| 501 | hw_dbg(hw, "Wrote %4.4x to %4.4x\n", eword, |
| 502 | phy_offset); |
| 503 | data_offset++; |
| 504 | phy_offset++; |
| 505 | } |
| 506 | break; |
| 507 | case IXGBE_CONTROL_NL: |
| 508 | data_offset++; |
| 509 | hw_dbg(hw, "CONTROL: \n"); |
| 510 | if (edata == IXGBE_CONTROL_EOL_NL) { |
| 511 | hw_dbg(hw, "EOL\n"); |
| 512 | end_data = true; |
| 513 | } else if (edata == IXGBE_CONTROL_SOL_NL) { |
| 514 | hw_dbg(hw, "SOL\n"); |
| 515 | } else { |
| 516 | hw_dbg(hw, "Bad control value\n"); |
| 517 | ret_val = IXGBE_ERR_PHY; |
| 518 | goto out; |
| 519 | } |
| 520 | break; |
| 521 | default: |
| 522 | hw_dbg(hw, "Bad control type\n"); |
| 523 | ret_val = IXGBE_ERR_PHY; |
| 524 | goto out; |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | out: |
| 529 | return ret_val; |
| 530 | } |
| 531 | |
| 532 | /** |
| 533 | * ixgbe_identify_sfp_module_generic - Identifies SFP module and assigns |
| 534 | * the PHY type. |
| 535 | * @hw: pointer to hardware structure |
| 536 | * |
| 537 | * Searches for and indentifies the SFP module. Assings appropriate PHY type. |
| 538 | **/ |
| 539 | s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) |
| 540 | { |
| 541 | s32 status = IXGBE_ERR_PHY_ADDR_INVALID; |
| 542 | u32 vendor_oui = 0; |
| 543 | u8 identifier = 0; |
| 544 | u8 comp_codes_1g = 0; |
| 545 | u8 comp_codes_10g = 0; |
| 546 | u8 oui_bytes[4] = {0, 0, 0, 0}; |
| 547 | u8 transmission_media = 0; |
| 548 | |
| 549 | status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER, |
| 550 | &identifier); |
| 551 | |
| 552 | if (status == IXGBE_ERR_SFP_NOT_PRESENT) { |
| 553 | hw->phy.sfp_type = ixgbe_sfp_type_not_present; |
| 554 | goto out; |
| 555 | } |
| 556 | |
| 557 | if (identifier == IXGBE_SFF_IDENTIFIER_SFP) { |
| 558 | hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_1GBE_COMP_CODES, |
| 559 | &comp_codes_1g); |
| 560 | hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_10GBE_COMP_CODES, |
| 561 | &comp_codes_10g); |
| 562 | hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_TRANSMISSION_MEDIA, |
| 563 | &transmission_media); |
| 564 | |
| 565 | /* ID Module |
| 566 | * ========= |
| 567 | * 0 SFP_DA_CU |
| 568 | * 1 SFP_SR |
| 569 | * 2 SFP_LR |
| 570 | */ |
| 571 | if (transmission_media & IXGBE_SFF_TWIN_AX_CAPABLE) |
| 572 | hw->phy.sfp_type = ixgbe_sfp_type_da_cu; |
| 573 | else if (comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE) |
| 574 | hw->phy.sfp_type = ixgbe_sfp_type_sr; |
| 575 | else if (comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE) |
| 576 | hw->phy.sfp_type = ixgbe_sfp_type_lr; |
| 577 | else |
| 578 | hw->phy.sfp_type = ixgbe_sfp_type_unknown; |
| 579 | |
| 580 | /* Determine PHY vendor */ |
| 581 | if (hw->phy.type == ixgbe_phy_unknown) { |
| 582 | hw->phy.id = identifier; |
| 583 | hw->phy.ops.read_i2c_eeprom(hw, |
| 584 | IXGBE_SFF_VENDOR_OUI_BYTE0, |
| 585 | &oui_bytes[0]); |
| 586 | hw->phy.ops.read_i2c_eeprom(hw, |
| 587 | IXGBE_SFF_VENDOR_OUI_BYTE1, |
| 588 | &oui_bytes[1]); |
| 589 | hw->phy.ops.read_i2c_eeprom(hw, |
| 590 | IXGBE_SFF_VENDOR_OUI_BYTE2, |
| 591 | &oui_bytes[2]); |
| 592 | |
| 593 | vendor_oui = |
| 594 | ((oui_bytes[0] << IXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT) | |
| 595 | (oui_bytes[1] << IXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT) | |
| 596 | (oui_bytes[2] << IXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT)); |
| 597 | |
| 598 | switch (vendor_oui) { |
| 599 | case IXGBE_SFF_VENDOR_OUI_TYCO: |
| 600 | if (transmission_media & |
| 601 | IXGBE_SFF_TWIN_AX_CAPABLE) |
| 602 | hw->phy.type = ixgbe_phy_tw_tyco; |
| 603 | break; |
| 604 | case IXGBE_SFF_VENDOR_OUI_FTL: |
| 605 | hw->phy.type = ixgbe_phy_sfp_ftl; |
| 606 | break; |
| 607 | case IXGBE_SFF_VENDOR_OUI_AVAGO: |
| 608 | hw->phy.type = ixgbe_phy_sfp_avago; |
| 609 | break; |
| 610 | default: |
| 611 | if (transmission_media & |
| 612 | IXGBE_SFF_TWIN_AX_CAPABLE) |
| 613 | hw->phy.type = ixgbe_phy_tw_unknown; |
| 614 | else |
| 615 | hw->phy.type = ixgbe_phy_sfp_unknown; |
| 616 | break; |
| 617 | } |
| 618 | } |
| 619 | status = 0; |
| 620 | } |
| 621 | |
| 622 | out: |
| 623 | return status; |
| 624 | } |
| 625 | |
| 626 | /** |
| 627 | * ixgbe_get_sfp_init_sequence_offsets - Checks the MAC's EEPROM to see |
| 628 | * if it supports a given SFP+ module type, if so it returns the offsets to the |
| 629 | * phy init sequence block. |
| 630 | * @hw: pointer to hardware structure |
| 631 | * @list_offset: offset to the SFP ID list |
| 632 | * @data_offset: offset to the SFP data block |
| 633 | **/ |
| 634 | s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, |
| 635 | u16 *list_offset, |
| 636 | u16 *data_offset) |
| 637 | { |
| 638 | u16 sfp_id; |
| 639 | |
| 640 | if (hw->phy.sfp_type == ixgbe_sfp_type_unknown) |
| 641 | return IXGBE_ERR_SFP_NOT_SUPPORTED; |
| 642 | |
| 643 | if (hw->phy.sfp_type == ixgbe_sfp_type_not_present) |
| 644 | return IXGBE_ERR_SFP_NOT_PRESENT; |
| 645 | |
| 646 | if ((hw->device_id == IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM) && |
| 647 | (hw->phy.sfp_type == ixgbe_sfp_type_da_cu)) |
| 648 | return IXGBE_ERR_SFP_NOT_SUPPORTED; |
| 649 | |
| 650 | /* Read offset to PHY init contents */ |
| 651 | hw->eeprom.ops.read(hw, IXGBE_PHY_INIT_OFFSET_NL, list_offset); |
| 652 | |
| 653 | if ((!*list_offset) || (*list_offset == 0xFFFF)) |
| 654 | return IXGBE_ERR_PHY; |
| 655 | |
| 656 | /* Shift offset to first ID word */ |
| 657 | (*list_offset)++; |
| 658 | |
| 659 | /* |
| 660 | * Find the matching SFP ID in the EEPROM |
| 661 | * and program the init sequence |
| 662 | */ |
| 663 | hw->eeprom.ops.read(hw, *list_offset, &sfp_id); |
| 664 | |
| 665 | while (sfp_id != IXGBE_PHY_INIT_END_NL) { |
| 666 | if (sfp_id == hw->phy.sfp_type) { |
| 667 | (*list_offset)++; |
| 668 | hw->eeprom.ops.read(hw, *list_offset, data_offset); |
| 669 | if ((!*data_offset) || (*data_offset == 0xFFFF)) { |
| 670 | hw_dbg(hw, "SFP+ module not supported\n"); |
| 671 | return IXGBE_ERR_SFP_NOT_SUPPORTED; |
| 672 | } else { |
| 673 | break; |
| 674 | } |
| 675 | } else { |
| 676 | (*list_offset) += 2; |
| 677 | if (hw->eeprom.ops.read(hw, *list_offset, &sfp_id)) |
| 678 | return IXGBE_ERR_PHY; |
| 679 | } |
| 680 | } |
| 681 | |
| 682 | if (sfp_id == IXGBE_PHY_INIT_END_NL) { |
| 683 | hw_dbg(hw, "No matching SFP+ module found\n"); |
| 684 | return IXGBE_ERR_SFP_NOT_SUPPORTED; |
| 685 | } |
| 686 | |
| 687 | return 0; |
| 688 | } |
| 689 | |
| 690 | /** |
Jesse Brandeburg | 0befdb3 | 2008-10-31 00:46:40 -0700 | [diff] [blame] | 691 | * ixgbe_check_phy_link_tnx - Determine link and speed status |
| 692 | * @hw: pointer to hardware structure |
| 693 | * |
| 694 | * Reads the VS1 register to determine if link is up and the current speed for |
| 695 | * the PHY. |
| 696 | **/ |
| 697 | s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed, |
| 698 | bool *link_up) |
| 699 | { |
| 700 | s32 status = 0; |
| 701 | u32 time_out; |
| 702 | u32 max_time_out = 10; |
| 703 | u16 phy_link = 0; |
| 704 | u16 phy_speed = 0; |
| 705 | u16 phy_data = 0; |
| 706 | |
| 707 | /* Initialize speed and link to default case */ |
| 708 | *link_up = false; |
| 709 | *speed = IXGBE_LINK_SPEED_10GB_FULL; |
| 710 | |
| 711 | /* |
| 712 | * Check current speed and link status of the PHY register. |
| 713 | * This is a vendor specific register and may have to |
| 714 | * be changed for other copper PHYs. |
| 715 | */ |
| 716 | for (time_out = 0; time_out < max_time_out; time_out++) { |
| 717 | udelay(10); |
| 718 | status = hw->phy.ops.read_reg(hw, |
| 719 | IXGBE_MDIO_VENDOR_SPECIFIC_1_STATUS, |
| 720 | IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, |
| 721 | &phy_data); |
| 722 | phy_link = phy_data & |
| 723 | IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS; |
| 724 | phy_speed = phy_data & |
| 725 | IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS; |
| 726 | if (phy_link == IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS) { |
| 727 | *link_up = true; |
| 728 | if (phy_speed == |
| 729 | IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS) |
| 730 | *speed = IXGBE_LINK_SPEED_1GB_FULL; |
| 731 | break; |
| 732 | } |
| 733 | } |
| 734 | |
| 735 | return status; |
| 736 | } |
| 737 | |
| 738 | /** |
| 739 | * ixgbe_get_phy_firmware_version_tnx - Gets the PHY Firmware Version |
| 740 | * @hw: pointer to hardware structure |
| 741 | * @firmware_version: pointer to the PHY Firmware Version |
| 742 | **/ |
| 743 | s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw, |
| 744 | u16 *firmware_version) |
| 745 | { |
| 746 | s32 status = 0; |
| 747 | |
| 748 | status = hw->phy.ops.read_reg(hw, TNX_FW_REV, |
| 749 | IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, |
| 750 | firmware_version); |
| 751 | |
| 752 | return status; |
| 753 | } |
| 754 | |