@@ -14,6 +14,43 @@ Contents
- Support
+Identifying Your Adapter
+========================
+The driver is compatible with WangXun Sapphire Dual ports Ethernet Adapters.
+
+SFP+ Devices with Pluggable Optics
+----------------------------------
+The following is a list of 3rd party SFP+ modules that have been tested and verified.
+
++----------+----------------------+----------------------+
+| Supplier | Type | Part Numbers |
++==========+======================+======================+
+| Avago | SFP+ | AFBR-709SMZ |
++----------+----------------------+----------------------+
+| F-tone | SFP+ | FTCS-851X-02D |
++----------+----------------------+----------------------+
+| Finisar | SFP+ | FTLX8574D3BCL |
++----------+----------------------+----------------------+
+| Hasense | SFP+ | AFBR-709SMZ |
++----------+----------------------+----------------------+
+| HGTECH | SFP+ | MTRS-01X11-G |
++----------+----------------------+----------------------+
+| HP | SFP+ | SR SFP+ 456096-001 |
++----------+----------------------+----------------------+
+| Huawei | SFP+ | AFBR-709SMZ |
++----------+----------------------+----------------------+
+| Intel | SFP+ | FTLX8571D3BCV-IT |
++----------+----------------------+----------------------+
+| JDSU | SFP+ | PLRXPL-SC-S43 |
++----------+----------------------+----------------------+
+| SONT | SFP+ | XP-8G10-01 |
++----------+----------------------+----------------------+
+| Trixon | SFP+ | TPS-TGM3-85DCR |
++----------+----------------------+----------------------+
+| WTD | SFP+ | RTXM228-551 |
++----------+----------------------+----------------------+
+
+
Support
=======
If you got any problem, contact Wangxun support team via support@trustnetic.com
@@ -114,12 +114,13 @@ static DEFINE_MUTEX(wx_sw_sync_lock);
* Releases the SW semaphore for the specified
* function (CSR, PHY0, PHY1, EEPROM, Flash)
**/
-static void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask)
+void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask)
{
mutex_lock(&wx_sw_sync_lock);
wr32m(wxhw, WX_MNG_SWFW_SYNC, mask, 0);
mutex_unlock(&wx_sw_sync_lock);
}
+EXPORT_SYMBOL(wx_release_sw_sync);
/**
* wx_acquire_sw_sync - Acquire SW semaphore
@@ -129,7 +130,7 @@ static void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask)
* Acquires the SW semaphore for the specified
* function (CSR, PHY0, PHY1, EEPROM, Flash)
**/
-static int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask)
+int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask)
{
u32 sem = 0;
int ret = 0;
@@ -147,6 +148,7 @@ static int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask)
return ret;
}
+EXPORT_SYMBOL(wx_acquire_sw_sync);
/**
* wx_host_interface_command - Issue command to manageability block
@@ -7,6 +7,8 @@
int wx_check_flash_load(struct wx_hw *hw, u32 check_bit);
void wx_control_hw(struct wx_hw *wxhw, bool drv);
int wx_mng_present(struct wx_hw *wxhw);
+void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask);
+int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask);
int wx_host_interface_command(struct wx_hw *wxhw, u32 *buffer,
u32 length, u32 timeout, bool return_data);
int wx_read_ee_hostif(struct wx_hw *wxhw, u16 offset, u16 *data);
@@ -123,6 +123,7 @@
/************************************** MNG ********************************/
#define WX_MNG_SWFW_SYNC 0x1E008
+#define WX_MNG_SWFW_SYNC_SW_PHY BIT(0)
#define WX_MNG_SWFW_SYNC_SW_MB BIT(2)
#define WX_MNG_SWFW_SYNC_SW_FLASH BIT(3)
#define WX_MNG_MBOX 0x1E100
@@ -14,6 +14,375 @@
#include "txgbe_hw.h"
#include "txgbe.h"
+static u32 txgbe_rd32_epcs(struct txgbe_hw *hw, u32 addr)
+{
+ struct wx_hw *wxhw = &hw->wxhw;
+ u32 offset;
+
+ /* Set the LAN port indicator to offset[1] */
+ /* 1st, write the offset to IDA_ADDR register */
+ offset = TXGBE_XPCS_IDA_ADDR;
+ wr32(wxhw, offset, addr);
+
+ /* 2nd, read the data from IDA_DATA register */
+ offset = TXGBE_XPCS_IDA_DATA;
+ return rd32(wxhw, offset);
+}
+
+static void txgbe_init_i2c(struct txgbe_hw *hw)
+{
+ struct wx_hw *wxhw = &hw->wxhw;
+
+ wr32(wxhw, TXGBE_I2C_ENABLE, 0);
+
+ wr32(wxhw, TXGBE_I2C_CON,
+ (TXGBE_I2C_CON_MASTER_MODE |
+ TXGBE_I2C_CON_SPEED(1) |
+ TXGBE_I2C_CON_RESTART_EN |
+ TXGBE_I2C_CON_SLAVE_DISABLE));
+ /* Default addr is 0xA0 ,bit 0 is configure for read/write! */
+ wr32(wxhw, TXGBE_I2C_TAR, TXGBE_I2C_SLAVE_ADDR);
+ wr32(wxhw, TXGBE_I2C_SS_SCL_HCNT, 600);
+ wr32(wxhw, TXGBE_I2C_SS_SCL_LCNT, 600);
+ wr32(wxhw, TXGBE_I2C_RX_TL, 0); /* 1byte for rx full signal */
+ wr32(wxhw, TXGBE_I2C_TX_TL, 4);
+ wr32(wxhw, TXGBE_I2C_SCL_STUCK_TIMEOUT, 0xFFFFFF);
+ wr32(wxhw, TXGBE_I2C_SDA_STUCK_TIMEOUT, 0xFFFFFF);
+
+ wr32(wxhw, TXGBE_I2C_INTR_MASK, 0);
+ wr32(wxhw, TXGBE_I2C_ENABLE, 1);
+}
+
+/**
+ * txgbe_read_i2c_byte_int - Reads 8 bit word over I2C
+ * @hw: pointer to hardware structure
+ * @byte_offset: byte offset to read
+ * @data: value read
+ *
+ * Performs byte read operation to SFP module's EEPROM over I2C interface at
+ * a specified device address.
+ **/
+static int txgbe_read_i2c_byte_int(struct txgbe_hw *hw, u8 byte_offset, u8 *data)
+{
+ u32 sync = WX_MNG_SWFW_SYNC_SW_PHY;
+ struct wx_hw *wxhw = &hw->wxhw;
+ int status = 0;
+ u32 val;
+
+ status = wx_acquire_sw_sync(wxhw, sync);
+ if (status != 0)
+ return status;
+
+ /* wait tx empty */
+ status = read_poll_timeout(rd32, val,
+ (val & TXGBE_I2C_INTR_STAT_TEMP) == TXGBE_I2C_INTR_STAT_TEMP,
+ 100, 1000, false, wxhw, TXGBE_I2C_RAW_INTR_STAT);
+ if (status != 0)
+ goto out;
+
+ /* read data */
+ wr32(wxhw, TXGBE_I2C_DATA_CMD, byte_offset | TXGBE_I2C_DATA_CMD_STOP);
+ wr32(wxhw, TXGBE_I2C_DATA_CMD, TXGBE_I2C_DATA_CMD_READ);
+
+ /* wait for read complete */
+ status = read_poll_timeout(rd32, val,
+ (val & TXGBE_I2C_INTR_STAT_RFUL) == TXGBE_I2C_INTR_STAT_RFUL,
+ 100, 1000, false, wxhw, TXGBE_I2C_RAW_INTR_STAT);
+ if (status != 0)
+ goto out;
+
+ *data = 0xFF & rd32(wxhw, TXGBE_I2C_DATA_CMD);
+
+out:
+ wx_release_sw_sync(wxhw, sync);
+ return status;
+}
+
+/**
+ * txgbe_switch_i2c_slave_addr - Switch I2C slave address
+ * @hw: pointer to hardware structure
+ * @dev_addr: slave addr to switch
+ **/
+static void txgbe_switch_i2c_slave_addr(struct txgbe_hw *hw, u8 dev_addr)
+{
+ struct wx_hw *wxhw = &hw->wxhw;
+
+ wr32(wxhw, TXGBE_I2C_ENABLE, 0);
+ wr32(wxhw, TXGBE_I2C_TAR, dev_addr >> 1);
+ wr32(wxhw, TXGBE_I2C_ENABLE, 1);
+}
+
+/**
+ * txgbe_read_i2c_byte - Reads 8 bit word over I2C
+ * @hw: pointer to hardware structure
+ * @byte_offset: byte offset to read
+ * @dev_addr: device address
+ * @data: value read
+ *
+ * Performs byte read operation to SFP module's EEPROM over I2C interface at
+ * a specified device address.
+ **/
+static int txgbe_read_i2c_byte(struct txgbe_hw *hw, u8 byte_offset,
+ u8 dev_addr, u8 *data)
+{
+ txgbe_switch_i2c_slave_addr(hw, dev_addr);
+
+ return txgbe_read_i2c_byte_int(hw, byte_offset, data);
+}
+
+/**
+ * txgbe_read_i2c_eeprom - Reads 8 bit EEPROM word over I2C interface
+ * @hw: pointer to hardware structure
+ * @byte_offset: EEPROM byte offset to read
+ * @eeprom_data: value read
+ *
+ * Performs byte read operation to SFP module's EEPROM over I2C interface.
+ **/
+static int txgbe_read_i2c_eeprom(struct txgbe_hw *hw, u8 byte_offset,
+ u8 *eeprom_data)
+{
+ return txgbe_read_i2c_byte(hw, byte_offset,
+ TXGBE_I2C_EEPROM_DEV_ADDR,
+ eeprom_data);
+}
+
+/**
+ * txgbe_identify_sfp_module - Identifies SFP modules
+ * @hw: pointer to hardware structure
+ *
+ * Searches for and identifies the SFP module and assigns appropriate PHY type.
+ **/
+static int txgbe_identify_sfp_module(struct txgbe_hw *hw)
+{
+ u8 oui_bytes[3] = {0, 0, 0};
+ u8 comp_codes_10g = 0;
+ u8 comp_codes_1g = 0;
+ int status = -EFAULT;
+ u32 vendor_oui = 0;
+ u8 identifier = 0;
+ u8 cable_tech = 0;
+ u8 cable_spec = 0;
+
+ /* LAN ID is needed for I2C access */
+ txgbe_init_i2c(hw);
+
+ status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_IDENTIFIER, &identifier);
+ if (status != 0)
+ goto err_read_i2c_eeprom;
+
+ if (identifier != TXGBE_SFF_IDENTIFIER_SFP) {
+ hw->phy.type = txgbe_phy_sfp_unsupported;
+ status = -ENODEV;
+ } else {
+ status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_1GBE_COMP_CODES,
+ &comp_codes_1g);
+ if (status != 0)
+ goto err_read_i2c_eeprom;
+
+ status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_10GBE_COMP_CODES,
+ &comp_codes_10g);
+ if (status != 0)
+ goto err_read_i2c_eeprom;
+
+ status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_CABLE_TECHNOLOGY,
+ &cable_tech);
+ if (status != 0)
+ goto err_read_i2c_eeprom;
+
+ /* ID Module
+ * =========
+ * 1 SFP_DA_CORE
+ * 2 SFP_SR/LR_CORE
+ * 3 SFP_act_lmt_DA_CORE
+ * 4 SFP_1g_cu_CORE
+ * 5 SFP_1g_sx_CORE
+ * 6 SFP_1g_lx_CORE
+ */
+ if (cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE) {
+ hw->phy.sfp_type = txgbe_sfp_type_da_cu_core;
+ } else if (cable_tech & TXGBE_SFF_DA_ACTIVE_CABLE) {
+ txgbe_read_i2c_eeprom(hw, TXGBE_SFF_CABLE_SPEC_COMP,
+ &cable_spec);
+ if (cable_spec & TXGBE_SFF_DA_SPEC_ACTIVE_LIMITING)
+ hw->phy.sfp_type = txgbe_sfp_type_da_act_lmt_core;
+ else
+ hw->phy.sfp_type = txgbe_sfp_type_unknown;
+ } else if (comp_codes_10g & (TXGBE_SFF_10GBASESR_CAPABLE |
+ TXGBE_SFF_10GBASELR_CAPABLE)) {
+ hw->phy.sfp_type = txgbe_sfp_type_srlr_core;
+ } else if (comp_codes_1g & TXGBE_SFF_1GBASET_CAPABLE) {
+ hw->phy.sfp_type = txgbe_sfp_type_1g_cu_core;
+ } else if (comp_codes_1g & TXGBE_SFF_1GBASESX_CAPABLE) {
+ hw->phy.sfp_type = txgbe_sfp_type_1g_sx_core;
+ } else if (comp_codes_1g & TXGBE_SFF_1GBASELX_CAPABLE) {
+ hw->phy.sfp_type = txgbe_sfp_type_1g_lx_core;
+ } else {
+ hw->phy.sfp_type = txgbe_sfp_type_unknown;
+ }
+
+ /* Determine if the SFP+ PHY is dual speed or not. */
+ hw->phy.multispeed_fiber = false;
+ if (((comp_codes_1g & TXGBE_SFF_1GBASESX_CAPABLE) &&
+ (comp_codes_10g & TXGBE_SFF_10GBASESR_CAPABLE)) ||
+ ((comp_codes_1g & TXGBE_SFF_1GBASELX_CAPABLE) &&
+ (comp_codes_10g & TXGBE_SFF_10GBASELR_CAPABLE)))
+ hw->phy.multispeed_fiber = true;
+
+ /* Determine PHY vendor */
+ status = txgbe_read_i2c_eeprom(hw,
+ TXGBE_SFF_VENDOR_OUI_BYTE0,
+ &oui_bytes[0]);
+ if (status != 0)
+ goto err_read_i2c_eeprom;
+
+ status = txgbe_read_i2c_eeprom(hw,
+ TXGBE_SFF_VENDOR_OUI_BYTE1,
+ &oui_bytes[1]);
+ if (status != 0)
+ goto err_read_i2c_eeprom;
+
+ status = txgbe_read_i2c_eeprom(hw,
+ TXGBE_SFF_VENDOR_OUI_BYTE2,
+ &oui_bytes[2]);
+ if (status != 0)
+ goto err_read_i2c_eeprom;
+
+ vendor_oui =
+ ((oui_bytes[0] << TXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT) |
+ (oui_bytes[1] << TXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT) |
+ (oui_bytes[2] << TXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT));
+
+ switch (vendor_oui) {
+ case TXGBE_SFF_VENDOR_OUI_TYCO:
+ if (cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE)
+ hw->phy.type = txgbe_phy_sfp_passive_tyco;
+ break;
+ case TXGBE_SFF_VENDOR_OUI_FTL:
+ if (cable_tech & TXGBE_SFF_DA_ACTIVE_CABLE)
+ hw->phy.type = txgbe_phy_sfp_ftl_active;
+ else
+ hw->phy.type = txgbe_phy_sfp_ftl;
+ break;
+ case TXGBE_SFF_VENDOR_OUI_AVAGO:
+ hw->phy.type = txgbe_phy_sfp_avago;
+ break;
+ case TXGBE_SFF_VENDOR_OUI_INTEL:
+ hw->phy.type = txgbe_phy_sfp_intel;
+ break;
+ default:
+ if (cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE)
+ hw->phy.type = txgbe_phy_sfp_passive_unknown;
+ else if (cable_tech & TXGBE_SFF_DA_ACTIVE_CABLE)
+ hw->phy.type = txgbe_phy_sfp_active_unknown;
+ else
+ hw->phy.type = txgbe_phy_sfp_unknown;
+ break;
+ }
+
+ /* Allow any DA cable vendor */
+ if (cable_tech & (TXGBE_SFF_DA_PASSIVE_CABLE |
+ TXGBE_SFF_DA_ACTIVE_CABLE))
+ return 0;
+
+ /* Verify supported 1G SFP modules */
+ if (comp_codes_10g == 0 &&
+ !(hw->phy.sfp_type == txgbe_sfp_type_1g_cu_core ||
+ hw->phy.sfp_type == txgbe_sfp_type_1g_lx_core ||
+ hw->phy.sfp_type == txgbe_sfp_type_1g_sx_core)) {
+ hw->phy.type = txgbe_phy_sfp_unsupported;
+ return -ENODEV;
+ }
+ }
+
+ return status;
+
+err_read_i2c_eeprom:
+ hw->phy.sfp_type = txgbe_sfp_type_not_present;
+ hw->phy.type = txgbe_phy_unknown;
+
+ return -ENODEV;
+}
+
+/**
+ * txgbe_get_media_type - Get media type
+ * @hw: pointer to hardware structure
+ *
+ * Returns the media type (fiber, copper, backplane)
+ **/
+static enum txgbe_media_type txgbe_get_media_type(struct txgbe_hw *hw)
+{
+ enum txgbe_media_type media_type;
+ struct wx_hw *wxhw = &hw->wxhw;
+ u8 device_type;
+
+ device_type = wxhw->subsystem_device_id & 0xF0;
+ switch (device_type) {
+ case TXGBE_ID_MAC_XAUI:
+ case TXGBE_ID_MAC_SGMII:
+ case TXGBE_ID_KR_KX_KX4:
+ /* Default device ID is mezzanine card KX/KX4 */
+ media_type = txgbe_media_type_backplane;
+ break;
+ case TXGBE_ID_SFP:
+ media_type = txgbe_media_type_fiber;
+ break;
+ case TXGBE_ID_XAUI:
+ case TXGBE_ID_SGMII:
+ media_type = txgbe_media_type_copper;
+ break;
+ case TXGBE_ID_SFI_XAUI:
+ if (wxhw->bus.func == 0)
+ media_type = txgbe_media_type_fiber;
+ else
+ media_type = txgbe_media_type_copper;
+ break;
+ default:
+ media_type = txgbe_media_type_unknown;
+ break;
+ }
+
+ return media_type;
+}
+
+/**
+ * txgbe_identify_phy - Get physical layer module
+ * @hw: pointer to hardware structure
+ *
+ * Determines the physical layer module found on the current adapter.
+ * If PHY already detected, maintains current PHY type in hw struct,
+ * otherwise executes the PHY detection routine.
+ **/
+static int txgbe_identify_phy(struct txgbe_hw *hw)
+{
+ int status;
+
+ /* Detect PHY if not unknown - returns success if already detected. */
+ hw->phy.media_type = txgbe_get_media_type(hw);
+ if (hw->phy.media_type == txgbe_media_type_fiber) {
+ status = txgbe_identify_sfp_module(hw);
+ } else {
+ hw->phy.type = txgbe_phy_none;
+ status = 0;
+ }
+
+ return status;
+}
+
+/**
+ * txgbe_init_phy - PHY/SFP specific init
+ * @hw: pointer to hardware structure
+ **/
+static int txgbe_init_phy(struct txgbe_hw *hw)
+{
+ int ret_val = 0;
+
+ txgbe_init_i2c(hw);
+ /* Identify the PHY or SFP module */
+ ret_val = txgbe_identify_phy(hw);
+
+ return ret_val;
+}
+
/**
* txgbe_init_thermal_sensor_thresh - Inits thermal sensor thresholds
* @hw: pointer to hardware structure
@@ -260,6 +629,7 @@ static void txgbe_reset_misc(struct txgbe_hw *hw)
{
struct wx_hw *wxhw = &hw->wxhw;
+ txgbe_init_i2c(hw);
wx_reset_misc(wxhw);
txgbe_init_thermal_sensor_thresh(hw);
}
@@ -277,11 +647,30 @@ int txgbe_reset_hw(struct txgbe_hw *hw)
struct wx_hw *wxhw = &hw->wxhw;
int status;
+ u32 sr_pcs_ctl, sr_pma_mmd_ctl1, sr_an_mmd_ctl, sr_an_mmd_adv_reg2;
+ u32 vr_xs_or_pcs_mmd_digi_ctl1, curr_vr_xs_or_pcs_mmd_digi_ctl1;
+ u32 curr_sr_an_mmd_ctl, curr_sr_an_mmd_adv_reg2;
+ u32 curr_sr_pcs_ctl, curr_sr_pma_mmd_ctl1;
+
/* Call adapter stop to disable tx/rx and clear interrupts */
status = wx_stop_adapter(wxhw);
if (status != 0)
return status;
+ /* Identify PHY and related function pointers */
+ status = txgbe_init_phy(hw);
+ if (status != 0 && hw->phy.type == txgbe_phy_sfp_unsupported)
+ return status;
+
+ /* Remember internal phy regs from before we reset */
+ curr_sr_pcs_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_PCS_CTL2);
+ curr_sr_pma_mmd_ctl1 = txgbe_rd32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1);
+ curr_sr_an_mmd_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_AN_MMD_CTL);
+ curr_sr_an_mmd_adv_reg2 = txgbe_rd32_epcs(hw,
+ TXGBE_SR_AN_MMD_ADV_REG2);
+ curr_vr_xs_or_pcs_mmd_digi_ctl1 =
+ txgbe_rd32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
+
if (!(((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) ||
((wxhw->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP)))
wx_reset_hostif(wxhw);
@@ -294,6 +683,38 @@ int txgbe_reset_hw(struct txgbe_hw *hw)
txgbe_reset_misc(hw);
+ /* Store the original values if they have not been stored
+ * off yet. Otherwise restore the stored original values
+ * since the reset operation sets back to defaults.
+ */
+ sr_pcs_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_PCS_CTL2);
+ sr_pma_mmd_ctl1 = txgbe_rd32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1);
+ sr_an_mmd_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_AN_MMD_CTL);
+ sr_an_mmd_adv_reg2 = txgbe_rd32_epcs(hw, TXGBE_SR_AN_MMD_ADV_REG2);
+ vr_xs_or_pcs_mmd_digi_ctl1 =
+ txgbe_rd32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
+
+ if (!hw->phy.orig_link_settings_stored) {
+ hw->phy.orig_sr_pcs_ctl2 = sr_pcs_ctl;
+ hw->phy.orig_sr_pma_mmd_ctl1 = sr_pma_mmd_ctl1;
+ hw->phy.orig_sr_an_mmd_ctl = sr_an_mmd_ctl;
+ hw->phy.orig_sr_an_mmd_adv_reg2 = sr_an_mmd_adv_reg2;
+ hw->phy.orig_vr_xs_or_pcs_mmd_digi_ctl1 =
+ vr_xs_or_pcs_mmd_digi_ctl1;
+ hw->phy.orig_link_settings_stored = true;
+ } else {
+ hw->phy.orig_sr_pcs_ctl2 = curr_sr_pcs_ctl;
+ hw->phy.orig_sr_pma_mmd_ctl1 = curr_sr_pma_mmd_ctl1;
+ hw->phy.orig_sr_an_mmd_ctl = curr_sr_an_mmd_ctl;
+ hw->phy.orig_sr_an_mmd_adv_reg2 =
+ curr_sr_an_mmd_adv_reg2;
+ hw->phy.orig_vr_xs_or_pcs_mmd_digi_ctl1 =
+ curr_vr_xs_or_pcs_mmd_digi_ctl1;
+ }
+
+ /*make sure phy power is up*/
+ msleep(100);
+
/* Store the permanent mac address */
wx_get_mac_addr(wxhw, wxhw->mac.perm_addr);
@@ -174,7 +174,9 @@ static void txgbe_reset(struct txgbe_adapter *adapter)
int err;
err = txgbe_reset_hw(hw);
- if (err != 0)
+ if (err != 0 &&
+ hw->phy.type != txgbe_phy_sfp_unsupported &&
+ hw->phy.sfp_type != txgbe_sfp_type_not_present)
dev_err(&adapter->pdev->dev, "Hardware Error: %d\n", err);
/* do not flush user set addresses */
@@ -487,9 +489,19 @@ static int txgbe_probe(struct pci_dev *pdev,
}
err = txgbe_reset_hw(hw);
- if (err) {
- dev_err(&pdev->dev, "HW Init failed: %d\n", err);
- goto err_free_mac_table;
+ if (err != 0) {
+ if (hw->phy.sfp_type == txgbe_sfp_type_not_present) {
+ err = 0;
+ } else if (hw->phy.type == txgbe_phy_sfp_unsupported) {
+ dev_err(&pdev->dev,
+ "An unsupported SFP+ module type was detected.\n");
+ dev_err(&pdev->dev,
+ "Reload the driver after installing a supported module.\n");
+ goto err_free_mac_table;
+ } else {
+ dev_err(&pdev->dev, "HW Init failed: %d\n", err);
+ goto err_free_mac_table;
+ }
}
netdev->features |= NETIF_F_HIGHDMA;
@@ -568,6 +580,15 @@ static int txgbe_probe(struct pci_dev *pdev,
err = txgbe_read_pba_string(hw, part_str, TXGBE_PBANUM_LENGTH);
if (err)
strncpy(part_str, "Unknown", TXGBE_PBANUM_LENGTH);
+ if (hw->phy.media_type == txgbe_media_type_fiber &&
+ hw->phy.sfp_type != txgbe_sfp_type_not_present)
+ netif_info(adapter, probe, netdev,
+ "PHY: %d, SFP+: %d, PBA No: %s\n",
+ hw->phy.type, hw->phy.sfp_type, part_str);
+ else
+ netif_info(adapter, probe, netdev,
+ "PHY: %d, PBA No: %s\n",
+ hw->phy.type, part_str);
netif_info(adapter, probe, netdev, "%pM\n", netdev->dev_addr);
@@ -53,6 +53,72 @@
#define TXGBE_TS_CTL 0x10300
#define TXGBE_TS_CTL_EVAL_MD BIT(31)
+/*********************** ETH PHY ***********************/
+#define TXGBE_XPCS_IDA_ADDR 0x13000
+#define TXGBE_XPCS_IDA_DATA 0x13004
+/* ETH PHY Registers */
+#define TXGBE_SR_PCS_CTL2 0x30007
+#define TXGBE_SR_PMA_MMD_CTL1 0x10000
+#define TXGBE_SR_AN_MMD_CTL 0x70000
+#define TXGBE_SR_AN_MMD_ADV_REG2 0x70011
+#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1 0x38000
+/* I2C registers */
+#define TXGBE_I2C_CON 0x14900 /* I2C Control */
+#define TXGBE_I2C_CON_SLAVE_DISABLE BIT(6)
+#define TXGBE_I2C_CON_RESTART_EN BIT(5)
+#define TXGBE_I2C_CON_SPEED(_v) (((_v) & 0x3) << 1)
+#define TXGBE_I2C_CON_MASTER_MODE BIT(0)
+#define TXGBE_I2C_TAR 0x14904 /* I2C Target Address */
+#define TXGBE_I2C_DATA_CMD 0x14910 /* I2C Rx/Tx Data Buf and Cmd */
+#define TXGBE_I2C_DATA_CMD_STOP BIT(9)
+#define TXGBE_I2C_DATA_CMD_READ (BIT(8) | TXGBE_I2C_DATA_CMD_STOP)
+#define TXGBE_I2C_SS_SCL_HCNT 0x14914
+#define TXGBE_I2C_SS_SCL_LCNT 0x14918
+#define TXGBE_I2C_INTR_MASK 0x14930 /* I2C Interrupt Mask */
+#define TXGBE_I2C_RAW_INTR_STAT 0x14934 /* I2C Raw Interrupt Status */
+#define TXGBE_I2C_INTR_STAT_RFUL BIT(2)
+#define TXGBE_I2C_INTR_STAT_TEMP BIT(4)
+#define TXGBE_I2C_RX_TL 0x14938 /* I2C Receive FIFO Threshold */
+#define TXGBE_I2C_TX_TL 0x1493C /* I2C TX FIFO Threshold */
+#define TXGBE_I2C_ENABLE 0x1496C /* I2C Enable */
+#define TXGBE_I2C_SCL_STUCK_TIMEOUT 0x149AC
+#define TXGBE_I2C_SDA_STUCK_TIMEOUT 0x149B0
+
+#define TXGBE_I2C_SLAVE_ADDR (0xA0 >> 1)
+#define TXGBE_I2C_EEPROM_DEV_ADDR 0xA0
+
+/* EEPROM byte offsets */
+#define TXGBE_SFF_IDENTIFIER 0x0
+#define TXGBE_SFF_IDENTIFIER_SFP 0x3
+#define TXGBE_SFF_VENDOR_OUI_BYTE0 0x25
+#define TXGBE_SFF_VENDOR_OUI_BYTE1 0x26
+#define TXGBE_SFF_VENDOR_OUI_BYTE2 0x27
+#define TXGBE_SFF_1GBE_COMP_CODES 0x6
+#define TXGBE_SFF_10GBE_COMP_CODES 0x3
+#define TXGBE_SFF_CABLE_TECHNOLOGY 0x8
+#define TXGBE_SFF_CABLE_SPEC_COMP 0x3C
+
+/* Bitmasks */
+#define TXGBE_SFF_DA_PASSIVE_CABLE BIT(2)
+#define TXGBE_SFF_DA_ACTIVE_CABLE BIT(3)
+#define TXGBE_SFF_DA_SPEC_ACTIVE_LIMITING BIT(2)
+#define TXGBE_SFF_1GBASESX_CAPABLE BIT(0)
+#define TXGBE_SFF_1GBASELX_CAPABLE BIT(1)
+#define TXGBE_SFF_1GBASET_CAPABLE BIT(3)
+#define TXGBE_SFF_10GBASESR_CAPABLE BIT(4)
+#define TXGBE_SFF_10GBASELR_CAPABLE BIT(5)
+
+/* Bit-shift macros */
+#define TXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT 24
+#define TXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT 16
+#define TXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT 8
+
+/* Vendor OUIs: format of OUI is 0x[byte0][byte1][byte2][00] */
+#define TXGBE_SFF_VENDOR_OUI_TYCO 0x00407600
+#define TXGBE_SFF_VENDOR_OUI_FTL 0x00906500
+#define TXGBE_SFF_VENDOR_OUI_AVAGO 0x00176A00
+#define TXGBE_SFF_VENDOR_OUI_INTEL 0x001B2100
+
/* Part Number String Length */
#define TXGBE_PBANUM_LENGTH 32
@@ -67,8 +133,66 @@
#define TXGBE_PBANUM1_PTR 0x06
#define TXGBE_PBANUM_PTR_GUARD 0xFAFA
+/* SFP+ module type IDs:
+ *
+ * ID Module Type
+ * =============
+ * 0 SFP_DA_CU
+ * 1 SFP_SR
+ * 2 SFP_LR
+ * 3 SFP_DA_CU_CORE0
+ * 4 SFP_DA_CU_CORE1
+ * 5 SFP_SR/LR_CORE0
+ * 6 SFP_SR/LR_CORE1
+ */
+enum txgbe_sfp_type {
+ txgbe_sfp_type_da_cu_core = 1,
+ txgbe_sfp_type_srlr_core = 2,
+ txgbe_sfp_type_da_act_lmt_core = 3,
+ txgbe_sfp_type_1g_cu_core = 4,
+ txgbe_sfp_type_1g_sx_core = 5,
+ txgbe_sfp_type_1g_lx_core = 6,
+ txgbe_sfp_type_not_present = 0xFFFE,
+ txgbe_sfp_type_unknown = 0xFFFF
+};
+
+enum txgbe_phy_type {
+ txgbe_phy_unknown = 0,
+ txgbe_phy_none,
+ txgbe_phy_sfp_passive_tyco,
+ txgbe_phy_sfp_passive_unknown,
+ txgbe_phy_sfp_active_unknown,
+ txgbe_phy_sfp_avago,
+ txgbe_phy_sfp_ftl,
+ txgbe_phy_sfp_ftl_active,
+ txgbe_phy_sfp_unknown,
+ txgbe_phy_sfp_intel,
+ txgbe_phy_sfp_unsupported
+};
+
+enum txgbe_media_type {
+ txgbe_media_type_unknown = 0,
+ txgbe_media_type_fiber,
+ txgbe_media_type_copper,
+ txgbe_media_type_backplane
+};
+
+struct txgbe_phy_info {
+ enum txgbe_sfp_type sfp_type;
+ enum txgbe_phy_type type;
+ enum txgbe_media_type media_type;
+ u32 orig_sr_pcs_ctl2;
+ u32 orig_sr_pma_mmd_ctl1;
+ u32 orig_sr_an_mmd_ctl;
+ u32 orig_sr_an_mmd_adv_reg2;
+ u32 orig_vr_xs_or_pcs_mmd_digi_ctl1;
+ bool orig_link_settings_stored;
+ bool multispeed_fiber;
+};
+
struct txgbe_hw {
struct wx_hw wxhw;
+ struct txgbe_phy_info phy;
};
#endif /* _TXGBE_TYPE_H_ */