From patchwork Tue Nov 8 11:19:03 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mengyuan Lou X-Patchwork-Id: 13036162 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3C1DDC4332F for ; Tue, 8 Nov 2022 11:20:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233631AbiKHLUu (ORCPT ); Tue, 8 Nov 2022 06:20:50 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60082 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233540AbiKHLUn (ORCPT ); Tue, 8 Nov 2022 06:20:43 -0500 Received: from smtpbguseast1.qq.com (smtpbguseast1.qq.com [54.204.34.129]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C09D828E32 for ; Tue, 8 Nov 2022 03:20:40 -0800 (PST) X-QQ-mid: bizesmtp85t1667906367tc0qa5d3 Received: from localhost.localdomain ( [183.129.236.74]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 08 Nov 2022 19:19:25 +0800 (CST) X-QQ-SSF: 01400000000000M0M000000A0000000 X-QQ-FEAT: CR3LFp2JE4k1HfVaDDiWOq9n3l7ziNJyw6/F1oWOXjo8HBzIKANfPYJtidg6r gFH4yWWKi1h9LBdBVUIY834cLihIrgJ6f57WcCl+OoDjwGEHM2ARh/s9WPZfqML9nPg263v EDxoM8CRQcwztZC4FgiXxDoX/SVFZDKJhPyRupeucuEo+nGsm2kGHPHOZdiECa1uHPWtmVh 6gtBdvNAMFtBWw99JjWsd5OAp1jefYukSgjXj8aSH2gBWvckwm8OG+CGVq1uuPN4ExagJx1 ZpmppJjnNZe4l1wtOiTrmoPaY8wQIjYPfu4vvFHq77BcMEphuHS7T8Isg8i4DGAK5snTdTZ gl5+ie1oQqZqvDHHfQcNlhj7d0q/Lg88eH7PotnkuBWe0Gus9fgSnWj2FDXEo7QQOrRwYIA 5rfE8TJYwTM= X-QQ-GoodBg: 2 From: Mengyuan Lou To: netdev@vger.kernel.org Cc: jiawenwu@trustnetic.com Subject: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module Date: Tue, 8 Nov 2022 19:19:03 +0800 Message-Id: <20221108111907.48599-2-mengyuanlou@net-swift.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221108111907.48599-1-mengyuanlou@net-swift.com> References: <20221108111907.48599-1-mengyuanlou@net-swift.com> MIME-Version: 1.0 X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:net-swift.com:qybglogicsvr:qybglogicsvr1 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Jiawen Wu Add to get media type and physical layer module, support I2C access. Signed-off-by: Jiawen Wu --- .../device_drivers/ethernet/wangxun/txgbe.rst | 37 ++ drivers/net/ethernet/wangxun/libwx/wx_hw.c | 6 +- drivers/net/ethernet/wangxun/libwx/wx_hw.h | 2 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 1 + drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 421 ++++++++++++++++++ .../net/ethernet/wangxun/txgbe/txgbe_main.c | 29 +- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 124 ++++++ 7 files changed, 614 insertions(+), 6 deletions(-) diff --git a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst index eaa87dbe8848..3cb9549fb7b0 100644 --- a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst +++ b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst @@ -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 diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 1eb7388f1dd5..045d6e978598 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -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 diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h index a0652f5e9939..5058774381c1 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -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); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 1cbeef8230bf..c95cda53bf67 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -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 diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index 0b1032195859..77a44e48fc9e 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -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); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 36780e7f05b7..1c00ecbc1c6a 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -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); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 740a1c447e20..2f8be0118157 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -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_ */ From patchwork Tue Nov 8 11:19:04 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mengyuan Lou X-Patchwork-Id: 13036159 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E3510C433FE for ; Tue, 8 Nov 2022 11:19:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233873AbiKHLTk (ORCPT ); Tue, 8 Nov 2022 06:19:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58986 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233792AbiKHLTj (ORCPT ); Tue, 8 Nov 2022 06:19:39 -0500 Received: from smtpbgjp3.qq.com (smtpbgjp3.qq.com [54.92.39.34]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3B403F588 for ; Tue, 8 Nov 2022 03:19:37 -0800 (PST) X-QQ-mid: bizesmtp85t1667906372tk0m9kdr Received: from localhost.localdomain ( [183.129.236.74]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 08 Nov 2022 19:19:30 +0800 (CST) X-QQ-SSF: 01400000000000M0M000000A0000000 X-QQ-FEAT: ELPc6+jLxfaPqEXvWKS/nQq4Em1YaoB2TKbok51cVqNeC6fDrnCXWbj7LoheX eDP5s1B+zQTqoy9YJrZXZIxAaAnPpzOYZMar55I5G1K1rpptQ9/V2Bx2YnLURjmyCKNX9+8 Z4+2YhZODgKvLWS4ic0++daSqo4OoKNbe+M4kJt+J4QF6X3+ClwyBvX5UrzYnJt3VtLeHnP /lP1YGJrOtVIrDYA1Hi74DRQqU1EHwVwihXfS3wfYVsd3kQOY8i3Bsf7TBABPV63QEGNT0u BxegKfUD4c3+dw2onYsyK1g4Rzo2wDgAa3JHoSTRETPF8o0NH5Pwwa2r4Hu7duGHN/bRzgF B6foEVZm8Ot9QedmHEJ+ge2uN9FPxgVnq6l3ioB4+1p05L0/V9OkrrvpGy25CsbFYv8o3wx u4FCySSyDNf7tBtQNaPg0Q== X-QQ-GoodBg: 2 From: Mengyuan Lou To: netdev@vger.kernel.org Cc: jiawenwu@trustnetic.com Subject: [PATCH net-next 2/5] net: txgbe: Initialize service task Date: Tue, 8 Nov 2022 19:19:04 +0800 Message-Id: <20221108111907.48599-3-mengyuanlou@net-swift.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221108111907.48599-1-mengyuanlou@net-swift.com> References: <20221108111907.48599-1-mengyuanlou@net-swift.com> MIME-Version: 1.0 X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:net-swift.com:qybglogicsvr:qybglogicsvr1 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Jiawen Wu Setup work queue, and initialize service task to process the following tasks. Signed-off-by: Jiawen Wu --- drivers/net/ethernet/wangxun/txgbe/txgbe.h | 15 +++ .../net/ethernet/wangxun/txgbe/txgbe_main.c | 113 +++++++++++++++++- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe.h b/drivers/net/ethernet/wangxun/txgbe/txgbe.h index 19e61377bd00..fb8fd413b755 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe.h @@ -24,6 +24,17 @@ struct txgbe_mac_addr { #define TXGBE_MAC_STATE_MODIFIED 0x2 #define TXGBE_MAC_STATE_IN_USE 0x4 +enum txgbe_state_t { + __TXGBE_TESTING, + __TXGBE_RESETTING, + __TXGBE_DOWN, + __TXGBE_HANGING, + __TXGBE_DISABLED, + __TXGBE_REMOVING, + __TXGBE_SERVICE_SCHED, + __TXGBE_SERVICE_INITED, +}; + /* board specific private data structure */ struct txgbe_adapter { u8 __iomem *io_addr; /* Mainly for iounmap use */ @@ -31,6 +42,10 @@ struct txgbe_adapter { struct net_device *netdev; struct pci_dev *pdev; + unsigned long state; + struct timer_list service_timer; + struct work_struct service_task; + /* structs defined in txgbe_type.h */ struct txgbe_hw hw; u16 msg_enable; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 1c00ecbc1c6a..cb86c001baa6 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -35,6 +35,8 @@ static const struct pci_device_id txgbe_pci_tbl[] = { #define DEFAULT_DEBUG_LEVEL_SHIFT 3 +static struct workqueue_struct *txgbe_wq; + static void txgbe_check_minimum_link(struct txgbe_adapter *adapter) { struct pci_dev *pdev; @@ -73,6 +75,50 @@ static int txgbe_enumerate_functions(struct txgbe_adapter *adapter) return physfns; } +static void txgbe_service_event_schedule(struct txgbe_adapter *adapter) +{ + if (!test_bit(__TXGBE_DOWN, &adapter->state) && + !test_bit(__TXGBE_REMOVING, &adapter->state) && + !test_and_set_bit(__TXGBE_SERVICE_SCHED, &adapter->state)) + queue_work(txgbe_wq, &adapter->service_task); +} + +static void txgbe_service_event_complete(struct txgbe_adapter *adapter) +{ + if (WARN_ON(!test_bit(__TXGBE_SERVICE_SCHED, &adapter->state))) + return; + + /* flush memory to make sure state is correct before next watchdog */ + smp_mb__before_atomic(); + clear_bit(__TXGBE_SERVICE_SCHED, &adapter->state); +} + +static void txgbe_service_timer(struct timer_list *t) +{ + struct txgbe_adapter *adapter = from_timer(adapter, t, service_timer); + unsigned long next_event_offset; + + next_event_offset = HZ * 2; + + /* Reset the timer */ + mod_timer(&adapter->service_timer, next_event_offset + jiffies); + + txgbe_service_event_schedule(adapter); +} + +/** + * txgbe_service_task - manages and runs subtasks + * @work: pointer to work_struct containing our data + **/ +static void txgbe_service_task(struct work_struct *work) +{ + struct txgbe_adapter *adapter = container_of(work, + struct txgbe_adapter, + service_task); + + txgbe_service_event_complete(adapter); +} + static void txgbe_sync_mac_table(struct txgbe_adapter *adapter) { struct txgbe_hw *hw = &adapter->hw; @@ -190,6 +236,10 @@ static void txgbe_disable_device(struct txgbe_adapter *adapter) struct net_device *netdev = adapter->netdev; struct wx_hw *wxhw = &adapter->hw.wxhw; + /* signal that we are down to the interrupt handler */ + if (test_and_set_bit(__TXGBE_DOWN, &adapter->state)) + return; /* do nothing if already down */ + wx_disable_pcie_master(wxhw); /* disable receives */ wx_disable_rx(wxhw); @@ -197,6 +247,8 @@ static void txgbe_disable_device(struct txgbe_adapter *adapter) netif_carrier_off(netdev); netif_tx_disable(netdev); + del_timer_sync(&adapter->service_timer); + if (wxhw->bus.func < 2) wr32m(wxhw, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wxhw->bus.func), 0); else @@ -266,6 +318,8 @@ static int txgbe_sw_init(struct txgbe_adapter *adapter) return -ENOMEM; } + set_bit(__TXGBE_DOWN, &adapter->state); + return 0; } @@ -336,7 +390,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) wx_control_hw(wxhw, false); - pci_disable_device(pdev); + if (!test_and_set_bit(__TXGBE_DISABLED, &adapter->state)) + pci_disable_device(pdev); } static void txgbe_shutdown(struct pci_dev *pdev) @@ -410,6 +465,7 @@ static int txgbe_probe(struct pci_dev *pdev, struct txgbe_hw *hw = NULL; struct wx_hw *wxhw = NULL; struct net_device *netdev; + bool disable_dev = false; int err, expected_gts; u16 eeprom_verh = 0, eeprom_verl = 0, offset = 0; @@ -468,6 +524,7 @@ static int txgbe_probe(struct pci_dev *pdev, } netdev->netdev_ops = &txgbe_netdev_ops; + netdev->watchdog_timeo = 5 * HZ; /* setup the private structure */ err = txgbe_sw_init(adapter); @@ -518,6 +575,11 @@ static int txgbe_probe(struct pci_dev *pdev, eth_hw_addr_set(netdev, wxhw->mac.perm_addr); txgbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr); + timer_setup(&adapter->service_timer, txgbe_service_timer, 0); + INIT_WORK(&adapter->service_task, txgbe_service_task); + set_bit(__TXGBE_SERVICE_INITED, &adapter->state); + clear_bit(__TXGBE_SERVICE_SCHED, &adapter->state); + /* Save off EEPROM version number and Option Rom version which * together make a unique identify for the eeprom */ @@ -599,11 +661,13 @@ static int txgbe_probe(struct pci_dev *pdev, err_free_mac_table: kfree(adapter->mac_table); err_pci_release_regions: + disable_dev = !test_and_set_bit(__TXGBE_DISABLED, &adapter->state); pci_disable_pcie_error_reporting(pdev); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); err_pci_disable_dev: - pci_disable_device(pdev); + if (!adapter || disable_dev) + pci_disable_device(pdev); return err; } @@ -620,18 +684,25 @@ static void txgbe_remove(struct pci_dev *pdev) { struct txgbe_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev; + bool disable_dev; netdev = adapter->netdev; + + set_bit(__TXGBE_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + unregister_netdev(netdev); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); kfree(adapter->mac_table); + disable_dev = !test_and_set_bit(__TXGBE_DISABLED, &adapter->state); pci_disable_pcie_error_reporting(pdev); - pci_disable_device(pdev); + if (disable_dev) + pci_disable_device(pdev); } static struct pci_driver txgbe_driver = { @@ -642,7 +713,41 @@ static struct pci_driver txgbe_driver = { .shutdown = txgbe_shutdown, }; -module_pci_driver(txgbe_driver); +/** + * txgbe_init_module - Driver Registration Routine + * + * txgbe_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + **/ +static int __init txgbe_init_module(void) +{ + int ret; + + txgbe_wq = create_singlethread_workqueue(txgbe_driver_name); + if (!txgbe_wq) { + pr_err("%s: Failed to create workqueue\n", txgbe_driver_name); + return -ENOMEM; + } + + ret = pci_register_driver(&txgbe_driver); + return ret; +} + +module_init(txgbe_init_module); + +/** + * txgbe_exit_module - Driver Exit Cleanup Routine + * + * txgbe_exit_module is called just before the driver is removed + * from memory. + **/ +static void __exit txgbe_exit_module(void) +{ + pci_unregister_driver(&txgbe_driver); + destroy_workqueue(txgbe_wq); +} + +module_exit(txgbe_exit_module); MODULE_DEVICE_TABLE(pci, txgbe_pci_tbl); MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, "); From patchwork Tue Nov 8 11:19:05 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mengyuan Lou X-Patchwork-Id: 13036160 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8AAFCC4332F for ; Tue, 8 Nov 2022 11:19:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233900AbiKHLTu (ORCPT ); Tue, 8 Nov 2022 06:19:50 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59074 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234014AbiKHLTs (ORCPT ); Tue, 8 Nov 2022 06:19:48 -0500 Received: from smtpbguseast3.qq.com (smtpbguseast3.qq.com [54.243.244.52]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B5CC9275CE for ; Tue, 8 Nov 2022 03:19:42 -0800 (PST) X-QQ-mid: bizesmtp85t1667906376tc0mugfp Received: from localhost.localdomain ( [183.129.236.74]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 08 Nov 2022 19:19:34 +0800 (CST) X-QQ-SSF: 01400000000000M0M000000A0000000 X-QQ-FEAT: ElntjVByhgXghUpQiFTCp8EuGNPoYH9R+hPkaH7t+z5RK2hU3E3ahANPZAiEK 4T4Ib0ZCFT8K8tSW44aQKqvmackND8XPfp3Pv4rnHA9RD2Hj0OxjhHAPK1VOJdlJxNPYofU kvGm0qJLVeV2y5avly/zRM4A0tCCiQkuEhv5ZauqHKrMab3vlcLPUaDXQ8Mu3SgbZi6j9aE uU2G7DBP2vJR2ZQEqqBG+MDB0t8aZVJW34ar3C5uOFKPhvvnf9TBplcKZA6ZSkUsOEl2DC7 8JVzH1lZIU3oPp/AZSos7jSf3pVWKsBT16Y2AheXxMRM1HZMvvIxWLejQg4y9Ml0M9DBFmX j9nmV79qfer15Q7nc7Q5mIdeTR7glFuPCejgLuJBDGoHP/zQWMJxksA9xxYAFMtDZc5WwWo B50UdFfA2mh/wSAI5E53CA== X-QQ-GoodBg: 2 From: Mengyuan Lou To: netdev@vger.kernel.org Cc: jiawenwu@trustnetic.com Subject: [PATCH net-next 3/5] net: txgbe: Support to setup link Date: Tue, 8 Nov 2022 19:19:05 +0800 Message-Id: <20221108111907.48599-4-mengyuanlou@net-swift.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221108111907.48599-1-mengyuanlou@net-swift.com> References: <20221108111907.48599-1-mengyuanlou@net-swift.com> MIME-Version: 1.0 X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:net-swift.com:qybglogicsvr:qybglogicsvr1 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Jiawen Wu Get link capabilities, setup MAC and PHY link, and support to enable or disable Tx laser. Signed-off-by: Jiawen Wu --- .../device_drivers/ethernet/wangxun/txgbe.rst | 5 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 6 + drivers/net/ethernet/wangxun/txgbe/txgbe.h | 16 + drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 1171 ++++++++++++++++- drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h | 9 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 335 ++++- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 103 ++ 7 files changed, 1640 insertions(+), 5 deletions(-) diff --git a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst index 3cb9549fb7b0..a41acd88bfb6 100644 --- a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst +++ b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst @@ -50,6 +50,11 @@ The following is a list of 3rd party SFP+ modules that have been tested and veri | WTD | SFP+ | RTXM228-551 | +----------+----------------------+----------------------+ +Laser turns off for SFP+ when ifconfig ethX down +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"ifconfig ethX down" turns off the laser for SFP+ fiber adapters. +"ifconfig ethX up" turns on the laser. + Support ======= diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index c95cda53bf67..a5de626608d0 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -22,6 +22,7 @@ /* chip control Registers */ #define WX_MIS_PWR 0x10000 #define WX_MIS_RST 0x1000C +#define WX_MIS_RST_LAN_ETH_MODE(_i) BIT((_i) + 29) #define WX_MIS_RST_LAN_RST(_i) BIT((_i) + 1) #define WX_MIS_RST_SW_RST BIT(0) #define WX_MIS_ST 0x10028 @@ -63,6 +64,7 @@ /************************* Port Registers ************************************/ /* port cfg Registers */ #define WX_CFG_PORT_CTL 0x14400 +#define WX_CFG_PORT_CTL_PFRSTD BIT(14) /* Phy Function Reset Done */ #define WX_CFG_PORT_CTL_DRV_LOAD BIT(3) /*********************** Transmit DMA registers **************************/ @@ -134,11 +136,15 @@ /************************************* ETH MAC *****************************/ #define WX_MAC_TX_CFG 0x11000 #define WX_MAC_TX_CFG_TE BIT(0) +#define WX_MAC_TX_CFG_SPEED_MASK (0x3 << 29) +#define WX_MAC_TX_CFG_SPEED_10G (0x0 << 29) +#define WX_MAC_TX_CFG_SPEED_1G (0x3 << 29) #define WX_MAC_RX_CFG 0x11004 #define WX_MAC_RX_CFG_RE BIT(0) #define WX_MAC_RX_CFG_JE BIT(8) #define WX_MAC_PKT_FLT 0x11008 #define WX_MAC_PKT_FLT_PR BIT(0) /* promiscuous mode */ +#define WX_MAC_WDG_TIMEOUT 0x1100C #define WX_MAC_RX_FLOW_CTRL 0x11090 #define WX_MAC_RX_FLOW_CTRL_RFE BIT(0) /* receive fc enable */ #define WX_MMC_CONTROL 0x11800 diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe.h b/drivers/net/ethernet/wangxun/txgbe/txgbe.h index fb8fd413b755..021c348a2784 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe.h @@ -33,8 +33,17 @@ enum txgbe_state_t { __TXGBE_REMOVING, __TXGBE_SERVICE_SCHED, __TXGBE_SERVICE_INITED, + __TXGBE_IN_SFP_INIT, }; +#define TXGBE_TRY_LINK_TIMEOUT (4 * HZ) /* trying for four seconds */ +#define TXGBE_SFP_POLL_JIFFIES (2 * HZ) /* SFP poll every 2 seconds */ + +/* txgbe_adapter.flag */ +#define TXGBE_FLAG_NEED_LINK_UPDATE BIT(0) +#define TXGBE_FLAG_NEED_LINK_CONFIG BIT(1) +#define TXGBE_FLAG_SFP_NEEDS_RESET BIT(2) + /* board specific private data structure */ struct txgbe_adapter { u8 __iomem *io_addr; /* Mainly for iounmap use */ @@ -46,6 +55,13 @@ struct txgbe_adapter { struct timer_list service_timer; struct work_struct service_task; + u32 flags; + + bool link_up; + u32 link_speed; + unsigned long sfp_poll_time; + unsigned long link_check_timeout; + /* structs defined in txgbe_type.h */ struct txgbe_hw hw; u16 msg_enable; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index 77a44e48fc9e..2fcc097854dc 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,36 @@ static u32 txgbe_rd32_epcs(struct txgbe_hw *hw, u32 addr) return rd32(wxhw, offset); } +static void txgbe_wr32_ephy(struct txgbe_hw *hw, u32 addr, u32 data) +{ + 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_ETHPHY_IDA_ADDR; + wr32(wxhw, offset, addr); + + /* 2nd, read the data from IDA_DATA register */ + offset = TXGBE_ETHPHY_IDA_DATA; + wr32(wxhw, offset, data); +} + +static void txgbe_wr32_epcs(struct txgbe_hw *hw, u32 addr, u32 data) +{ + 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; + wr32(wxhw, offset, data); +} + static void txgbe_init_i2c(struct txgbe_hw *hw) { struct wx_hw *wxhw = &hw->wxhw; @@ -152,7 +183,7 @@ static int txgbe_read_i2c_eeprom(struct txgbe_hw *hw, u8 byte_offset, * * Searches for and identifies the SFP module and assigns appropriate PHY type. **/ -static int txgbe_identify_sfp_module(struct txgbe_hw *hw) +int txgbe_identify_sfp_module(struct txgbe_hw *hw) { u8 oui_bytes[3] = {0, 0, 0}; u8 comp_codes_10g = 0; @@ -383,6 +414,1102 @@ static int txgbe_init_phy(struct txgbe_hw *hw) return ret_val; } +/** + * txgbe_set_hard_rate_select_speed - Set module link speed + * @hw: pointer to hardware structure + * @speed: link speed to set + * + * Set module link speed via RS0/RS1 rate select pins. + */ +static void txgbe_set_hard_rate_select_speed(struct txgbe_hw *hw, u32 speed) +{ + struct wx_hw *wxhw = &hw->wxhw; + u32 esdp_reg; + + esdp_reg = rd32(wxhw, TXGBE_GPIO_DR); + + switch (speed) { + case SPEED_10000: + esdp_reg |= TXGBE_GPIO_DR_5 | TXGBE_GPIO_DR_4; + break; + case SPEED_1000: + esdp_reg &= ~(TXGBE_GPIO_DR_5 | TXGBE_GPIO_DR_4); + break; + default: + wx_err(wxhw, "Invalid fixed module speed\n"); + return; + } + + wr32(wxhw, TXGBE_GPIO_DDR, + TXGBE_GPIO_DDR_5 | TXGBE_GPIO_DDR_4 | + TXGBE_GPIO_DDR_1 | TXGBE_GPIO_DDR_0); + + wr32(wxhw, TXGBE_GPIO_DR, esdp_reg); + + WX_WRITE_FLUSH(wxhw); +} + +static void txgbe_set_sgmii_an37_ability(struct txgbe_hw *hw) +{ + u16 sub_dev_id = hw->wxhw.subsystem_device_id & TXGBE_DEV_MASK; + u32 value; + + txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0x3002); + /* for sgmii + external phy, set to 0x0105 (phy sgmii mode) */ + /* for sgmii direct link, set to 0x010c (mac sgmii mode) */ + if (sub_dev_id == TXGBE_ID_MAC_SGMII || + hw->phy.media_type == txgbe_media_type_fiber) { + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x010c); + } else if (sub_dev_id == TXGBE_ID_SGMII || + sub_dev_id == TXGBE_ID_XAUI) { + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x0105); + } + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_DIGI_CTL, TXGBE_SR_MII_MMD_DIGI_CTL_AS); + value = txgbe_rd32_epcs(hw, TXGBE_SR_MII_MMD_CTL); + value |= TXGBE_SR_MII_MMD_CTL_AN_EN | TXGBE_SR_MII_MMD_CTL_RESTART_AN; + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_CTL, value); +} + +static void txgbe_set_link_to_kr(struct txgbe_hw *hw, bool autoneg) +{ + int status = 0; + u32 value; + + /* 1. Wait xpcs power-up good */ + status = read_poll_timeout(txgbe_rd32_epcs, value, + (value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M) == + TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG, + 10000, 1000000, false, + hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST); + if (status < 0) { + wx_err(&hw->wxhw, "xpcs power-up timeout.\n"); + return; + } + + wx_dbg(&hw->wxhw, "It is set to kr.\n"); + + txgbe_wr32_epcs(hw, TXGBE_VR_AN_INTR_MSK, + TXGBE_VR_AN_INTR_MSK_AN_PG_RCV_IE | + TXGBE_VR_AN_INTR_MSK_AN_INC_LINK_IE | + TXGBE_VR_AN_INTR_MSK_AN_INT_CMPLT_IE); + + /* 2. Disable xpcs AN-73 */ + txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, + TXGBE_SR_AN_MMD_CTL_EXT_NP_CTL | TXGBE_SR_AN_MMD_CTL_ENABLE); + txgbe_wr32_epcs(hw, TXGBE_VR_AN_KR_MODE_CL, TXGBE_VR_AN_KR_MODE_CL_PDET_EN); + + /* 3. Set VR_XS_PMA_Gen5_12G_MPLLA_CTRL3 Register */ + /* Bit[10:0](MPLLA_BANDWIDTH) = 11'd123 (default: 11'd16) */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0x7B); + + /* 4. Set VR_XS_PMA_Gen5_12G_MISC_CTRL0 Register */ + /* Bit[12:8](RX_VREF_CTRL) = 5'hF (default: 5'h11) */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00); + + /* 5. Set VR_XS_PMA_Gen5_12G_RX_EQ_CTRL0 Register */ + /* Bit[15:8](VGA1/2_GAIN_0) = 8'h77, + * Bit[7:5](CTLE_POLE_0) = 3'h2 + * Bit[4:0](CTLE_BOOST_0) = 4'hA + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, 0x774A); + + /* 6. Set VR_MII_Gen5_12G_RX_GENCTRL3 Register */ + /* Bit[2:0](LOS_TRSHLD_0) = 3'h4 (default: 3) */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3, 0x0004); + /* 7. Initialize the mode by setting VR XS or PCS MMD Digital */ + /* Control1 Register Bit[15](VR_RST) */ + txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0xA000); + /* wait phy initialization done */ + status = read_poll_timeout(txgbe_rd32_epcs, value, + !(value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST), + 100000, 10000000, false, + hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1); + if (status < 0) + wx_err(&hw->wxhw, "PHY initialization timeout.\n"); +} + +static void txgbe_set_link_to_kx4(struct txgbe_hw *hw, bool autoneg) +{ + struct wx_hw *wxhw = &hw->wxhw; + int status = 0; + u32 value, i; + + /* check link status, if already set, skip setting it again */ + if (hw->link_status == TXGBE_LINK_STATUS_KX4) + return; + + wx_dbg(wxhw, "It is set to kx4.\n"); + + /* Wait xpcs power-up good */ + status = read_poll_timeout(txgbe_rd32_epcs, value, + (value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M) == + TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG, + 10000, 1000000, false, + hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST); + if (status < 0) { + wx_err(wxhw, "xpcs power-up timeout.\n"); + return; + } + + wr32m(wxhw, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0); + + /* Disable xpcs AN-73 */ + if (!autoneg) + txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, 0); + else + txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, + TXGBE_SR_AN_MMD_CTL_EXT_NP_CTL | TXGBE_SR_AN_MMD_CTL_ENABLE); + + /* Disable PHY MPLLA for eth mode change(after ECO) */ + txgbe_wr32_ephy(hw, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x250A); + WX_WRITE_FLUSH(wxhw); + msleep(20); + + /* Set the eth change_mode bit first in mis_rst register + * for corresponding LAN port + */ + wr32(wxhw, WX_MIS_RST, WX_MIS_RST_LAN_ETH_MODE(wxhw->bus.func)); + + /* Set SR PCS Control2 Register Bits[1:0] = 2'b01 + * PCS_TYPE_SEL: non KR + */ + txgbe_wr32_epcs(hw, TXGBE_SR_PCS_CTL2, + TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X); + /* Set SR PMA MMD Control1 Register Bit[13] = 1'b1 SS13: 10G speed */ + txgbe_wr32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1, + TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_10G); + + value = (0xf5f0 & ~0x7F0) | (0x5 << 8) | (0x7 << 5) | 0xF0; + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GENCTRL1, value); + + if ((wxhw->subsystem_device_id & TXGBE_DEV_MASK) == TXGBE_ID_MAC_XAUI) + txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00); + else + txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0x4F00); + + for (i = 0; i < 4; i++) { + if (i == 0) + value = (0x45 & ~0xFFFF) | (0x7 << 12) | (0x7 << 8) | 0x6; + else + value = (0xff06 & ~0xFFFF) | (0x7 << 12) | (0x7 << 8) | 0x6; + + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0 + i, value); + } + + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0, 0x0); + + txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0x0); + + value = (0x6db & ~0xFFF) | (0x1 << 9) | (0x1 << 6) | (0x1 << 3) | 0x1; + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3, value); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA */ + /* Control 0 Register Bit[7:0] = 8'd40 MPLLA_MULTIPLIER */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL0, 0x28); + + /* Set VR XS, PMA or MII Synopsys Enterprise Gen5 12G PHY MPLLA */ + /* Control 3 Register Bit[10:0] = 11'd86 MPLLA_BANDWIDTH */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0x56); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */ + /* Calibration Load 0 Register Bit[12:0] = 13'd1360 VCO_LD_VAL_0 */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD0, 0x550); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */ + /* Calibration Load 1 Register Bit[12:0] = 13'd1360 VCO_LD_VAL_1 */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD1, 0x550); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */ + /* Calibration Load 2 Register Bit[12:0] = 13'd1360 VCO_LD_VAL_2 */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD2, 0x550); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */ + /* Calibration Load 3 Register Bit[12:0] = 13'd1360 VCO_LD_VAL_3 */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD3, 0x550); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */ + /* Calibration Reference 0 Register Bit[5:0] = 6'd34 VCO_REF_LD_0/1 */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF0, 0x2222); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */ + /* Calibration Reference 1 Register Bit[5:0] = 6'd34 VCO_REF_LD_2/3 */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF1, 0x2222); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY AFE-DFE */ + /* Enable Register Bit[7:0] = 8'd0 AFE_EN_0/3_1, DFE_EN_0/3_1 */ + txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, 0x0); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx */ + /* Equalization Control 4 Register Bit[3:0] = 4'd0 CONT_ADAPT_0/3_1 */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL, 0x00F0); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Tx Rate */ + /* Control Register Bit[14:12], Bit[10:8], Bit[6:4], Bit[2:0], + * all rates to 3'b010 TX0/1/2/3_RATE + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_RATE_CTL, 0x2222); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx Rate */ + /* Control Register Bit[13:12], Bit[9:8], Bit[5:4], Bit[1:0], + * all rates to 2'b10 RX0/1/2/3_RATE + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_RATE_CTL, 0x2222); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Tx General */ + /* Control 2 Register Bit[15:8] = 2'b01 TX0/1/2/3_WIDTH: 10bits */ + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GEN_CTL2, 0x5500); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx General */ + /* Control 2 Register Bit[15:8] = 2'b01 RX0/1/2/3_WIDTH: 10bits */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL2, 0x5500); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA Control + * 2 Register Bit[10:8] = 3'b010 + * MPLLA_DIV16P5_CLK_EN=0, MPLLA_DIV10_CLK_EN=1, MPLLA_DIV8_CLK_EN=0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL2, 0x200); + + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_CTL, 0x0); + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x0); + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_DIGI_CTL, 0x0); + + /* Initialize the mode by setting VR XS or PCS MMD Digital Control1 + * Register Bit[15](VR_RST) + */ + txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0xA000); + + /* wait phy initialization done */ + status = read_poll_timeout(txgbe_rd32_epcs, value, + !(value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST), + 100000, 10000000, false, + hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1); + if (status < 0) { + wx_err(wxhw, "PHY initialization timeout.\n"); + return; + } + + /* if success, set link status */ + hw->link_status = TXGBE_LINK_STATUS_KX4; +} + +static void txgbe_set_link_to_kx(struct txgbe_hw *hw, u32 speed, bool autoneg) +{ + struct wx_hw *wxhw = &hw->wxhw; + int status = 0; + u32 wdata = 0; + u32 value, i; + + /* check link status, if already set, skip setting it again */ + if (hw->link_status == TXGBE_LINK_STATUS_KX) + return; + + wx_dbg(wxhw, "It is set to kx. speed =0x%x\n", speed); + + /* Wait xpcs power-up good */ + status = read_poll_timeout(txgbe_rd32_epcs, value, + (value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M) == + TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG, + 10000, 1000000, false, + hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST); + if (status < 0) { + wx_err(wxhw, "xpcs power-up timeout.\n"); + return; + } + + wr32m(wxhw, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0); + + /* Disable xpcs AN-73 */ + if (!autoneg) + txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, 0); + else + txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, + TXGBE_SR_AN_MMD_CTL_EXT_NP_CTL | TXGBE_SR_AN_MMD_CTL_ENABLE); + + /* Disable PHY MPLLA for eth mode change(after ECO) */ + txgbe_wr32_ephy(hw, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x240A); + WX_WRITE_FLUSH(wxhw); + msleep(20); + + /* Set the eth change_mode bit first in mis_rst register */ + /* for corresponding LAN port */ + wr32(wxhw, WX_MIS_RST, WX_MIS_RST_LAN_ETH_MODE(wxhw->bus.func)); + + /* Set SR PCS Control2 Register Bits[1:0] = 2'b01 + * PCS_TYPE_SEL: non KR + */ + txgbe_wr32_epcs(hw, TXGBE_SR_PCS_CTL2, + TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X); + + /* Set SR PMA MMD Control1 Register Bit[13] = 1'b0 SS13: 1G speed */ + txgbe_wr32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1, + TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_1G); + + /* Set SR MII MMD Control Register to corresponding speed: {Bit[6], + * Bit[13]}=[2'b00,2'b01,2'b10]->[10M,100M,1G] + */ + wdata = TXGBE_SR_MII_MMD_CTL_DUPLEX_MODE; + if (speed == SPEED_100) + wdata |= TXGBE_SR_MII_MMD_CTL_SGMII_100; + else if (speed == SPEED_1000) + wdata |= TXGBE_SR_MII_MMD_CTL_SGMII_1000; + else if (speed == SPEED_10) + wdata |= TXGBE_SR_MII_MMD_CTL_SGMII_10; + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_CTL, wdata); + + value = (0xf5f0 & ~0x710) | (0x5 << 8) | 0x10; + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GENCTRL1, value); + + txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00); + + for (i = 0; i < 4; i++) { + if (i != 0) + value = 0xff06; + else + value = (0x45 & ~0xFFFF) | (0x7 << 12) | + (0x7 << 8) | 0x6; + + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0 + i, value); + } + + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0, 0x0); + + txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0x0); + + value = (0x6db & ~0x7) | 0x4; + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3, value); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA Control + * 0 Register Bit[7:0] = 8'd32 MPLLA_MULTIPLIER + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL0, 0x20); + + /* Set VR XS, PMA or MII Synopsys Enterprise Gen5 12G PHY MPLLA Control + * 3 Register Bit[10:0] = 11'd70 MPLLA_BANDWIDTH + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0x56); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO + * Calibration Load 0 Register Bit[12:0] = 13'd1344 VCO_LD_VAL_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD0, 0x540); + + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD1, 0x549); + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD2, 0x549); + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD3, 0x549); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO + * Calibration Reference 0 Register Bit[5:0] = 6'd42 VCO_REF_LD_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF0, 0x2A); + + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF1, 0x2929); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY AFE-DFE + * Enable Register Bit[4], Bit[0] = 1'b0 AFE_EN_0, DFE_EN_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, 0x0); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx + * Equalization Control 4 Register Bit[0] = 1'b0 CONT_ADAPT_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL, 0x0010); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Tx Rate + * Control Register Bit[2:0] = 3'b011 TX0_RATE + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_RATE_CTL, 0x3); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx Rate + * Control Register Bit[2:0] = 3'b011 RX0_RATE + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_RATE_CTL, 0x3); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Tx General + * Control 2 Register Bit[9:8] = 2'b01 TX0_WIDTH: 10bits + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GEN_CTL2, 0x100); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx General + * Control 2 Register Bit[9:8] = 2'b01 RX0_WIDTH: 10bits + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL2, 0x100); + + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA Control + * 2 Register Bit[10:8] = 3'b010 MPLLA_DIV16P5_CLK_EN=0, + * MPLLA_DIV10_CLK_EN=1, MPLLA_DIV8_CLK_EN=0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL2, 0x200); + + /* VR MII MMD AN Control Register Bit[8] = 1'b1 MII_CTRL */ + /* Set to 8bit MII (required in 10M/100M SGMII) */ + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x100); + + /* Initialize the mode by setting VR XS or PCS MMD Digital Control1 + * Register Bit[15](VR_RST) + */ + txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0xA000); + /* wait phy initialization done */ + status = read_poll_timeout(txgbe_rd32_epcs, value, + !(value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST), + 100000, 10000000, false, + hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1); + if (status < 0) { + wx_err(wxhw, "PHY initialization timeout.\n"); + return; + } + + /* if success, set link status */ + hw->link_status = TXGBE_LINK_STATUS_KX; +} + +static void txgbe_set_link_to_sfi(struct txgbe_hw *hw, u32 speed) +{ + struct wx_hw *wxhw = &hw->wxhw; + int status = 0; + u32 value = 0; + + /* Set the module link speed */ + txgbe_set_hard_rate_select_speed(hw, speed); + + /* Wait xpcs power-up good */ + status = read_poll_timeout(txgbe_rd32_epcs, value, + (value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M) == + TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG, + 10000, 1000000, false, + hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST); + if (status < 0) { + wx_err(wxhw, "xpcs power-up timeout.\n"); + return; + } + + wr32m(wxhw, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0); + + /* Disable xpcs AN-73 */ + txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, 0x0); + + /* Disable PHY MPLLA for eth mode change(after ECO) */ + txgbe_wr32_ephy(hw, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x243A); + WX_WRITE_FLUSH(wxhw); + msleep(20); + + /* Set the eth change_mode bit first in mis_rst register + * for corresponding LAN port + */ + wr32(wxhw, WX_MIS_RST, WX_MIS_RST_LAN_ETH_MODE(wxhw->bus.func)); + + if (speed == SPEED_10000) { + /* Set SR PCS Control2 Register Bits[1:0] = 2'b00 + * PCS_TYPE_SEL: KR + */ + txgbe_wr32_epcs(hw, TXGBE_SR_PCS_CTL2, 0); + value = txgbe_rd32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1); + value |= TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_10G; + txgbe_wr32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1, value); + /* Set VR_XS_PMA_Gen5_12G_MPLLA_CTRL0 Register Bit[7:0] = 8'd33 + * MPLLA_MULTIPLIER + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL0, 0x0021); + /* Set VR_XS_PMA_Gen5_12G_MPLLA_CTRL3 Register + * Bit[10:0](MPLLA_BANDWIDTH) = 11'd0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0); + value = txgbe_rd32_epcs(hw, TXGBE_PHY_TX_GENCTRL1); + value = (value & ~0x700) | 0x500; + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GENCTRL1, value); + /* Set VR_XS_PMA_Gen5_12G_MISC_CTRL0 Register + * Bit[12:8](RX_VREF_CTRL) = 5'hF + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00); + /* Set VR_XS_PMA_Gen5_12G_VCO_CAL_LD0 Register + * Bit[12:0] = 13'd1353 VCO_LD_VAL_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD0, 0x0549); + /* Set VR_XS_PMA_Gen5_12G_VCO_CAL_REF0 Register Bit[5:0] = 6'd41 + * VCO_REF_LD_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF0, 0x0029); + /* Set VR_XS_PMA_Gen5_12G_TX_RATE_CTRL Register + * Bit[2:0] = 3'b000 TX0_RATE + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_RATE_CTL, 0); + /* Set VR_XS_PMA_Gen5_12G_RX_RATE_CTRL Register + * Bit[2:0] = 3'b000 RX0_RATE + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_RATE_CTL, 0); + /* Set VR_XS_PMA_Gen5_12G_TX_GENCTRL2 Register Bit[9:8] = 2'b11 + * TX0_WIDTH: 20bits + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GEN_CTL2, 0x0300); + /* Set VR_XS_PMA_Gen5_12G_RX_GENCTRL2 Register Bit[9:8] = 2'b11 + * RX0_WIDTH: 20bits + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL2, 0x0300); + /* Set VR_XS_PMA_Gen5_12G_MPLLA_CTRL2 Register + * Bit[10:8] = 3'b110 MPLLA_DIV16P5_CLK_EN=1, + * MPLLA_DIV10_CLK_EN=1, MPLLA_DIV8_CLK_EN=0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL2, 0x0600); + + if (hw->phy.sfp_type == txgbe_sfp_type_da_cu_core) { + /* Set VR_XS_PMA_Gen5_12G_RX_EQ_CTRL0 Register + * Bit[15:8](VGA1/2_GAIN_0) = 8'h77, Bit[7:5] + * (CTLE_POLE_0) = 3'h2, Bit[4:0](CTLE_BOOST_0) = 4'hF + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, 0x774F); + } else { + /* Set VR_XS_PMA_Gen5_12G_RX_EQ_CTRL0 Register + * Bit[15:8] (VGA1/2_GAIN_0) = 8'h00, + * Bit[7:5](CTLE_POLE_0) = 3'h2, + * Bit[4:0](CTLE_BOOST_0) = 4'hA + */ + value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0); + value = (value & ~0xFFFF) | (2 << 5) | 0x05; + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, value); + } + value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0); + value &= ~0x7; + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0, value); + + if (hw->phy.sfp_type == txgbe_sfp_type_da_cu_core) { + /* Set VR_XS_PMA_Gen5_12G_DFE_TAP_CTRL0 Register + * Bit[7:0](DFE_TAP1_0) = 8'd20 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0x0014); + value = txgbe_rd32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE); + value |= 0x11; + txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, value); + } else { + /* Set VR_XS_PMA_Gen5_12G_DFE_TAP_CTRL0 Register + * Bit[7:0](DFE_TAP1_0) = 8'd20 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0xBE); + /* Set VR_MII_Gen5_12G_AFE_DFE_EN_CTRL Register + * Bit[4](DFE_EN_0) = 1'b0, Bit[0](AFE_EN_0) = 1'b0 + */ + value = txgbe_rd32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE); + value &= ~0x11; + txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, value); + } + value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_CTL); + value &= ~0x1; + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL, value); + } else { + /* Set SR PCS Control2 Register Bits[1:0] = 2'b00 + * PCS_TYPE_SEL: KR + */ + txgbe_wr32_epcs(hw, TXGBE_SR_PCS_CTL2, 0x1); + /* Set SR PMA MMD Control1 Register Bit[13] = 1'b0 + * SS13: 1G speed + */ + txgbe_wr32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1, 0x0); + /* Set SR MII MMD Control Register to corresponding speed: */ + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_CTL, 0x0140); + + value = txgbe_rd32_epcs(hw, TXGBE_PHY_TX_GENCTRL1); + value = (value & ~0x710) | 0x500; + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GENCTRL1, value); + /* Set VR_XS_PMA_Gen5_12G_MISC_CTRL0 Register + * Bit[12:8](RX_VREF_CTRL) = 5'hF + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00); + + if (hw->phy.sfp_type == txgbe_sfp_type_da_cu_core) { + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, 0x774F); + } else { + /* Set VR_XS_PMA_Gen5_12G_RX_EQ_CTRL0 Register + * Bit[15:8] (VGA1/2_GAIN_0) = 8'h00, + * Bit[7:5](CTLE_POLE_0) = 3'h2, + * Bit[4:0](CTLE_BOOST_0) = 4'hA + */ + value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0); + value = (value & ~0xFFFF) | 0x7706; + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, value); + } + value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0); + value &= ~0x7; + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0, value); + /* Set VR_XS_PMA_Gen5_12G_DFE_TAP_CTRL0 Register + * Bit[7:0](DFE_TAP1_0) = 8'd00 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0x0); + /* Set VR_XS_PMA_Gen5_12G_RX_GENCTRL3 Register + * Bit[2:0] LOS_TRSHLD_0 = 4 + */ + value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3); + value = (value & ~0x7) | 0x4; + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3, value); + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY + * MPLLA Control 0 Register Bit[7:0] = 8'd32 MPLLA_MULTIPLIER + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL0, 0x0020); + /* Set VR XS, PMA or MII Synopsys Enterprise Gen5 12G PHY MPLLA + * Control 3 Register Bit[10:0] = 11'd70 MPLLA_BANDWIDTH + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0x0046); + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO + * Calibration Load 0 Register + * Bit[12:0] = 13'd1344 VCO_LD_VAL_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD0, 0x0540); + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO + * Calibration Reference 0 Register + * Bit[5:0] = 6'd42 VCO_REF_LD_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF0, 0x002A); + /* Set VR XS, PMA, MII Synopsys Enterprise Gen5 12G PHY AFE-DFE + * Enable Register Bit[4], Bit[0] = 1'b0 AFE_EN_0, DFE_EN_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, 0x0); + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx + * Equalization Control 4 Register Bit[0] = 1'b0 CONT_ADAPT_0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL, 0x0010); + /* Set VR XS, PMA, MII Synopsys Enterprise Gen5 12G PHY Tx Rate + * Control Register Bit[2:0] = 3'b011 TX0_RATE + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_RATE_CTL, 0x0003); + /* Set VR XS, PMA, MII Synopsys Enterprise Gen5 12G PHY Rx Rate + * Control Register Bit[2:0] = 3'b011 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_RATE_CTL, 0x0003); + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY + * Tx General Control 2 Register + * Bit[9:8] = 2'b01 TX0_WIDTH: 10bits + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GEN_CTL2, 0x0100); + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY + * Rx General Control 2 Register + * Bit[9:8] = 2'b01 RX0_WIDTH: 10bits + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL2, 0x0100); + /* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA + * Control 2 Register Bit[10:8] = 3'b010 MPLLA_DIV16P5_CLK_EN=0, + * MPLLA_DIV10_CLK_EN=1, MPLLA_DIV8_CLK_EN=0 + */ + txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL2, 0x0200); + /* VR MII MMD AN Control Register Bit[8] = 1'b1 MII_CTRL */ + txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x0100); + } + /* Initialize the mode by setting VR XS or PCS MMD Digital Control1 + * Register Bit[15](VR_RST) + */ + txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0xA000); + /* wait phy initialization done */ + status = read_poll_timeout(txgbe_rd32_epcs, value, + !(value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST), + 100000, 10000000, false, + hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1); + if (status < 0) + wx_err(wxhw, "PHY initialization timeout.\n"); +} + +/** + * txgbe_check_mac_link - Determine link and speed status + * @hw: pointer to hardware structure + * @speed: pointer to link speed + * @link_up: true when link is up + * @link_up_wait_to_complete: bool used to wait for link up or not + * + * Reads the links register to determine if link is up and the current speed + **/ +void txgbe_check_mac_link(struct txgbe_hw *hw, u32 *speed, + bool *link_up, bool link_up_wait_to_complete) +{ + struct wx_hw *wxhw = &hw->wxhw; + u32 links_reg = 0; + int ret; + + *link_up = false; + + if (link_up_wait_to_complete) { + ret = read_poll_timeout(rd32, links_reg, + links_reg & TXGBE_CFG_PORT_ST_LINK_UP, + 10000, 10000 * 900, false, + wxhw, TXGBE_CFG_PORT_ST); + if (!ret) + *link_up = true; + } else { + links_reg = rd32(wxhw, TXGBE_CFG_PORT_ST); + if (links_reg & TXGBE_CFG_PORT_ST_LINK_UP) + *link_up = true; + } + + if (*link_up) { + if (links_reg & TXGBE_CFG_PORT_ST_LINK_10G) + *speed = SPEED_10000; + else if (links_reg & TXGBE_CFG_PORT_ST_LINK_1G) + *speed = SPEED_1000; + else if (links_reg & TXGBE_CFG_PORT_ST_LINK_100M) + *speed = SPEED_100; + else + *speed = SPEED_10; + } else { + *speed = SPEED_UNKNOWN; + } +} + +/** + * txgbe_get_link_capabilities - Determines link capabilities + * @hw: pointer to hardware structure + * @speed: pointer to link speed + * @autoneg: true when autoneg or autotry is enabled + **/ +int txgbe_get_link_capabilities(struct txgbe_hw *hw, + u32 *speed, + bool *autoneg) +{ + u16 sub_dev_id = hw->wxhw.subsystem_device_id & TXGBE_DEV_MASK; + u32 sr_pcs_ctl, sr_pma_mmd_ctl1, sr_an_mmd_ctl; + u32 sr_an_mmd_adv_reg2; + + /* Check if 1G SFP module. */ + if (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) { + *speed = SPEED_1000; + *autoneg = false; + } else if (hw->phy.multispeed_fiber) { + *speed = SPEED_10000 | SPEED_1000; + *autoneg = true; + } + /* SFP */ + else if (hw->phy.media_type == txgbe_media_type_fiber) { + *speed = SPEED_10000; + *autoneg = false; + } + /* SGMII */ + else if (sub_dev_id == TXGBE_ID_SGMII) { + *speed = SPEED_1000 | SPEED_100 | SPEED_10; + *autoneg = false; + hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_1000BASE_T | + TXGBE_PHYSICAL_LAYER_100BASE_TX; + /* MAC XAUI */ + } else if (sub_dev_id == TXGBE_ID_MAC_XAUI) { + *speed = SPEED_10000; + *autoneg = false; + hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_10GBASE_KX4; + /* MAC SGMII */ + } else if (sub_dev_id == TXGBE_ID_MAC_SGMII) { + *speed = SPEED_1000; + *autoneg = false; + hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_1000BASE_KX; + } else { /* KR KX KX4 */ + /* Determine link capabilities based on the stored value, + * which represents EEPROM defaults. If value has not + * been stored, use the current register values. + */ + if (hw->phy.orig_link_settings_stored) { + sr_pcs_ctl = hw->phy.orig_sr_pcs_ctl2; + sr_pma_mmd_ctl1 = hw->phy.orig_sr_pma_mmd_ctl1; + sr_an_mmd_ctl = hw->phy.orig_sr_an_mmd_ctl; + sr_an_mmd_adv_reg2 = hw->phy.orig_sr_an_mmd_adv_reg2; + } else { + 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); + } + + if ((sr_pcs_ctl & TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_MASK) == + TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X && + (sr_pma_mmd_ctl1 & TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_MASK) == + TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_1G && + (sr_an_mmd_ctl & TXGBE_SR_AN_MMD_CTL_ENABLE) == 0) { + /* 1G or KX - no backplane auto-negotiation */ + *speed = SPEED_1000; + *autoneg = false; + hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_1000BASE_KX; + } else if ((sr_pcs_ctl & TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_MASK) == + TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X && + (sr_pma_mmd_ctl1 & TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_MASK) == + TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_10G && + (sr_an_mmd_ctl & TXGBE_SR_AN_MMD_CTL_ENABLE) == 0) { + *speed = SPEED_10000; + *autoneg = false; + hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_10GBASE_KX4; + } else if ((sr_pcs_ctl & TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_MASK) == + TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_R && + (sr_an_mmd_ctl & TXGBE_SR_AN_MMD_CTL_ENABLE) == 0) { + /* 10 GbE serial link (KR -no backplane auto-negotiation) */ + *speed = SPEED_10000; + *autoneg = false; + hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_10GBASE_KR; + } else if (sr_an_mmd_ctl & TXGBE_SR_AN_MMD_CTL_ENABLE) { + /* KX/KX4/KR backplane auto-negotiation enable */ + *speed = SPEED_UNKNOWN; + if (sr_an_mmd_adv_reg2 & TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KR) + *speed |= SPEED_10000; + if (sr_an_mmd_adv_reg2 & TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KX4) + *speed |= SPEED_10000; + if (sr_an_mmd_adv_reg2 & TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KX) + *speed |= SPEED_1000; + *autoneg = true; + hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_10GBASE_KR | + TXGBE_PHYSICAL_LAYER_10GBASE_KX4 | + TXGBE_PHYSICAL_LAYER_1000BASE_KX; + } else { + return -ENODEV; + } + } + + return 0; +} + +/** + * txgbe_disable_tx_laser - Disable Tx laser + * @hw: pointer to hardware structure + * + * The base drivers may require better control over SFP+ module + * PHY states. This includes selectively shutting down the Tx + * laser on the PHY, effectively halting physical link. + **/ +void txgbe_disable_tx_laser(struct txgbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + u32 esdp_reg; + + if (hw->phy.media_type != txgbe_media_type_fiber) + return; + + /* Disable Tx laser; allow 100us to go dark per spec */ + esdp_reg = rd32(wxhw, TXGBE_GPIO_DR); + esdp_reg |= TXGBE_GPIO_DR_1 | TXGBE_GPIO_DR_0; + wr32(wxhw, TXGBE_GPIO_DR, esdp_reg); + WX_WRITE_FLUSH(wxhw); + usleep_range(100, 200); +} + +/** + * txgbe_enable_tx_laser - Enable Tx laser + * @hw: pointer to hardware structure + * + * The base drivers may require better control over SFP+ module + * PHY states. This includes selectively turning on the Tx + * laser on the PHY, effectively starting physical link. + **/ +void txgbe_enable_tx_laser(struct txgbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + + if (hw->phy.media_type != txgbe_media_type_fiber) + return; + + /* Enable Tx laser; allow 100ms to light up */ + wr32m(wxhw, TXGBE_GPIO_DR, + TXGBE_GPIO_DR_0 | TXGBE_GPIO_DR_1, 0); + WX_WRITE_FLUSH(wxhw); + msleep(100); +} + +/** + * txgbe_flap_tx_laser - Flap Tx laser + * @hw: pointer to hardware structure + * + * When the driver changes the link speeds that it can support, + * it sets autotry_restart to true to indicate that we need to + * initiate a new autotry session with the link partner. To do + * so, we set the speed then disable and re-enable the Tx laser, to + * alert the link partner that it also needs to restart autotry on its + * end. This is consistent with true clause 37 autoneg, which also + * involves a loss of signal. + **/ +static void txgbe_flap_tx_laser(struct txgbe_hw *hw) +{ + if (hw->phy.media_type != txgbe_media_type_fiber) + return; + + if (hw->phy.autotry_restart) { + txgbe_disable_tx_laser(hw); + txgbe_enable_tx_laser(hw); + hw->phy.autotry_restart = false; + } +} + +/** + * txgbe_setup_mac_link - Set MAC link speed + * @hw: pointer to hardware structure + * @speed: new link speed + * + * Set the link speed. + **/ +static int txgbe_setup_mac_link(struct txgbe_hw *hw, u32 speed) +{ + u16 sub_dev_id = hw->wxhw.subsystem_device_id & TXGBE_DEV_MASK; + u32 link_capabilities = SPEED_UNKNOWN; + u32 link_speed = SPEED_UNKNOWN; + bool autoneg = false; + bool link_up = false; + int status = 0; + + /* Check to see if speed passed in is supported. */ + status = txgbe_get_link_capabilities(hw, &link_capabilities, &autoneg); + if (status) + return status; + + speed &= link_capabilities; + if (speed == SPEED_UNKNOWN) + return -EINVAL; + + if (!(sub_dev_id == TXGBE_ID_KR_KX_KX4 || + sub_dev_id == TXGBE_ID_MAC_XAUI || + sub_dev_id == TXGBE_ID_MAC_SGMII)) { + txgbe_check_mac_link(hw, &link_speed, &link_up, false); + if (link_speed == speed && link_up) + return 0; + } + + if (sub_dev_id == TXGBE_ID_KR_KX_KX4) { + if (!autoneg) { + switch (hw->phy.link_mode) { + case TXGBE_PHYSICAL_LAYER_10GBASE_KR: + txgbe_set_link_to_kr(hw, autoneg); + break; + case TXGBE_PHYSICAL_LAYER_10GBASE_KX4: + txgbe_set_link_to_kx4(hw, autoneg); + break; + case TXGBE_PHYSICAL_LAYER_1000BASE_KX: + txgbe_set_link_to_kx(hw, speed, autoneg); + break; + default: + return -ENODEV; + } + } else { + txgbe_set_link_to_kr(hw, autoneg); + } + } else if (sub_dev_id == TXGBE_ID_XAUI || + sub_dev_id == TXGBE_ID_MAC_XAUI || + sub_dev_id == TXGBE_ID_SGMII || + sub_dev_id == TXGBE_ID_MAC_SGMII) { + if (speed == SPEED_10000) { + txgbe_set_link_to_kx4(hw, 0); + } else { + txgbe_set_link_to_kx(hw, speed, 0); + txgbe_set_sgmii_an37_ability(hw); + } + } else if (hw->phy.media_type == txgbe_media_type_fiber) { + txgbe_set_link_to_sfi(hw, speed); + if (speed == SPEED_1000) + txgbe_set_sgmii_an37_ability(hw); + } + + return 0; +} + +/** + * txgbe_setup_link - Set MAC link speed + * @hw: pointer to hardware structure + * @speed: new link speed + * @autoneg_wait_to_complete: true when waiting for completion is needed + * + * Set the link speed in the MAC and/or PHY register and restarts link. + **/ +int txgbe_setup_link(struct txgbe_hw *hw, u32 speed, + bool autoneg_wait_to_complete) +{ + u32 highest_link_speed = SPEED_UNKNOWN; + u32 link_speed = SPEED_UNKNOWN; + bool autoneg, link_up = false; + u32 speedcnt = 0; + int status = 0; + u32 i = 0; + + if (!hw->phy.multispeed_fiber) + return txgbe_setup_mac_link(hw, speed); + + /* Mask off requested but non-supported speeds */ + status = txgbe_get_link_capabilities(hw, &link_speed, &autoneg); + if (status != 0) + return status; + + speed &= link_speed; + + /* Try each speed one by one, highest priority first. We do this in + * software because 10Gb fiber doesn't support speed autonegotiation. + */ + if (speed & SPEED_10000) { + speedcnt++; + highest_link_speed = SPEED_10000; + + /* If we already have link at this speed, just jump out */ + txgbe_check_mac_link(hw, &link_speed, &link_up, false); + if (link_speed == SPEED_10000 && link_up) + goto out; + + /* Allow module to change analog characteristics (1G->10G) */ + msleep(40); + + status = txgbe_setup_mac_link(hw, SPEED_10000); + if (status != 0) + return status; + + /* Flap the Tx laser if it has not already been done */ + txgbe_flap_tx_laser(hw); + + /* Wait for the controller to acquire link. Per IEEE 802.3ap, + * Section 73.10.2, we may have to wait up to 500ms if KR is + * attempted. sapphire uses the same timing for 10g SFI. + */ + for (i = 0; i < 5; i++) { + /* Wait for the link partner to also set speed */ + msleep(100); + + /* If we have link, just jump out */ + txgbe_check_mac_link(hw, &link_speed, &link_up, false); + if (link_up) + goto out; + } + } + + if (speed & SPEED_1000) { + speedcnt++; + if (highest_link_speed == SPEED_UNKNOWN) + highest_link_speed = SPEED_1000; + + /* If we already have link at this speed, just jump out */ + txgbe_check_mac_link(hw, &link_speed, &link_up, false); + if (link_speed == SPEED_1000 && link_up) + goto out; + + /* Allow module to change analog characteristics (10G->1G) */ + msleep(40); + + status = txgbe_setup_mac_link(hw, SPEED_1000); + if (status != 0) + return status; + + /* Flap the Tx laser if it has not already been done */ + txgbe_flap_tx_laser(hw); + + /* Wait for the link partner to also set speed */ + msleep(100); + + /* If we have link, just jump out */ + txgbe_check_mac_link(hw, &link_speed, &link_up, false); + if (link_up) + goto out; + } + + /* We didn't get link. Configure back to the highest speed we tried, + * (if there was more than one). We call ourselves back with just the + * single highest speed that the user requested. + */ + if (speedcnt > 1) + status = txgbe_setup_link(hw, highest_link_speed, + autoneg_wait_to_complete); + +out: + /* Set autoneg_advertised value based on input link speed */ + hw->phy.autoneg_advertised = 0; + + if (speed & SPEED_10000) + hw->phy.autoneg_advertised |= SPEED_10000; + + if (speed & SPEED_1000) + hw->phy.autoneg_advertised |= SPEED_1000; + + return status; +} + /** * txgbe_init_thermal_sensor_thresh - Inits thermal sensor thresholds * @hw: pointer to hardware structure @@ -628,8 +1755,14 @@ int txgbe_validate_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum_val) static void txgbe_reset_misc(struct txgbe_hw *hw) { struct wx_hw *wxhw = &hw->wxhw; + u32 value; txgbe_init_i2c(hw); + + value = txgbe_rd32_epcs(hw, TXGBE_SR_PCS_CTL2); + if ((value & 0x3) != TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X) + hw->link_status = TXGBE_LINK_STATUS_NONE; + wx_reset_misc(wxhw); txgbe_init_thermal_sensor_thresh(hw); } @@ -672,8 +1805,11 @@ int txgbe_reset_hw(struct txgbe_hw *hw) 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); + ((wxhw->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) { + status = wx_reset_hostif(wxhw); + if (status == 0) + hw->link_status = TXGBE_LINK_STATUS_NONE; + } usleep_range(10, 100); @@ -729,3 +1865,32 @@ int txgbe_reset_hw(struct txgbe_hw *hw) return 0; } + +/** + * txgbe_start_hw - Prepare hardware for Tx/Rx + * @hw: pointer to hardware structure + * + * Starts the hardware using the generic start_hw function + * and the generation start_hw function. + * Then performs revision-specific operations, if any. + **/ +void txgbe_start_hw(struct txgbe_hw *hw) +{ + /* We need to run link autotry after the driver loads */ + hw->phy.autotry_restart = true; +} + +int txgbe_init_hw(struct txgbe_hw *hw) +{ + int status; + + /* Reset the hardware */ + status = txgbe_reset_hw(hw); + + if (status == 0) { + /* Start the HW */ + txgbe_start_hw(hw); + } + + return status; +} diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h index 6a751a69177b..32d3f9d7c0e5 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h @@ -4,8 +4,17 @@ #ifndef _TXGBE_HW_H_ #define _TXGBE_HW_H_ +int txgbe_identify_sfp_module(struct txgbe_hw *hw); +void txgbe_check_mac_link(struct txgbe_hw *hw, u32 *speed, + bool *link_up, bool link_up_wait_to_complete); +int txgbe_get_link_capabilities(struct txgbe_hw *hw, u32 *speed, bool *autoneg); +void txgbe_disable_tx_laser(struct txgbe_hw *hw); +void txgbe_enable_tx_laser(struct txgbe_hw *hw); +int txgbe_setup_link(struct txgbe_hw *hw, u32 speed, bool autoneg_wait_to_complete); int txgbe_read_pba_string(struct txgbe_hw *hw, u8 *pba_num, u32 pba_num_size); int txgbe_validate_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum_val); int txgbe_reset_hw(struct txgbe_hw *hw); +void txgbe_start_hw(struct txgbe_hw *hw); +int txgbe_init_hw(struct txgbe_hw *hw); #endif /* _TXGBE_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index cb86c001baa6..a0efe4259fdc 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include "../libwx/wx_type.h" @@ -93,12 +95,250 @@ static void txgbe_service_event_complete(struct txgbe_adapter *adapter) clear_bit(__TXGBE_SERVICE_SCHED, &adapter->state); } +/** + * txgbe_watchdog_update_link - update the link status + * @adapter: pointer to the device adapter structure + **/ +static void txgbe_watchdog_update_link(struct txgbe_adapter *adapter) +{ + u32 link_speed = adapter->link_speed; + struct txgbe_hw *hw = &adapter->hw; + bool link_up = adapter->link_up; + struct wx_hw *wxhw = &hw->wxhw; + u32 i, reg; + + if (!(adapter->flags & TXGBE_FLAG_NEED_LINK_UPDATE)) + return; + + link_speed = SPEED_10000; + link_up = true; + txgbe_check_mac_link(hw, &link_speed, &link_up, false); + + if (link_up || time_after(jiffies, (adapter->link_check_timeout + + TXGBE_TRY_LINK_TIMEOUT))) { + adapter->flags &= ~TXGBE_FLAG_NEED_LINK_UPDATE; + } + + for (i = 0; i < 3; i++) { + txgbe_check_mac_link(hw, &link_speed, &link_up, false); + msleep(20); + } + + adapter->link_up = link_up; + adapter->link_speed = link_speed; + + if (link_up) { + if (link_speed & SPEED_10000) { + reg = rd32(wxhw, WX_MAC_TX_CFG); + reg &= ~WX_MAC_TX_CFG_SPEED_MASK; + reg |= WX_MAC_TX_CFG_SPEED_10G | WX_MAC_TX_CFG_TE; + wr32(wxhw, WX_MAC_TX_CFG, reg); + } else if (link_speed & (SPEED_1000 | SPEED_100 | SPEED_10)) { + reg = rd32(wxhw, WX_MAC_TX_CFG); + reg &= ~WX_MAC_TX_CFG_SPEED_MASK; + reg |= WX_MAC_TX_CFG_SPEED_1G | WX_MAC_TX_CFG_TE; + wr32(wxhw, WX_MAC_TX_CFG, reg); + } + + /* Re configure MAC RX */ + reg = rd32(wxhw, WX_MAC_RX_CFG); + wr32(wxhw, WX_MAC_RX_CFG, reg); + wr32(wxhw, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR); + reg = rd32(wxhw, WX_MAC_WDG_TIMEOUT); + wr32(wxhw, WX_MAC_WDG_TIMEOUT, reg); + } +} + +/** + * txgbe_watchdog_link_is_up - update netif_carrier status and + * print link up message + * @adapter: pointer to the device adapter structure + **/ +static void txgbe_watchdog_link_is_up(struct txgbe_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + const char *speed_str; + + /* only continue if link was previously down */ + if (netif_carrier_ok(netdev)) + return; + + switch (adapter->link_speed) { + case SPEED_10000: + speed_str = "10 Gbps"; + break; + case SPEED_1000: + speed_str = "1 Gbps"; + break; + case SPEED_100: + speed_str = "100 Mbps"; + break; + case SPEED_10: + speed_str = "10 Mbps"; + break; + default: + speed_str = "unknown speed"; + break; + } + + netif_info(adapter, drv, netdev, + "NIC Link is Up %s\n", speed_str); + + netif_carrier_on(netdev); +} + +/** + * txgbe_watchdog_link_is_down - update netif_carrier status and + * print link down message + * @adapter: pointer to the adapter structure + **/ +static void txgbe_watchdog_link_is_down(struct txgbe_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + adapter->link_up = false; + adapter->link_speed = 0; + + /* only continue if link was up previously */ + if (!netif_carrier_ok(netdev)) + return; + + netif_info(adapter, drv, netdev, "NIC Link is Down\n"); + netif_carrier_off(netdev); +} + +/** + * txgbe_watchdog_subtask - check and bring link up + * @adapter: pointer to the device adapter structure + **/ +static void txgbe_watchdog_subtask(struct txgbe_adapter *adapter) +{ + /* if interface is down do nothing */ + if (test_bit(__TXGBE_DOWN, &adapter->state) || + test_bit(__TXGBE_REMOVING, &adapter->state) || + test_bit(__TXGBE_RESETTING, &adapter->state)) + return; + + txgbe_watchdog_update_link(adapter); + + if (adapter->link_up) + txgbe_watchdog_link_is_up(adapter); + else + txgbe_watchdog_link_is_down(adapter); +} + +/** + * txgbe_sfp_detection_subtask - poll for SFP+ cable + * @adapter: the txgbe adapter structure + **/ +static void txgbe_sfp_detection_subtask(struct txgbe_adapter *adapter) +{ + struct txgbe_hw *hw = &adapter->hw; + int err; + + /* not searching for SFP so there is nothing to do here */ + if (!(adapter->flags & TXGBE_FLAG_SFP_NEEDS_RESET)) + return; + + if (adapter->sfp_poll_time && + time_after(adapter->sfp_poll_time, jiffies)) + return; /* If not yet time to poll for SFP */ + + /* someone else is in init, wait until next service event */ + if (test_and_set_bit(__TXGBE_IN_SFP_INIT, &adapter->state)) + return; + + adapter->sfp_poll_time = jiffies + TXGBE_SFP_POLL_JIFFIES - 1; + + err = txgbe_identify_sfp_module(hw); + if (err != 0) { + if (hw->phy.sfp_type == txgbe_sfp_type_not_present) { + /* If no cable is present, then we need to reset + * the next time we find a good cable. + */ + adapter->flags |= TXGBE_FLAG_SFP_NEEDS_RESET; + } + /* exit on error */ + goto sfp_out; + } + + /* exit if reset not needed */ + if (!(adapter->flags & TXGBE_FLAG_SFP_NEEDS_RESET)) + goto sfp_out; + + adapter->flags &= ~TXGBE_FLAG_SFP_NEEDS_RESET; + + if (!hw->phy.multispeed_fiber) + hw->phy.autoneg_advertised = 0; + + adapter->flags |= TXGBE_FLAG_NEED_LINK_CONFIG; + netif_info(adapter, probe, adapter->netdev, + "detected SFP+: %d\n", hw->phy.sfp_type); + +sfp_out: + clear_bit(__TXGBE_IN_SFP_INIT, &adapter->state); + + if (hw->phy.type == txgbe_phy_sfp_unsupported) + dev_err(&adapter->pdev->dev, + "failed to initialize because an unsupported SFP+ module type was detected.\n"); +} + +/** + * txgbe_sfp_link_config_subtask - set up link SFP after module install + * @adapter: the txgbe adapter structure + **/ +static void txgbe_sfp_link_config_subtask(struct txgbe_adapter *adapter) +{ + struct txgbe_hw *hw = &adapter->hw; + bool autoneg = false; + u32 speed; + + if (!(adapter->flags & TXGBE_FLAG_NEED_LINK_CONFIG)) + return; + + /* someone else is in init, wait until next service event */ + if (test_and_set_bit(__TXGBE_IN_SFP_INIT, &adapter->state)) + return; + + adapter->flags &= ~TXGBE_FLAG_NEED_LINK_CONFIG; + + if ((hw->wxhw.subsystem_device_id & TXGBE_DEV_MASK) == + TXGBE_ID_MAC_SGMII) { + speed = SPEED_10000; + } else { + speed = hw->phy.autoneg_advertised; + if (!speed) { + txgbe_get_link_capabilities(hw, &speed, &autoneg); + /* setup the highest link when no autoneg */ + if (!autoneg) { + if (speed & SPEED_10000) + speed = SPEED_10000; + } + } + } + + txgbe_setup_link(hw, speed, false); + + adapter->flags |= TXGBE_FLAG_NEED_LINK_UPDATE; + adapter->link_check_timeout = jiffies; + clear_bit(__TXGBE_IN_SFP_INIT, &adapter->state); +} + static void txgbe_service_timer(struct timer_list *t) { struct txgbe_adapter *adapter = from_timer(adapter, t, service_timer); unsigned long next_event_offset; - next_event_offset = HZ * 2; + /* poll faster when waiting for link */ + if (adapter->flags & TXGBE_FLAG_NEED_LINK_UPDATE) { + if ((adapter->hw.wxhw.subsystem_device_id & TXGBE_DEV_MASK) == + TXGBE_ID_KR_KX_KX4) + next_event_offset = HZ; + else + next_event_offset = HZ / 10; + } else { + next_event_offset = HZ * 2; + } /* Reset the timer */ mod_timer(&adapter->service_timer, next_event_offset + jiffies); @@ -116,6 +356,9 @@ static void txgbe_service_task(struct work_struct *work) struct txgbe_adapter, service_task); + txgbe_sfp_detection_subtask(adapter); + txgbe_sfp_link_config_subtask(adapter); + txgbe_watchdog_subtask(adapter); txgbe_service_event_complete(adapter); } @@ -208,8 +451,55 @@ static void txgbe_up_complete(struct txgbe_adapter *adapter) { struct txgbe_hw *hw = &adapter->hw; struct wx_hw *wxhw = &hw->wxhw; + u32 links_reg; wx_control_hw(wxhw, true); + + /* enable the optics for SFP+ fiber */ + txgbe_enable_tx_laser(hw); + + /* make sure to complete pre-operations */ + smp_mb__before_atomic(); + clear_bit(__TXGBE_DOWN, &adapter->state); + + if (hw->phy.media_type == txgbe_media_type_fiber) { + /* We are assuming the worst case scenerio here, and that + * is that an SFP was inserted/removed after the reset + * but before SFP detection was enabled. As such the best + * solution is to just start searching as soon as we start + */ + adapter->flags |= TXGBE_FLAG_SFP_NEEDS_RESET; + adapter->sfp_poll_time = 0; + } else if (hw->phy.media_type == txgbe_media_type_backplane) { + adapter->flags |= TXGBE_FLAG_NEED_LINK_CONFIG; + txgbe_service_event_schedule(adapter); + } + + links_reg = rd32(wxhw, TXGBE_CFG_PORT_ST); + if (links_reg & TXGBE_CFG_PORT_ST_LINK_UP) { + if (links_reg & TXGBE_CFG_PORT_ST_LINK_10G) { + wr32(wxhw, WX_MAC_TX_CFG, + (rd32(wxhw, WX_MAC_TX_CFG) & ~WX_MAC_TX_CFG_SPEED_MASK) | + WX_MAC_TX_CFG_SPEED_10G); + } else if (links_reg & (TXGBE_CFG_PORT_ST_LINK_1G | + TXGBE_CFG_PORT_ST_LINK_100M)) { + wr32(wxhw, WX_MAC_TX_CFG, + (rd32(wxhw, WX_MAC_TX_CFG) & ~WX_MAC_TX_CFG_SPEED_MASK) | + WX_MAC_TX_CFG_SPEED_1G); + } + } + + /* bring the link up in the watchdog, this could race with our first + * link up interrupt but shouldn't be a problem + */ + adapter->flags |= TXGBE_FLAG_NEED_LINK_UPDATE; + adapter->link_check_timeout = jiffies; + + mod_timer(&adapter->service_timer, jiffies); + + /* Set PF Reset Done bit so PF/VF Mail Ops can work */ + wr32m(wxhw, WX_CFG_PORT_CTL, + WX_CFG_PORT_CTL_PFRSTD, WX_CFG_PORT_CTL_PFRSTD); } static void txgbe_reset(struct txgbe_adapter *adapter) @@ -219,12 +509,21 @@ static void txgbe_reset(struct txgbe_adapter *adapter) u8 old_addr[ETH_ALEN]; int err; - err = txgbe_reset_hw(hw); + /* lock SFP init bit to prevent race conditions with the watchdog */ + while (test_and_set_bit(__TXGBE_IN_SFP_INIT, &adapter->state)) + usleep_range(1000, 2000); + + /* clear all SFP and link config related flags while holding SFP_INIT */ + adapter->flags &= ~TXGBE_FLAG_SFP_NEEDS_RESET; + adapter->flags &= ~TXGBE_FLAG_NEED_LINK_CONFIG; + + err = txgbe_init_hw(hw); 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); + clear_bit(__TXGBE_IN_SFP_INIT, &adapter->state); /* do not flush user set addresses */ memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len); txgbe_flush_sw_mac_table(adapter); @@ -247,6 +546,8 @@ static void txgbe_disable_device(struct txgbe_adapter *adapter) netif_carrier_off(netdev); netif_tx_disable(netdev); + adapter->flags &= ~TXGBE_FLAG_NEED_LINK_UPDATE; + del_timer_sync(&adapter->service_timer); if (wxhw->bus.func < 2) @@ -268,8 +569,14 @@ static void txgbe_disable_device(struct txgbe_adapter *adapter) static void txgbe_down(struct txgbe_adapter *adapter) { + struct txgbe_hw *hw = &adapter->hw; + txgbe_disable_device(adapter); txgbe_reset(adapter); + + if ((hw->wxhw.subsystem_device_id & WX_NCSI_MASK) != WX_NCSI_SUP) + /* power down the optics for SFP+ fiber */ + txgbe_disable_tx_laser(hw); } /** @@ -350,7 +657,11 @@ static int txgbe_open(struct net_device *netdev) */ static void txgbe_close_suspend(struct txgbe_adapter *adapter) { + struct txgbe_hw *hw = &adapter->hw; + txgbe_disable_device(adapter); + if ((hw->wxhw.subsystem_device_id & WX_NCSI_MASK) != WX_NCSI_SUP) + txgbe_disable_tx_laser(hw); } /** @@ -618,12 +929,18 @@ static int txgbe_probe(struct pci_dev *pdev, "0x%08x", etrack_id); } + txgbe_start_hw(hw); + err = register_netdev(netdev); if (err) goto err_release_hw; pci_set_drvdata(pdev, adapter); + if (!((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP)) + /* power down the optics for SFP+ fiber */ + txgbe_disable_tx_laser(hw); + /* calculate the expected PCIe bandwidth required for optimal * performance. Note that some older parts will never have enough * bandwidth due to being older generation PCIe parts. We clamp these @@ -638,6 +955,11 @@ static int txgbe_probe(struct pci_dev *pdev, else dev_warn(&pdev->dev, "Failed to enumerate PF devices.\n"); + if ((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) + netif_info(adapter, probe, netdev, "NCSI : support"); + else + netif_info(adapter, probe, netdev, "NCSI : unsupported"); + /* First try to read PBA as a string */ err = txgbe_read_pba_string(hw, part_str, TXGBE_PBANUM_LENGTH); if (err) @@ -654,6 +976,14 @@ static int txgbe_probe(struct pci_dev *pdev, netif_info(adapter, probe, netdev, "%pM\n", netdev->dev_addr); + netif_info(adapter, probe, netdev, + "WangXun(R) 10 Gigabit Network Connection\n"); + + /* setup link for SFP devices with MNG FW, else wait for TXGBE_UP */ + if (hw->phy.media_type == txgbe_media_type_fiber && + ((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP)) + txgbe_setup_link(hw, SPEED_10000 | SPEED_1000, true); + return 0; err_release_hw: @@ -692,6 +1022,7 @@ static void txgbe_remove(struct pci_dev *pdev) cancel_work_sync(&adapter->service_task); unregister_netdev(netdev); + wx_control_hw(&adapter->hw.wxhw, false); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 2f8be0118157..0c5d62892ba1 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -36,6 +36,8 @@ /* Combined interface*/ #define TXGBE_ID_SFI_XAUI 0x50 +#define TXGBE_DEV_MASK 0xF0 + /* Revision ID */ #define TXGBE_SP_MPW 1 @@ -56,12 +58,67 @@ /*********************** ETH PHY ***********************/ #define TXGBE_XPCS_IDA_ADDR 0x13000 #define TXGBE_XPCS_IDA_DATA 0x13004 +#define TXGBE_ETHPHY_IDA_ADDR 0x13008 +#define TXGBE_ETHPHY_IDA_DATA 0x1300C /* ETH PHY Registers */ #define TXGBE_SR_PCS_CTL2 0x30007 +#define TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_R 0x0 +#define TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X BIT(0) +#define TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_MASK 0x3 #define TXGBE_SR_PMA_MMD_CTL1 0x10000 +#define TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_1G 0x0 +#define TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_10G BIT(13) +#define TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_MASK BIT(13) #define TXGBE_SR_AN_MMD_CTL 0x70000 +#define TXGBE_SR_AN_MMD_CTL_EXT_NP_CTL BIT(13) +#define TXGBE_SR_AN_MMD_CTL_ENABLE BIT(12) #define TXGBE_SR_AN_MMD_ADV_REG2 0x70011 +#define TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KX BIT(5) +#define TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KX4 BIT(6) +#define TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KR BIT(7) +#define TXGBE_SR_MII_MMD_CTL 0x1F0000 +#define TXGBE_SR_MII_MMD_CTL_AN_EN BIT(12) +#define TXGBE_SR_MII_MMD_CTL_RESTART_AN BIT(9) +#define TXGBE_SR_MII_MMD_CTL_DUPLEX_MODE BIT(8) +#define TXGBE_SR_MII_MMD_CTL_SGMII_1000 BIT(6) +#define TXGBE_SR_MII_MMD_CTL_SGMII_100 BIT(13) +#define TXGBE_SR_MII_MMD_CTL_SGMII_10 0 +#define TXGBE_SR_MII_MMD_DIGI_CTL 0x1F8000 +#define TXGBE_SR_MII_MMD_DIGI_CTL_AS BIT(9) +#define TXGBE_SR_MII_MMD_AN_CTL 0x1F8001 #define TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1 0x38000 +#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST BIT(15) +#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST 0x38010 +#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M (0x7 << 2) +#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG (BIT(2) << 2) +#define TXGBE_VR_AN_INTR_MSK 0x78001 +#define TXGBE_VR_AN_INTR_MSK_AN_PG_RCV_IE BIT(2) +#define TXGBE_VR_AN_INTR_MSK_AN_INC_LINK_IE BIT(1) +#define TXGBE_VR_AN_INTR_MSK_AN_INT_CMPLT_IE BIT(0) +#define TXGBE_VR_AN_KR_MODE_CL 0x78003 +#define TXGBE_VR_AN_KR_MODE_CL_PDET_EN BIT(0) +#define TXGBE_PHY_TX_GENCTRL1 0x18031 +#define TXGBE_PHY_TX_GEN_CTL2 0x18032 +#define TXGBE_PHY_TX_RATE_CTL 0x18034 +#define TXGBE_PHY_RX_GEN_CTL2 0x18052 +#define TXGBE_PHY_RX_GEN_CTL3 0x18053 +#define TXGBE_PHY_RX_RATE_CTL 0x18054 +#define TXGBE_PHY_RX_EQ_ATT_LVL0 0x18057 +#define TXGBE_PHY_RX_EQ_CTL0 0x18058 +#define TXGBE_PHY_RX_EQ_CTL 0x1805C +#define TXGBE_PHY_AFE_DFE_ENABLE 0x1805D +#define TXGBE_PHY_DFE_TAP_CTL0 0x1805E +#define TXGBE_PHY_MPLLA_CTL0 0x18071 +#define TXGBE_PHY_MPLLA_CTL2 0x18073 +#define TXGBE_PHY_MPLLA_CTL3 0x18077 +#define TXGBE_PHY_MISC_CTL0 0x18090 +#define TXGBE_PHY_VCO_CAL_LD0 0x18092 +#define TXGBE_PHY_VCO_CAL_LD1 0x18093 +#define TXGBE_PHY_VCO_CAL_LD2 0x18094 +#define TXGBE_PHY_VCO_CAL_LD3 0x18095 +#define TXGBE_PHY_VCO_CAL_REF0 0x18096 +#define TXGBE_PHY_VCO_CAL_REF1 0x18097 +#define TXGBE_SUP_DIG_MPLLA_OVRD_IN_0 0x4 /* I2C registers */ #define TXGBE_I2C_CON 0x14900 /* I2C Control */ #define TXGBE_I2C_CON_SLAVE_DISABLE BIT(6) @@ -87,6 +144,25 @@ #define TXGBE_I2C_SLAVE_ADDR (0xA0 >> 1) #define TXGBE_I2C_EEPROM_DEV_ADDR 0xA0 +/* port cfg Registers */ +#define TXGBE_CFG_PORT_ST 0x14404 +#define TXGBE_CFG_PORT_ST_LINK_UP BIT(0) +#define TXGBE_CFG_PORT_ST_LINK_10G BIT(1) +#define TXGBE_CFG_PORT_ST_LINK_1G BIT(2) +#define TXGBE_CFG_PORT_ST_LINK_100M BIT(3) + +/* GPIO Registers */ +#define TXGBE_GPIO_DR 0x14800 +#define TXGBE_GPIO_DR_0 BIT(0) /* SDP0 Data Value */ +#define TXGBE_GPIO_DR_1 BIT(1) /* SDP1 Data Value */ +#define TXGBE_GPIO_DR_4 BIT(4) /* SDP4 Data Value */ +#define TXGBE_GPIO_DR_5 BIT(5) /* SDP5 Data Value */ +#define TXGBE_GPIO_DDR 0x14804 +#define TXGBE_GPIO_DDR_0 BIT(0) /* SDP0 IO direction */ +#define TXGBE_GPIO_DDR_1 BIT(1) /* SDP1 IO direction */ +#define TXGBE_GPIO_DDR_4 BIT(4) /* SDP4 IO direction */ +#define TXGBE_GPIO_DDR_5 BIT(5) /* SDP5 IO direction */ + /* EEPROM byte offsets */ #define TXGBE_SFF_IDENTIFIER 0x0 #define TXGBE_SFF_IDENTIFIER_SFP 0x3 @@ -133,6 +209,22 @@ #define TXGBE_PBANUM1_PTR 0x06 #define TXGBE_PBANUM_PTR_GUARD 0xFAFA +/* Link speed */ +#define TXGBE_LINK_SPEED_AUTONEG (SPEED_10 | \ + SPEED_100 | \ + SPEED_1000 | \ + SPEED_10000) + +/* Physical layer type */ +#define TXGBE_PHYSICAL_LAYER_UNKNOWN 0 +#define TXGBE_PHYSICAL_LAYER_10GBASE_T BIT(0) +#define TXGBE_PHYSICAL_LAYER_1000BASE_T BIT(1) +#define TXGBE_PHYSICAL_LAYER_100BASE_TX BIT(2) +#define TXGBE_PHYSICAL_LAYER_10GBASE_KX4 BIT(3) +#define TXGBE_PHYSICAL_LAYER_1000BASE_KX BIT(4) +#define TXGBE_PHYSICAL_LAYER_10GBASE_KR BIT(5) +#define TXGBE_PHYSICAL_LAYER_10GBASE_XAUI BIT(6) + /* SFP+ module type IDs: * * ID Module Type @@ -188,11 +280,22 @@ struct txgbe_phy_info { u32 orig_vr_xs_or_pcs_mmd_digi_ctl1; bool orig_link_settings_stored; bool multispeed_fiber; + bool autotry_restart; + u32 autoneg_advertised; + u32 link_mode; +}; + +/* link status for KX/KX4 */ +enum txgbe_link_status { + TXGBE_LINK_STATUS_NONE = 0, + TXGBE_LINK_STATUS_KX, + TXGBE_LINK_STATUS_KX4 }; struct txgbe_hw { struct wx_hw wxhw; struct txgbe_phy_info phy; + enum txgbe_link_status link_status; }; #endif /* _TXGBE_TYPE_H_ */ From patchwork Tue Nov 8 11:19:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mengyuan Lou X-Patchwork-Id: 13036163 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 56561C4332F for ; Tue, 8 Nov 2022 11:21:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233809AbiKHLVA (ORCPT ); Tue, 8 Nov 2022 06:21:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60338 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233895AbiKHLU6 (ORCPT ); Tue, 8 Nov 2022 06:20:58 -0500 Received: from smtpbgau1.qq.com (smtpbgau1.qq.com [54.206.16.166]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 68E6E48761 for ; Tue, 8 Nov 2022 03:20:53 -0800 (PST) X-QQ-mid: bizesmtp85t1667906380tfior9ei Received: from localhost.localdomain ( [183.129.236.74]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 08 Nov 2022 19:19:39 +0800 (CST) X-QQ-SSF: 01400000000000M0M000000A0000000 X-QQ-FEAT: Yl5FOsZ5kXn6mUhwivhji20Fj04SM4kHqIYUxufQYPzAmIIjxKAixWwQNMiNW aBrDrufPZ6kk+jdLn3SZ+T2tLLqkiHiAAO6iUvTjdpCq/w4kF+yOO7Qn7TK9KSMJiZ9FIbV YZ+1DFefedIimfmnLg98RLmTR5nDGc6Vrxb/ou38dpLOTDwtrXN4GqVzCLaVsZP05gmj6z0 /yElRAJ19izWSv3nXkFVBW7tE4sshsO4i5IB0oT4pijlNLveE3sLRVSmLE4V7i6ecixm3G1 IkIjlHNKndxKRz6pWjHb1FbjeUnsBG4GstGb4wxNzYlXOUbXvPWkpVZHlAkl9DMy3+uyZdt t5D5Chfz+GV51yy3tRE2ljofaPzObhYdfhb+IAOweBCwUhNvTSUTSTL0Ge/iKFpL0B7JGRz 79cILPsifOmotusy7pGo9w== X-QQ-GoodBg: 2 From: Mengyuan Lou To: netdev@vger.kernel.org Cc: jiawenwu@trustnetic.com, Mengyuan Lou Subject: [PATCH net-next 4/5] net: ngbe: Initialize phy information Date: Tue, 8 Nov 2022 19:19:06 +0800 Message-Id: <20221108111907.48599-5-mengyuanlou@net-swift.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221108111907.48599-1-mengyuanlou@net-swift.com> References: <20221108111907.48599-1-mengyuanlou@net-swift.com> MIME-Version: 1.0 X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:net-swift.com:qybglogicsvr:qybglogicsvr1 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Initialize phy media type. Initialize phy ops functions. Signed-off-by: Mengyuan Lou --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 3 +- drivers/net/ethernet/wangxun/libwx/wx_hw.h | 1 + drivers/net/ethernet/wangxun/ngbe/Makefile | 2 +- drivers/net/ethernet/wangxun/ngbe/ngbe.h | 2 + drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c | 31 + drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h | 1 + drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 28 +- drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c | 1113 +++++++++++++++++ drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h | 22 + drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 113 +- 10 files changed, 1305 insertions(+), 11 deletions(-) create mode 100644 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c create mode 100644 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 045d6e978598..fb76a19ba613 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -41,7 +41,7 @@ static int wx_fmgr_cmd_op(struct wx_hw *wxhw, u32 cmd, u32 cmd_addr) false, wxhw, WX_SPI_STATUS); } -static int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data) +int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data) { int ret = 0; @@ -53,6 +53,7 @@ static int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data) return ret; } +EXPORT_SYMBOL(wx_flash_read_dword); int wx_check_flash_load(struct wx_hw *hw, u32 check_bit) { diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h index 5058774381c1..f562dcfa2d0f 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -4,6 +4,7 @@ #ifndef _WX_HW_H_ #define _WX_HW_H_ +int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data); 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); diff --git a/drivers/net/ethernet/wangxun/ngbe/Makefile b/drivers/net/ethernet/wangxun/ngbe/Makefile index 391c2cbc1bb4..56bd8cf800c8 100644 --- a/drivers/net/ethernet/wangxun/ngbe/Makefile +++ b/drivers/net/ethernet/wangxun/ngbe/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_NGBE) += ngbe.o -ngbe-objs := ngbe_main.o ngbe_hw.o +ngbe-objs := ngbe_main.o ngbe_hw.o ngbe_phy.o diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe.h b/drivers/net/ethernet/wangxun/ngbe/ngbe.h index af147ca8605c..ac67c0403592 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe.h @@ -48,6 +48,8 @@ struct ngbe_adapter { struct ngbe_mac_addr *mac_table; u16 msg_enable; + DECLARE_BITMAP(flags, NGBE_FLAGS_NBITS); + /* Tx fast path data */ int num_tx_queues; u16 tx_itr_setting; diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c index 0e3923b3737e..274d54832579 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c @@ -38,6 +38,37 @@ int ngbe_eeprom_chksum_hostif(struct ngbe_hw *hw) return -EIO; } +int ngbe_phy_led_oem_hostif(struct ngbe_hw *hw, u32 *data) +{ + struct wx_hic_read_shadow_ram buffer; + struct wx_hw *wxhw = &hw->wxhw; + int status; + + buffer.hdr.req.cmd = NGBE_FW_PHY_LED_CONF; + buffer.hdr.req.buf_lenh = 0; + buffer.hdr.req.buf_lenl = 0; + buffer.hdr.req.checksum = NGBE_FW_CMD_DEFAULT_CHECKSUM; + + /* convert offset from words to bytes */ + buffer.address = 0; + /* one word */ + buffer.length = 0; + + status = wx_host_interface_command(wxhw, (u32 *)&buffer, sizeof(buffer), + WX_HI_COMMAND_TIMEOUT, false); + + if (status) + return status; + + *data = rd32a(wxhw, WX_MNG_MBOX, 1); + if (*data == NGBE_FW_CMD_ST_PASS) + *data = rd32a(wxhw, WX_MNG_MBOX, 2); + else + *data = 0xffffffff; + + return 0; +} + static int ngbe_reset_misc(struct ngbe_hw *hw) { struct wx_hw *wxhw = &hw->wxhw; diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h index 42476a3fe57c..cf4008a2b5ec 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h @@ -8,5 +8,6 @@ #define _NGBE_HW_H_ int ngbe_eeprom_chksum_hostif(struct ngbe_hw *hw); +int ngbe_phy_led_oem_hostif(struct ngbe_hw *hw, u32 *data); int ngbe_reset_hw(struct ngbe_hw *hw); #endif /* _NGBE_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index f0b24366da18..ebf3fcdc4719 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -76,16 +76,16 @@ static void ngbe_init_type_code(struct ngbe_hw *hw) hw->phy.type = ngbe_phy_m88e1512; break; case NGBE_SUBID_M88E1512_MIX: - hw->phy.type = ngbe_phy_m88e1512_unknown; + hw->phy.type = ngbe_phy_m88e1512_mix; break; case NGBE_SUBID_YT8521S_SFP: case NGBE_SUBID_YT8521S_SFP_GPIO: case NGBE_SUBID_LY_YT8521S_SFP: - hw->phy.type = ngbe_phy_yt8521s_sfi; + hw->phy.type = ngbe_phy_yt_mix; break; case NGBE_SUBID_INTERNAL_YT8521S_SFP: case NGBE_SUBID_INTERNAL_YT8521S_SFP_GPIO: - hw->phy.type = ngbe_phy_internal_yt8521s_sfi; + hw->phy.type = ngbe_phy_internal_yt_sfi; break; case NGBE_SUBID_RGMII_FPGA: case NGBE_SUBID_OCP_CARD: @@ -96,7 +96,7 @@ static void ngbe_init_type_code(struct ngbe_hw *hw) } if (hw->phy.type == ngbe_phy_internal || - hw->phy.type == ngbe_phy_internal_yt8521s_sfi) + hw->phy.type == ngbe_phy_internal_yt_sfi) hw->mac_type = ngbe_mac_type_mdi; else hw->mac_type = ngbe_mac_type_rgmii; @@ -116,6 +116,8 @@ static void ngbe_init_type_code(struct ngbe_hw *hw) hw->gpio_ctrl = 0; break; } + + hw->phy.id = 0; } /** @@ -203,6 +205,22 @@ static int ngbe_sw_init(struct ngbe_adapter *adapter) return 0; } +static void ngbe_oem_conf_in_rom(struct ngbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + + /* phy led oem conf*/ + if (ngbe_phy_led_oem_hostif(hw, &hw->led_conf)) + dev_err(&wxhw->pdev->dev, "The led_oem is not supported\n"); + + wx_flash_read_dword(wxhw, + 0xfe010 + wxhw->bus.func * 8, + &hw->phy.gphy_efuse[0]); + wx_flash_read_dword(wxhw, + 0xfe010 + wxhw->bus.func + 4, + &hw->phy.gphy_efuse[1]); +} + static void ngbe_down(struct ngbe_adapter *adapter) { netif_carrier_off(adapter->netdev); @@ -442,6 +460,8 @@ static int ngbe_probe(struct pci_dev *pdev, } } + ngbe_oem_conf_in_rom(hw); + adapter->wol = 0; if (hw->wol_enabled) adapter->wol = NGBE_PSR_WKUP_CTL_MAG; diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c new file mode 100644 index 000000000000..874d929285b0 --- /dev/null +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c @@ -0,0 +1,1113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#include +#include +#include + +#include "../libwx/wx_type.h" +#include "../libwx/wx_hw.h" +#include "ngbe_type.h" +#include "ngbe_phy.h" +#include "ngbe.h" + +static u16 ngbe_phy_read_reg_internal(struct ngbe_hw *hw, u32 reg_addr) +{ + u16 phy_data = 0; + + phy_data = (u16)rd32(&hw->wxhw, NGBE_PHY_CONFIG(reg_addr)); + return phy_data; +} + +static void ngbe_phy_write_reg_internal(struct ngbe_hw *hw, u32 reg_addr, u16 phy_data) +{ + wr32(&hw->wxhw, NGBE_PHY_CONFIG(reg_addr), phy_data); +} + +/** + * ngbe_phy_read_reg_mdi - Reads a val from an external PHY register + * @hw: pointer to hardware structure + * @reg_addr: 32 bit address of PHY register to read + **/ +static u16 ngbe_phy_read_reg_mdi(struct ngbe_hw *hw, u32 reg_addr) +{ + u32 command = 0, device_type = 0; + struct wx_hw *wxhw = &hw->wxhw; + u32 phy_addr = 0; + u16 phy_data = 0; + u32 val = 0; + int ret = 0; + + /* setup and write the address cycle command */ + command = NGBE_MSCA_RA(reg_addr) | + NGBE_MSCA_PA(phy_addr) | + NGBE_MSCA_DA(device_type); + wr32(wxhw, NGBE_MSCA, command); + + command = NGBE_MSCC_CMD(NGBE_MSCA_CMD_READ) | + NGBE_MSCC_BUSY | + NGBE_MDIO_CLK(6); + wr32(wxhw, NGBE_MSCC, command); + + /* wait to complete */ + ret = read_poll_timeout(rd32, val, val & NGBE_MSCC_BUSY, 1000, + 20000, false, wxhw, NGBE_MSCC); + if (ret) + wx_dbg(wxhw, "PHY address command did not complete.\n"); + + /* read data from MSCC */ + phy_data = (u16)rd32(wxhw, NGBE_MSCC); + + return phy_data; +} + +/** + * ngbe_phy_write_reg_mdi - Writes a val to external PHY register + * @hw: pointer to hardware structure + * @reg_addr: 32 bit PHY register to write + * @phy_data: Data to write to the PHY register + **/ +static void ngbe_phy_write_reg_mdi(struct ngbe_hw *hw, u32 reg_addr, u16 phy_data) +{ + u32 command = 0, device_type = 0; + struct wx_hw *wxhw = &hw->wxhw; + u32 phy_addr = 0; + int ret = 0; + u16 val = 0; + + /* setup and write the address cycle command */ + command = NGBE_MSCA_RA(reg_addr) | + NGBE_MSCA_PA(phy_addr) | + NGBE_MSCA_DA(device_type); + wr32(wxhw, NGBE_MSCA, command); + + command = phy_data | + NGBE_MSCC_CMD(NGBE_MSCA_CMD_WRITE) | + NGBE_MSCC_BUSY | + NGBE_MDIO_CLK(6); + wr32(wxhw, NGBE_MSCC, command); + + /* wait to complete */ + ret = read_poll_timeout(rd32, val, val & NGBE_MSCC_BUSY, 1000, + 20000, false, wxhw, NGBE_MSCC); + if (ret) + wx_dbg(wxhw, "PHY address command did not complete.\n"); +} + +static u16 ngbe_phy_read_reg_ext_yt(struct ngbe_hw *hw, + u32 reg_addr) +{ + u16 val = 0; + + ngbe_phy_write_reg_mdi(hw, 0x1e, reg_addr); + val = ngbe_phy_read_reg_mdi(hw, 0x1f); + + return val; +} + +static void ngbe_phy_write_reg_ext_yt(struct ngbe_hw *hw, + u32 reg_addr, + u16 phy_data) +{ + ngbe_phy_write_reg_mdi(hw, 0x1e, reg_addr); + ngbe_phy_write_reg_mdi(hw, 0x1f, phy_data); +} + +static u16 ngbe_phy_read_reg_sds_ext_yt(struct ngbe_hw *hw, + u32 reg_addr) +{ + u16 val = 0; + + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x02); + val = ngbe_phy_read_reg_ext_yt(hw, reg_addr); + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x00); + + return val; +} + +static void ngbe_phy_write_reg_sds_ext_yt(struct ngbe_hw *hw, + u32 reg_addr, + u16 phy_data) +{ + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x02); + ngbe_phy_write_reg_ext_yt(hw, reg_addr, phy_data); + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x00); +} + +static u16 ngbe_phy_read_reg_sds_mii_yt(struct ngbe_hw *hw, + u32 reg_addr) +{ + u16 val = 0; + + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x02); + val = ngbe_phy_read_reg_mdi(hw, reg_addr); + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x00); + + return val; +} + +static void ngbe_phy_write_reg_sds_mii_yt(struct ngbe_hw *hw, + u32 reg_addr, + u16 phy_data) +{ + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x02); + ngbe_phy_write_reg_mdi(hw, reg_addr, phy_data); + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x00); +} + +static void ngbe_phy_led_ctrl_mv(struct ngbe_hw *hw) +{ + u16 val = 0; + + if (hw->led_conf == 0xffffffff) { + /* LED control */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 3); + val = ngbe_phy_read_reg(hw, NGBE_PHY_LED_FUNC_CTRL_REG_MV); + val &= ~0x00FF; + val |= (NGBE_LED1_CONF_MV << 4) | NGBE_LED0_CONF_MV; + ngbe_phy_write_reg(hw, NGBE_PHY_LED_FUNC_CTRL_REG_MV, val); + val = ngbe_phy_read_reg(hw, NGBE_PHY_LED_POL_CTRL_REG_MV); + val &= ~0x000F; + val |= (NGBE_LED1_POL_MV << 2) | NGBE_LED0_POL_MV; + ngbe_phy_write_reg(hw, NGBE_PHY_LED_POL_CTRL_REG_MV, val); + } +} + +static void ngbe_phy_led_ctrl_internal(struct ngbe_hw *hw) +{ + u16 val = 0; + + if (hw->led_conf != 0xffffffff) + val = hw->led_conf & 0xffff; + else + val = 0x205B; + + /* select page to 0xd04 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xd04); + ngbe_phy_write_reg(hw, 0x10, val); + ngbe_phy_write_reg(hw, 0x11, 0x0); + + val = ngbe_phy_read_reg(hw, 0x12); + if (hw->led_conf != 0xffffffff) { + val &= ~0x73; + val |= hw->led_conf >> 16; + } else { + val = val & 0xFFFC; + /*act led blinking mode set to 60ms*/ + val |= 0x2; + } + ngbe_phy_write_reg(hw, 0x12, val); +} + +static int ngbe_gphy_wait_mdio_access_on(struct ngbe_hw *hw) +{ + u16 val = 0; + int ret = 0; + + /* select page to 0xa43*/ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43); + /* wait to phy can access */ + ret = read_poll_timeout(ngbe_phy_read_reg, val, val & 0x20, 1000, + 100000, false, hw, 0x1d); + + if (ret) + wx_dbg(&hw->wxhw, "Access to phy timeout\n"); + + return ret; +} + +static int ngbe_phy_init_m88e1512(struct ngbe_hw *hw) +{ + u16 val = 0; + int ret = 0; + + /* select page to 0x2 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x2); + val = ngbe_phy_read_reg(hw, 0x15); + val &= ~NGBE_RGM_TTC_MV; + val |= NGBE_RGM_RTC_MV; + ngbe_phy_write_reg(hw, 0x15, val); + + /* phy reset */ + ret = ngbe_phy_reset(hw); + if (!ret) + return ret; + + /* set LED2 to interrupt output and INTn active low */ + /* select page to 0x3 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x3); + val = ngbe_phy_read_reg(hw, NGBE_PHY_INTM_REG); + val |= NGBE_INT_EN_MV; + val &= ~(NGBE_INT_POL_MV); + ngbe_phy_write_reg(hw, NGBE_PHY_INTM_REG, val); + + return 0; +} + +static int ngbe_get_phy_media_type(struct ngbe_hw *hw) +{ + u8 phy_mode = 0; + u32 val = 0; + + if (hw->phy.media_type != ngbe_media_type_unknown) + return 0; + + switch (hw->phy.type) { + case ngbe_phy_internal_yt_sfi: + case ngbe_phy_internal: + case ngbe_phy_m88e1512: + hw->phy.media_type = ngbe_media_type_copper; + break; + case ngbe_phy_m88e1512_sfi: + hw->phy.media_type = ngbe_media_type_fiber; + break; + case ngbe_phy_m88e1512_mix: + case ngbe_phy_yt_mix: + hw->phy.media_type = ngbe_media_type_unknown; + break; + default: + return -EINVAL; + } + + if (hw->phy.media_type) + return 0; + + if (hw->phy.type == ngbe_phy_yt_mix) { + ngbe_phy_write_reg(hw, 0x1e, 0xa001); + val = ngbe_phy_read_reg(hw, 0x1f); + phy_mode = val & 0x7; + switch (phy_mode) { + case 0: + hw->phy.media_type = ngbe_media_type_copper; + break; + case 1: + case 2: + hw->phy.media_type = ngbe_media_type_fiber; + break; + case 4: + case 5: + hw->phy.media_type = ngbe_media_type_backplane; + break; + default: + hw->phy.media_type = ngbe_media_type_unknown; + return -EINVAL; + } + } else if (hw->phy.type == ngbe_phy_m88e1512_mix) { + wx_flash_read_dword(&hw->wxhw, 0xff010, &val); + + phy_mode = (u8)(val >> (8 * hw->wxhw.bus.func)); + phy_mode = phy_mode & 0x7; + switch (phy_mode) { + case 0: + hw->phy.media_type = ngbe_media_type_copper; + break; + case 2: + hw->phy.media_type = ngbe_media_type_fiber; + break; + default: + hw->phy.media_type = ngbe_media_type_unknown; + return -EINVAL; + } + } + + hw->phy.phy_mode = phy_mode; + return 0; +} + +static void ngbe_check_phy_id(struct ngbe_hw *hw) +{ + u16 phy_id_high = 0, phy_id_low = 0; + u32 phy_id = 0xffffffff; + + phy_id_high = ngbe_phy_read_reg(hw, NGBE_PHY_ID1_REG); + phy_id_low = ngbe_phy_read_reg(hw, NGBE_PHY_ID2_REG); + + phy_id = phy_id_high << 6; + phy_id |= (phy_id_low & NGBE_PHY_ID_MASK) >> 10; + + /* for yt 8521s phy id is 0 */ + if (!phy_id) { + if (phy_id_low) + hw->phy.id = phy_id_low; + else + wx_dbg(&hw->wxhw, "Can not get phy id.\n"); + } + hw->phy.id = phy_id; +} + +/** + * ngbe_phy_identify - Identifies phy + * @hw: pointer to hardware structure + * + * Check whether the phy is accessible + **/ +static int ngbe_phy_identify(struct ngbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + u32 phy_id = 0; + int ret = 0; + + if (hw->phy.id) + return ret; + switch (hw->phy.type) { + case ngbe_phy_internal: + case ngbe_phy_internal_yt_sfi: + ngbe_gphy_wait_mdio_access_on(hw); + phy_id = NGBE_PHY_ID_INTERNAL; + break; + case ngbe_phy_m88e1512: + case ngbe_phy_m88e1512_sfi: + case ngbe_phy_m88e1512_mix: + phy_id = NGBE_PHY_ID_MV; + break; + case ngbe_phy_yt_mix: + phy_id = NGBE_PHY_ID_YT8521S | NGBE_PHY_ID_YT8531S; + break; + default: + ret = -EINVAL; + } + + ngbe_check_phy_id(hw); + if ((hw->phy.id & phy_id) != hw->phy.id) { + wx_err(wxhw, "Phy id 0x%x not supported.\n", phy_id); + return -EINVAL; + } + + return ret; +} + +/** + * ngbe_phy_init - PHY specific init + * @hw: pointer to hardware structure + * + * Check phy id, Initialize phy mode and media type, Enable the required interrupt. + **/ +int ngbe_phy_init(struct ngbe_hw *hw) +{ + int ret = 0; + u16 val = 0; + + /* Identify the PHY*/ + ret = ngbe_phy_identify(hw); + if (ret) + return ret; + + ret = ngbe_get_phy_media_type(hw); + if (ret) { + wx_err(&hw->wxhw, "The phy mode is not supported.\n"); + return ret; + } + + switch (hw->phy.type) { + case ngbe_phy_internal: + case ngbe_phy_internal_yt_sfi: + val = NGBE_PHY_INT_STATUS_LSC_INTERNAL | + NGBE_PHY_INT_STATUS_ANC_INTERNAL; + /* select page to 0xa42 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa42); + break; + case ngbe_phy_m88e1512: + ngbe_phy_init_m88e1512(hw); + /* select page to 0x0 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x0); + /* enable link status change and AN complete interrupts */ + val = NGBE_PHY_INT_STATUS_ANC_MV | NGBE_PHY_INT_STATUS_LSC_MV; + break; + case ngbe_phy_m88e1512_sfi: + ngbe_phy_init_m88e1512(hw); + /* select page to 0x1 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x1); + val = ngbe_phy_read_reg(hw, 0x10); + val &= ~0x4; + ngbe_phy_write_reg(hw, 0x10, val); + + /* enable link status change and AN complete interrupts */ + val = NGBE_PHY_INT_STATUS_ANC_MV | NGBE_PHY_INT_STATUS_LSC_MV; + break; + case ngbe_phy_yt_mix: + /* select sds area register */ + ngbe_phy_write_reg(hw, 0x1e, 0xa000); + ngbe_phy_write_reg(hw, 0x1f, 0x0); + + /* enable interrupt */ + val = NGBE_PHY_INT_STATUS_SDSLNKUP_YT | + NGBE_PHY_INT_STATUS_SDSLNKDN_YT | + NGBE_PHY_INT_STATUS_UTPLNKUP_YT | + NGBE_PHY_INT_STATUS_UTPLNKDN_YT; + break; + default: + ret = -EINVAL; + } + /* write interrupts bits to register */ + ngbe_phy_write_reg(hw, NGBE_PHY_INTM_REG, val); + + return ret; +} + +static void ngbe_phy_setup_link_copper(struct ngbe_hw *hw, + u32 speed) +{ + u16 value_r4 = 0, value_r9 = 0; + u16 val = 0; + + if (!hw->phy.autoneg) { + switch (speed) { + case SPEED_1000: + val = NGBE_PHY_SPEED_SELECT1; + break; + case SPEED_100: + val = NGBE_PHY_SPEED_SELECT0; + break; + case SPEED_10: + val = 0; + break; + default: + val = NGBE_PHY_SPEED_SELECT1 | NGBE_PHY_SPEED_SELECT0; + wx_dbg(&hw->wxhw, "unknown speed = 0x%x.\n", speed); + } + /* duplex full */ + val |= NGBE_PHY_DUPLEX; + ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val); + } + if (speed & SPEED_1000) + value_r9 |= NGBE_PHY_1000BASET_FULL; + if (speed & SPEED_100) + value_r4 |= NGBE_PHY_100BASETX_FULL; + if (speed & SPEED_10) + value_r4 |= NGBE_PHY_10BASET_FULL; + + val = ngbe_phy_read_reg(hw, NGBE_PHY_ANAR_REG); + /* clear all speed 100/10 full/half bit mask */ + val &= ~(NGBE_PHY_100BASETX_HALF | + NGBE_PHY_100BASETX_FULL | + NGBE_PHY_10BASET_HALF | + NGBE_PHY_10BASET_FULL); + /* set current speed 100/10 bit mask */ + value_r4 |= val; + ngbe_phy_write_reg(hw, NGBE_PHY_ANAR_REG, value_r4); + + val = ngbe_phy_read_reg(hw, NGBE_PHY_GBCR_REG); + /* clear all speed 1000 full/half bit mask */ + val &= ~(NGBE_PHY_1000BASET_HALF | + NGBE_PHY_1000BASET_FULL); + /* set current speed 1000 bit mask */ + value_r9 |= val; + ngbe_phy_write_reg(hw, NGBE_PHY_GBCR_REG, value_r9); +} + +static void ngbe_phy_dis_eee_internal(struct ngbe_hw *hw) +{ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa4b); + ngbe_phy_write_reg(hw, 0x11, 0x1110); + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0x0); + ngbe_phy_write_reg(hw, 0xd, 0x0007); + ngbe_phy_write_reg(hw, 0xe, 0x003c); + ngbe_phy_write_reg(hw, 0xd, 0x4007); + ngbe_phy_write_reg(hw, 0xe, 0x0000); +} + +static int ngbe_gphy_efuse_calibration(struct ngbe_hw *hw) +{ + u32 efuse[2] = {0, 0}; + + ngbe_gphy_wait_mdio_access_on(hw); + + efuse[0] = hw->phy.gphy_efuse[0]; + efuse[1] = hw->phy.gphy_efuse[1]; + + if (!efuse[0] && !efuse[1]) { + efuse[0] = 0xFFFFFFFF; + efuse[1] = 0xFFFFFFFF; + } + + /* calibration */ + efuse[0] |= 0xF0000100; + efuse[1] |= 0xFF807FFF; + + /* EODR, Efuse Output Data Register */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa46); + ngbe_phy_write_reg(hw, 16, (efuse[0] >> 0) & 0xFFFF); + ngbe_phy_write_reg(hw, 17, (efuse[0] >> 16) & 0xFFFF); + ngbe_phy_write_reg(hw, 18, (efuse[1] >> 0) & 0xFFFF); + ngbe_phy_write_reg(hw, 19, (efuse[1] >> 16) & 0xFFFF); + + /* set efuse ready */ + ngbe_phy_write_reg(hw, 20, 0x01); + ngbe_gphy_wait_mdio_access_on(hw); + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43); + ngbe_phy_write_reg(hw, 27, 0x8011); + ngbe_phy_write_reg(hw, 28, 0x5737); + ngbe_phy_dis_eee_internal(hw); + + return 0; +} + +static void ngbe_phy_setup_powerup(struct ngbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + u16 val = 0; + int ret = 0; + + ret = read_poll_timeout(rd32, val, + !(val & (BIT(9) << wxhw->bus.func)), 1000, + 100000, false, wxhw, 0x10028); + + if (ret) + wx_dbg(wxhw, "Lan reset exceeds maximum times.\n"); + + ngbe_gphy_efuse_calibration(hw); + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa46); + /* set efuse ready */ + ngbe_phy_write_reg(hw, 20, 0x02); + ngbe_gphy_wait_mdio_access_on(hw); + + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa42); + ret = read_poll_timeout(ngbe_phy_read_reg, val, ((val & 0x7) == 3), + 1000, 20000, false, hw, 0x10); + + if (ret) + wx_dbg(wxhw, "PHY reset exceeds maximum times.\n"); +} + +static int ngbe_phy_setup_link_internal(struct ngbe_hw *hw, + u32 speed, + bool need_restart_AN) +{ + u16 val = 0; + + ngbe_phy_setup_powerup(hw); + /* select page to 0x0 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0x0); + ngbe_phy_setup_link_copper(hw, speed); + + val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG); + if (hw->phy.autoneg) { + /* restart AN and wait AN done interrupt */ + if (hw->ncsi_enabled) { + val |= NGBE_PHY_ANE; + if (need_restart_AN) + val |= NGBE_PHY_ANE; + } else { + val |= NGBE_PHY_RESTART_AN | NGBE_PHY_ANE; + } + } + ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val); + ngbe_phy_led_ctrl_internal(hw); + ngbe_check_phy_event(hw); + + return 0; +} + +static int ngbe_phy_setup_link_mv(struct ngbe_hw *hw, + u32 speed, + bool autoneg_wait_to_complete) +{ + u16 val; + + if (hw->phy.media_type == ngbe_media_type_copper) { + ngbe_phy_setup_link_copper(hw, speed); + val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG); + if (hw->phy.autoneg) + val = NGBE_PHY_RESTART_AN | NGBE_PHY_ANE; + val |= NGBE_PHY_RESET; + ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val); + } else { + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 1); + val = ngbe_phy_read_reg(hw, NGBE_PHY_ANAR_REG); + val |= NGBE_PHY_1000BASEX_FULL_MV; + ngbe_phy_write_reg(hw, NGBE_PHY_ANAR_REG, val); + + val = NGBE_PHY_RESET | + NGBE_PHY_DUPLEX | + NGBE_PHY_SPEED_SELECT1; + if (hw->phy.autoneg) + val |= NGBE_PHY_RESTART_AN | + NGBE_PHY_ANE; + ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val); + } + /* sw reset power down bit is retain */ + val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG); + val &= ~NGBE_PHY_POWER_DOWN; + ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val); + ngbe_phy_led_ctrl_mv(hw); + ngbe_check_phy_event(hw); + + return 0; +} + +static int ngbe_phy_setup_link_yt(struct ngbe_hw *hw, + u32 speed, + bool autoneg_wait_to_complete) +{ + u16 value_r4 = 0; + u16 value_r9 = 0; + int ret_val = 0; + u16 val; + + hw->phy.autoneg_advertised = 0; + if (hw->phy.phy_mode == 0) {/* utp_to_rgmii */ + ngbe_phy_setup_link_copper(hw, speed); + /* software reset to make the above configuration take effect*/ + val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG); + if (hw->phy.autoneg) + val = NGBE_PHY_RESTART_AN | NGBE_PHY_ANE; + val |= NGBE_PHY_RESET; + ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val); + } else if (hw->phy.phy_mode == 1) {/* fiber_to_rgmii */ + if (!hw->phy.autoneg) { + switch (speed) { + case SPEED_1000: + val = SPEED_1000; + break; + case SPEED_100: + val = SPEED_100; + break; + default: + val = SPEED_1000; + break; + } + + val = ngbe_phy_read_reg_ext_yt(hw, 0xA006); + if (hw->phy.autoneg_advertised & SPEED_1000) + val |= 0x1; + else if (hw->phy.autoneg_advertised & SPEED_100) + val &= ~0x1; + ngbe_phy_write_reg_ext_yt(hw, 0xA006, val); + + /* close auto sensing */ + val = ngbe_phy_read_reg_sds_ext_yt(hw, 0xA5); + val &= ~0x8000; + ngbe_phy_write_reg_sds_ext_yt(hw, 0xA5, val); + + val = ngbe_phy_read_reg_ext_yt(hw, 0xA001); + val &= ~0x8000; + ngbe_phy_write_reg_ext_yt(hw, 0xA001, val); + + goto skip_an_fiber; + } + + /* open auto sensing */ + val = ngbe_phy_read_reg_sds_ext_yt(hw, 0xA5); + val |= 0x8000; + ngbe_phy_write_reg_sds_ext_yt(hw, 0xA5, val); + + val = ngbe_phy_read_reg_ext_yt(hw, 0xA006); + val |= 0x1; + ngbe_phy_write_reg_ext_yt(hw, 0xA006, val); +skip_an_fiber: + /* RGMII_Config1 : Config rx and tx training delay */ + ngbe_phy_write_reg_ext_yt(hw, 0xA003, 0x3cf1); + ngbe_phy_write_reg_ext_yt(hw, 0xA001, 0x8041); + + /* software reset */ + if (hw->phy.autoneg) + ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, 0x9340); + else + ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, 0x8140); + } else if (hw->phy.phy_mode == 2) { + /* power on in UTP mode */ + val = ngbe_phy_read_reg(hw, 0x0); + val &= ~0x800; + ngbe_phy_write_reg(hw, 0x0, val); + + /* power on in Fiber mode */ + val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x0); + val &= ~0x800; + ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val); + + val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x11); + + if (val & 0x400) { /* fiber up */ + hw->phy.autoneg_advertised |= SPEED_1000; + } else { /* utp up */ + value_r4 = 0x1E0; + value_r9 = 0x300; + /*disable 100/10base-T Self-negotiation ability*/ + val = ngbe_phy_read_reg_mdi(hw, 0x4); + val &= ~value_r4; + ngbe_phy_write_reg_mdi(hw, 0x4, val); + + /*disable 1000base-T Self-negotiation ability*/ + val = ngbe_phy_read_reg_mdi(hw, 0x9); + val &= ~value_r9; + ngbe_phy_write_reg_mdi(hw, 0x9, val); + + value_r4 = 0x0; + value_r9 = 0x0; + + if (speed & SPEED_1000) { + hw->phy.autoneg_advertised |= SPEED_1000; + value_r9 |= 0x200; + } + if (speed & SPEED_100) { + hw->phy.autoneg_advertised |= SPEED_1000; + value_r4 |= 0x100; + } + if (speed & SPEED_10) { + hw->phy.autoneg_advertised |= SPEED_1000; + value_r4 |= 0x40; + } + + /* enable 1000base-T Self-negotiation ability */ + val = ngbe_phy_read_reg_mdi(hw, 0x9); + val |= value_r9; + ngbe_phy_write_reg_mdi(hw, 0x9, val); + + /* enable 100/10base-T Self-negotiation ability */ + val = ngbe_phy_read_reg_mdi(hw, 0x4); + val |= value_r4; + ngbe_phy_write_reg_mdi(hw, 0x4, val); + + /* software reset to make the above configuration take effect*/ + val = ngbe_phy_read_reg_mdi(hw, 0x0); + val |= 0x8000; + ngbe_phy_write_reg_mdi(hw, 0x0, val); + } + } else if (hw->phy.phy_mode == 4) { + hw->phy.autoneg_advertised |= SPEED_1000; + + val = ngbe_phy_read_reg_ext_yt(hw, 0xA003); + val |= 0x8000; + ngbe_phy_write_reg_ext_yt(hw, 0xA003, val); + + val = ngbe_phy_read_reg_ext_yt(hw, 0xA004); + val &= ~0xf0; + val |= 0xb0; + ngbe_phy_write_reg_ext_yt(hw, 0xA004, val); + + val = ngbe_phy_read_reg_ext_yt(hw, 0xA001); + val &= ~0x8000; + ngbe_phy_write_reg_ext_yt(hw, 0xA001, val); + + /* power on phy */ + val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x0); + val &= ~0x800; + ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val); + } else if (hw->phy.phy_mode == 5) {/* sgmii_to_rgmii */ + if (!hw->phy.autoneg) { + switch (speed) { + case SPEED_1000: + val = NGBE_PHY_SPEED_SELECT1; + break; + case SPEED_100: + val = NGBE_PHY_SPEED_SELECT0; + break; + case SPEED_10: + val = 0; + break; + default: + val = NGBE_PHY_SPEED_SELECT0 | NGBE_PHY_SPEED_SELECT1; + wx_dbg(&hw->wxhw, "unknown speed = 0x%x.\n", speed); + break; + } + /* duplex full */ + val |= NGBE_PHY_DUPLEX | NGBE_PHY_RESET; + ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val); + + goto skip_an_sr; + } + + val = 0; + if (speed & SPEED_1000) + val |= 0x40; + if (speed & SPEED_100) + val |= 0x2000; + if (speed & SPEED_100) + val |= 0x0; + /* duplex full */ + val |= NGBE_PHY_DUPLEX | 0x8000; + ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val); + + /* software reset to make the above configuration take effect */ + val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x0); + val |= 0x9200; + ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val); +skip_an_sr: + /* power on in UTP mode */ + val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x0); + val &= ~0x800; + ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val); + } + ngbe_check_phy_event(hw); + + return ret_val; +} + +/** + * ngbe_check_phy_link_internal - Determine link and speed status + * @hw: pointer to hardware structure + * @speed: pointer to link speed + * @link_up: true when link is up + * @link_up_wait_to_complete: bool used to wait for link up or not + * + * Reads the links register to determine if link is up and the current speed + **/ +static void ngbe_check_phy_link_internal(struct ngbe_hw *hw, + u32 *speed, + bool *link_up, + bool link_up_wait_to_complete) +{ + u16 speed_sta = 0; + u16 val = 0; + + /* select page to 0xa43 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43); + val = ngbe_phy_read_reg(hw, NGBE_PHY_PHYSR_REG_INTERNAL); + if (val & 0x4) + *link_up = true; + else + *link_up = false; + + speed_sta = val & 0x38; + if (*link_up) { + if (speed_sta == 0x28) + *speed = SPEED_1000; + else if (speed_sta == 0x18) + *speed = SPEED_100; + else if (speed_sta == 0x8) + *speed = SPEED_10; + } else { + *speed = 0; + } + if (*speed == SPEED_1000) { + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43); + val = ngbe_phy_read_reg(hw, 0xa); + if (!(val & 0x2000)) + *link_up = false; + } +} + +static void ngbe_check_phy_link_mv(struct ngbe_hw *hw, + u32 *speed, + bool *link_up, + bool link_up_wait_to_complete) +{ + u16 speed_sta = 0; + u16 val = 0; + + if (hw->phy.media_type == ngbe_media_type_copper) + /* select page to 0x0 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x0); + else + /* select page to 0x1 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x1); + val = ngbe_phy_read_reg(hw, 0x11); + if (val & 0x400) + *link_up = true; + else + *link_up = false; + + speed_sta = val & 0xC000; + if (*link_up) { + if (speed_sta == 0x8000) + *speed = SPEED_1000; + else if (speed_sta == 0x4000) + *speed = SPEED_100; + else if (speed_sta == 0x0000) + *speed = SPEED_10; + } else { + *speed = 0; + } +} + +static void ngbe_check_phy_link_yt(struct ngbe_hw *hw, + u32 *speed, + bool *link_up, + bool link_up_wait_to_complete) +{ + u16 speed_sta = 0; + u16 val = 0; + + val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x11); + if (val & 0x400) { + *link_up = true; + } else { + *link_up = false; + + val = ngbe_phy_read_reg(hw, 0x11); + if (val & 0x400) + *link_up = true; + else + *link_up = false; + } + + speed_sta = val & 0xC000; + if (*link_up) { + if (speed_sta == 0x8000) { + *speed = SPEED_1000; + wr32m(&hw->wxhw, NGBE_CFG_LED_CTL, 0xE | BIT(17), BIT(1) | BIT(17)); + } else if (speed_sta == 0x4000) { + *speed = SPEED_100; + wr32m(&hw->wxhw, NGBE_CFG_LED_CTL, 0xE | BIT(17), BIT(2) | BIT(17)); + } else if (speed_sta == 0x0000) { + *speed = SPEED_10; + wr32m(&hw->wxhw, NGBE_CFG_LED_CTL, 0xE | BIT(17), BIT(3) | BIT(17)); + } + } else { + *speed = 0; + wr32m(&hw->wxhw, NGBE_CFG_LED_CTL, 0xE | BIT(17), 0); + } +} + +u16 ngbe_phy_read_reg(struct ngbe_hw *hw, u32 reg_addr) +{ + u16 phy_data = 0; + + if (hw->mac_type == ngbe_mac_type_mdi) + phy_data = ngbe_phy_read_reg_internal(hw, reg_addr); + else if (hw->mac_type == ngbe_mac_type_rgmii) + phy_data = ngbe_phy_read_reg_mdi(hw, reg_addr); + + return phy_data; +} + +void ngbe_phy_write_reg(struct ngbe_hw *hw, u32 reg_addr, u16 phy_data) +{ + if (hw->mac_type == ngbe_mac_type_mdi) + ngbe_phy_write_reg_internal(hw, reg_addr, phy_data); + else if (hw->mac_type == ngbe_mac_type_rgmii) + ngbe_phy_write_reg_mdi(hw, reg_addr, phy_data); +} + +int ngbe_phy_setup_link(struct ngbe_hw *hw, + u32 speed, + bool autoneg_wait_to_complete) +{ + int ret = 0; + + switch (hw->phy.type) { + case ngbe_phy_m88e1512: + case ngbe_phy_m88e1512_sfi: + case ngbe_phy_m88e1512_mix: + ret = ngbe_phy_setup_link_mv(hw, speed, false); + break; + case ngbe_phy_yt_mix: + ret = ngbe_phy_setup_link_yt(hw, speed, false); + break; + case ngbe_phy_internal_yt_sfi: + case ngbe_phy_internal: + ret = ngbe_phy_setup_link_internal(hw, speed, false); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int ngbe_phy_check_link(struct ngbe_hw *hw, + u32 *speed, + bool *link_up, + bool link_up_wait_to_complete) +{ + int ret = 0; + + switch (hw->phy.type) { + case ngbe_phy_m88e1512_sfi: + case ngbe_phy_m88e1512: + ngbe_check_phy_link_mv(hw, speed, link_up, false); + break; + case ngbe_phy_yt_mix: + ngbe_check_phy_link_yt(hw, speed, link_up, false); + break; + case ngbe_phy_internal_yt_sfi: + case ngbe_phy_internal: + /* select page to 0x0 */ + ngbe_check_phy_link_internal(hw, speed, link_up, false); + break; + default: + ret = -EIO; + break; + } + + return ret; +} + +int ngbe_phy_reset(struct ngbe_hw *hw) +{ + int ret = 0; + u16 val = 0; + + switch (hw->phy.type) { + case ngbe_phy_m88e1512_sfi: + /* select page to 0x1 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x1); + break; + case ngbe_phy_m88e1512: + /* select page to 0x0 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x0); + break; + case ngbe_phy_yt_mix: + if (hw->phy.media_type == ngbe_media_type_fiber) { + ngbe_phy_write_reg(hw, 0x1e, 0xa000); + ngbe_phy_write_reg(hw, 0x1f, 0x2); + } + break; + case ngbe_phy_internal_yt_sfi: + case ngbe_phy_internal: + /* select page to 0x0 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0x0); + break; + default: + ret = -EIO; + } + val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG); + val |= NGBE_PHY_RESET; + ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val); + + ret = read_poll_timeout(ngbe_phy_read_reg, val, val & NGBE_PHY_RESET, 1000, + 100000, false, hw, 0x0); + + if (!ret) + wx_err(&hw->wxhw, + "phy reset exceeds maximum waiting period.\n"); + + return ret; +} + +void ngbe_check_phy_event(struct ngbe_hw *hw) +{ + struct ngbe_adapter *adapter = + container_of(hw, struct ngbe_adapter, hw); + u16 int_mask = 0; + u16 val = 0; + + switch (hw->phy.type) { + case ngbe_phy_m88e1512: + int_mask = NGBE_PHY_INT_STATUS_ANC_MV | + NGBE_PHY_INT_STATUS_LSC_MV; + /* select page to 0x0 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x0); + val = ngbe_phy_read_reg(hw, NGBE_PHY_INT_STATUS_REG_MV); + break; + case ngbe_phy_m88e1512_sfi: + int_mask = NGBE_PHY_INT_STATUS_ANC_MV | + NGBE_PHY_INT_STATUS_LSC_MV; + /* select page to 0x1 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x1); + val = ngbe_phy_read_reg(hw, NGBE_PHY_INT_STATUS_REG_MV); + break; + case ngbe_phy_yt_mix: + int_mask = NGBE_PHY_INT_STATUS_SDSLNKUP_YT | + NGBE_PHY_INT_STATUS_SDSLNKDN_YT | + NGBE_PHY_INT_STATUS_UTPLNKUP_YT | + NGBE_PHY_INT_STATUS_UTPLNKDN_YT; + ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x0); + val = ngbe_phy_read_reg(hw, NGBE_PHY_INT_STATUS_REG_YT); + break; + case ngbe_phy_internal_yt_sfi: + case ngbe_phy_internal: + int_mask = NGBE_PHY_INT_STATUS_LSC_INTERNAL | + NGBE_PHY_INT_STATUS_ANC_INTERNAL; + /* select page to 0xa43 */ + ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43); + val = ngbe_phy_read_reg(hw, NGBE_PHY_INT_STATUS_REG_INTERNAL); + break; + default: + break; + } + + if (val | int_mask) + set_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags); +} diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h new file mode 100644 index 000000000000..5d94f00194cf --- /dev/null +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * WangXun Gigabit PCI Express Linux driver + * Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. + */ + +#ifndef _NGBE_PHY_H_ +#define _NGBE_PHY_H_ + +u16 ngbe_phy_read_reg(struct ngbe_hw *hw, u32 reg_addr); +void ngbe_phy_write_reg(struct ngbe_hw *hw, u32 reg_addr, u16 phy_data); +int ngbe_phy_init(struct ngbe_hw *hw); +int ngbe_phy_reset(struct ngbe_hw *hw); +int ngbe_phy_setup_link(struct ngbe_hw *hw, + u32 speed, + bool autoneg_wait_to_complete); +int ngbe_phy_check_link(struct ngbe_hw *hw, + u32 *speed, + bool *link_up, + bool link_up_wait_to_complete); +void ngbe_check_phy_event(struct ngbe_hw *hw); +#endif /* _NGBE_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h index 39f6c03f1a54..f6b257e84319 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -63,6 +63,26 @@ /* Media-dependent registers. */ #define NGBE_MDIO_CLAUSE_SELECT 0x11220 +/* mdio access */ +#define NGBE_MSCA 0x11200 +#define NGBE_MSCA_RA(v) ((0xFFFF & (v))) +#define NGBE_MSCA_PA(v) ((0x1F & (v)) << 16) +#define NGBE_MSCA_DA(v) ((0x1F & (v)) << 21) +#define NGBE_MSCC 0x11204 +#define NGBE_MSCC_DATA(v) ((0xFFFF & (v))) +#define NGBE_MSCC_CMD(v) ((0x3 & (v)) << 16) + +enum NGBE_MSCA_CMD_value { + NGBE_MSCA_CMD_RSV = 0, + NGBE_MSCA_CMD_WRITE, + NGBE_MSCA_CMD_POST_READ, + NGBE_MSCA_CMD_READ, +}; + +#define NGBE_MSCC_SADDR BIT(18) +#define NGBE_MSCC_BUSY BIT(22) +#define NGBE_MDIO_CLK(v) ((0x7 & (v)) << 19) + /* GPIO Registers */ #define NGBE_GPIO_DR 0x14800 #define NGBE_GPIO_DDR 0x14804 @@ -85,21 +105,95 @@ #define NGBE_PSR_WKUP_CTL_IPV6 BIT(7) /* Directed IPv6 Pkt Wakeup Enable */ #define NGBE_FW_EEPROM_CHECKSUM_CMD 0xE9 +#define NGBE_FW_PHY_LED_CONF 0xF1 #define NGBE_FW_NVM_DATA_OFFSET 3 #define NGBE_FW_CMD_DEFAULT_CHECKSUM 0xFF /* checksum always 0xFF */ #define NGBE_FW_CMD_ST_PASS 0x80658383 #define NGBE_FW_CMD_ST_FAIL 0x70657376 +/* NGBE phy base registers and BITs*/ +#define NGBE_PHY_BASE_CTRL_REG 0x0 +#define NGBE_PHY_ID1_REG 0x2 +#define NGBE_PHY_ID2_REG 0x3 +#define NGBE_PHY_ANAR_REG 0x4 +#define NGBE_PHY_GBCR_REG 0x9 +#define NGBE_PHY_INTM_REG 0x12 +#define NGBE_PHY_ID_MASK 0xFFFFFC00U + +/* NGBE_PHY_BASE_CTRL_REG bit mask*/ +#define NGBE_PHY_SPEED_SELECT1 BIT(6) +#define NGBE_PHY_DUPLEX BIT(8) +#define NGBE_PHY_RESTART_AN BIT(9) +#define NGBE_PHY_POWER_DOWN BIT(11) +#define NGBE_PHY_ANE BIT(12) +#define NGBE_PHY_SPEED_SELECT0 BIT(13) +#define NGBE_PHY_RESET BIT(15) + +/* NGBE_PHY_ANAR_REG bit mask */ +#define NGBE_PHY_10BASET_HALF BIT(5) +#define NGBE_PHY_10BASET_FULL BIT(6) +#define NGBE_PHY_100BASETX_HALF BIT(7) +#define NGBE_PHY_100BASETX_FULL BIT(8) + +#define NGBE_PHY_1000BASEX_FULL_MV BIT(5) + +/* NGBE_PHY_GBCR_REG bit mask*/ +#define NGBE_PHY_1000BASET_HALF BIT(8) +#define NGBE_PHY_1000BASET_FULL BIT(9) + +/* M88E1512 */ +#define NGBE_PHY_ID_MV 0x005043 +#define NGBE_PHY_PAGE_ACCESS_MV 0x16 +#define NGBE_PHY_INT_STATUS_REG_MV 0x13 +#define NGBE_PHY_LED_FUNC_CTRL_REG_MV 0x10 +#define NGBE_PHY_LED_POL_CTRL_REG_MV 0x11 + +/* reg 19_0 INT status*/ +#define NGBE_PHY_INT_STATUS_ANC_MV BIT(11) +#define NGBE_PHY_INT_STATUS_LSC_MV BIT(10) +/* reg 21_2 */ +#define NGBE_RGM_TTC_MV BIT(4) +#define NGBE_RGM_RTC_MV BIT(5) +/* reg 18_3 */ +#define NGBE_INT_EN_MV BIT(7) +#define NGBE_INT_POL_MV BIT(11) + +/* LED conf */ +#define NGBE_LED1_CONF_MV 0x6 +#define NGBE_LED0_CONF_MV 0x1 +/* LED polarity */ +#define NGBE_LED1_POL_MV 0x1 +#define NGBE_LED0_POL_MV 0x1 + +/* YT8521s/YT8531s */ +#define NGBE_PHY_ID_YT8521S 0x011a +#define NGBE_PHY_ID_YT8531S 0xe91a +#define NGBE_PHY_INT_STATUS_REG_YT 0x13 +#define NGBE_PHY_INT_STATUS_SDSLNKUP_YT BIT(2) +#define NGBE_PHY_INT_STATUS_SDSLNKDN_YT BIT(3) +#define NGBE_PHY_INT_STATUS_UTPLNKUP_YT BIT(10) +#define NGBE_PHY_INT_STATUS_UTPLNKDN_YT BIT(11) + +/* INTERNAL PHY*/ +#define NGBE_CFG_LED_CTL 0x14424 +#define NGBE_PHY_ID_INTERNAL 0x000732 +#define NGBE_PHY_CONFIG(reg_offset) (0x14000 + ((reg_offset) * 4)) +#define NGBE_PHY_PAGE_ACCESS_INTERNAL 0x1F +#define NGBE_PHY_INT_STATUS_REG_INTERNAL 0x1d +#define NGBE_PHY_PHYSR_REG_INTERNAL 0x1a +#define NGBE_PHY_INT_STATUS_LSC_INTERNAL BIT(4) +#define NGBE_PHY_INT_STATUS_ANC_INTERNAL BIT(3) + enum ngbe_phy_type { ngbe_phy_unknown = 0, ngbe_phy_none, ngbe_phy_internal, ngbe_phy_m88e1512, ngbe_phy_m88e1512_sfi, - ngbe_phy_m88e1512_unknown, - ngbe_phy_yt8521s, - ngbe_phy_yt8521s_sfi, - ngbe_phy_internal_yt8521s_sfi, + ngbe_phy_m88e1512_mix, + ngbe_phy_yt, + ngbe_phy_yt_mix, + ngbe_phy_internal_yt_sfi, ngbe_phy_generic }; @@ -122,9 +216,16 @@ struct ngbe_phy_info { u32 addr; u32 id; + u8 phy_mode; + u32 gphy_efuse[2]; - bool reset_if_overtemp; + bool autoneg; + u32 autoneg_advertised; +}; +enum ngbe_pf_flags { + NGBE_FLAG_NEED_LINK_UPDATE, + NGBE_FLAGS_NBITS /* must be last */ }; struct ngbe_hw { @@ -135,5 +236,7 @@ struct ngbe_hw { bool wol_enabled; bool ncsi_enabled; bool gpio_ctrl; + + u32 led_conf; }; #endif /* _NGBE_TYPE_H_ */ From patchwork Tue Nov 8 11:19:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mengyuan Lou X-Patchwork-Id: 13036161 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0BD62C4332F for ; Tue, 8 Nov 2022 11:20:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234096AbiKHLUI (ORCPT ); Tue, 8 Nov 2022 06:20:08 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59460 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234070AbiKHLUE (ORCPT ); Tue, 8 Nov 2022 06:20:04 -0500 Received: from smtpbgeu2.qq.com (smtpbgeu2.qq.com [18.194.254.142]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F29CE29CA1 for ; Tue, 8 Nov 2022 03:19:51 -0800 (PST) X-QQ-mid: bizesmtp85t1667906384t3xpw7ku Received: from localhost.localdomain ( [183.129.236.74]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 08 Nov 2022 19:19:43 +0800 (CST) X-QQ-SSF: 01400000000000M0M000000A0000000 X-QQ-FEAT: ao4JQgu0M3+OPY+iUeBYxgJ2gHPRH04rtB3AUoskEblB/z18dqlySOJ5hAy0n knhO9oNglxfWfL4KNKCJKM4i7PkqA4EY0kGQel46XSwDQ6+3MsRUl0jfjMhI/QcfzutbJrQ cwyzlara9jEVyQLw3WgtyCfK3V1DGZimO3fZIIQwzEgLZNXvd9e2DiCVlFmVkrPUbmaaN0i iUv88A8So6H7a85Xu7h6bQ3l/chOHMlysWegFbIw8UDViQpMxhTt6y2vw6DsyeSsKHPgoF2 PI7vmr59sCVfVtBD3LdTI+Psa3xoxXOO819pocjk9kCgfNdKF61DeslywyopBMyl/maB3EK Z9pFJWljmldIRFY+PlYFQLyIcEvLi+bWW7h2k/5ZFKoa0rLoViMZrRdD6u3p3c+hHpx6+Nk SDs67fcdNPz7EHcy1mXS5w== X-QQ-GoodBg: 2 From: Mengyuan Lou To: netdev@vger.kernel.org Cc: jiawenwu@trustnetic.com, Mengyuan Lou Subject: [PATCH net-next 5/5] net: ngbe: Initialize service task Date: Tue, 8 Nov 2022 19:19:07 +0800 Message-Id: <20221108111907.48599-6-mengyuanlou@net-swift.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221108111907.48599-1-mengyuanlou@net-swift.com> References: <20221108111907.48599-1-mengyuanlou@net-swift.com> MIME-Version: 1.0 X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:net-swift.com:qybglogicsvr:qybglogicsvr1 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Initialize ngbe_wq, building the framework for task checking. Signed-off-by: Mengyuan Lou --- drivers/net/ethernet/wangxun/ngbe/ngbe.h | 21 + drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c | 5 + drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 380 +++++++++++++++++- drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 1 + 4 files changed, 401 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe.h b/drivers/net/ethernet/wangxun/ngbe/ngbe.h index ac67c0403592..17b241264c2d 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe.h @@ -36,6 +36,17 @@ struct ngbe_mac_addr { u64 pools; }; +enum ngbe_adapter_state { + NGBE_TESTING, + NGBE_DOWN, + NGBE_REMOVING, + NGBE_RESETTING, + NGBE_NEEDS_RESTART, + NGBE_SERVICE_SCHED, + NGBE_SERVICE_DIS, + NGBE_STATE_NBITS /* must be last */ +}; + /* board specific private data structure */ struct ngbe_adapter { u8 __iomem *io_addr; /* Mainly for iounmap use */ @@ -48,7 +59,17 @@ struct ngbe_adapter { struct ngbe_mac_addr *mac_table; u16 msg_enable; + DECLARE_BITMAP(state, NGBE_STATE_NBITS); DECLARE_BITMAP(flags, NGBE_FLAGS_NBITS); + unsigned long serv_tmr_period; + unsigned long serv_tmr_prev; + struct timer_list serv_tmr; + struct work_struct serv_task; + + unsigned long link_check_timeout; + + u32 link_speed; + bool link_up; /* Tx fast path data */ int num_tx_queues; diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c index 274d54832579..915b27b25c1c 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c @@ -8,6 +8,7 @@ #include "../libwx/wx_type.h" #include "../libwx/wx_hw.h" #include "ngbe_type.h" +#include "ngbe_phy.h" #include "ngbe_hw.h" #include "ngbe.h" @@ -106,6 +107,10 @@ int ngbe_reset_hw(struct ngbe_hw *hw) wr32(wxhw, WX_MIS_RST, reset | rd32(wxhw, WX_MIS_RST)); ngbe_reset_misc(hw); + status = ngbe_phy_init(hw); + if (status) + return status; + /* Store the permanent mac address */ wx_get_mac_addr(wxhw, wxhw->mac.perm_addr); diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index ebf3fcdc4719..83cc61eb3be2 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -9,14 +9,18 @@ #include #include #include +#include #include "../libwx/wx_type.h" #include "../libwx/wx_hw.h" #include "ngbe_type.h" +#include "ngbe_phy.h" #include "ngbe_hw.h" #include "ngbe.h" char ngbe_driver_name[] = "ngbe"; +static struct workqueue_struct *ngbe_wq; + /* ngbe_pci_tbl - PCI Device ID Table * * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, @@ -39,6 +43,81 @@ static const struct pci_device_id ngbe_pci_tbl[] = { { .device = 0 } }; +/** + * ngbe_service_event_schedule - schedule the service task to wake up + * @adapter: board private structure + * + * If not already scheduled, this puts the task into the work queue. + */ +static void ngbe_service_event_schedule(struct ngbe_adapter *adapter) +{ + if (!test_bit(NGBE_SERVICE_DIS, adapter->state) && + !test_and_set_bit(NGBE_SERVICE_SCHED, adapter->state) && + !test_bit(NGBE_NEEDS_RESTART, adapter->state)) + queue_work(ngbe_wq, &adapter->serv_task); +} + +static void ngbe_service_event_complete(struct ngbe_adapter *adapter) +{ + WARN_ON(!test_bit(NGBE_SERVICE_SCHED, adapter->state)); + + /* flush memory to make sure state is correct before next watchdog */ + smp_mb__before_atomic(); + clear_bit(NGBE_SERVICE_SCHED, adapter->state); +} + +/** + * ngbe_service_event_stop - stop service task and cancel works + * @adapter: board private structure + */ +static int ngbe_service_event_stop(struct ngbe_adapter *adapter) +{ + int ret; + + ret = test_and_set_bit(NGBE_SERVICE_DIS, adapter->state); + if (adapter->serv_tmr.function) + del_timer_sync(&adapter->serv_tmr); + if (adapter->serv_task.func) + cancel_work_sync(&adapter->serv_task); + clear_bit(NGBE_SERVICE_SCHED, adapter->state); + + return ret; +} + +/** + * ngbe_service_timer - timer callback to schedule service task + * @t: pointer to timer_list + */ +static void ngbe_service_timer(struct timer_list *t) +{ + struct ngbe_adapter *adapter = + from_timer(adapter, t, serv_tmr); + + mod_timer(&adapter->serv_tmr, round_jiffies(adapter->serv_tmr_period + jiffies)); + ngbe_service_event_schedule(adapter); +} + +static void ngbe_sync_mac_table(struct ngbe_adapter *adapter) +{ + struct ngbe_hw *hw = &adapter->hw; + struct wx_hw *wxhw = &hw->wxhw; + int i; + + for (i = 0; i < wxhw->mac.num_rar_entries; i++) { + if (adapter->mac_table[i].state & NGBE_MAC_STATE_MODIFIED) { + if (adapter->mac_table[i].state & NGBE_MAC_STATE_IN_USE) { + wx_set_rar(wxhw, i, + adapter->mac_table[i].addr, + adapter->mac_table[i].pools, + WX_PSR_MAC_SWC_AD_H_AV); + } else { + wx_clear_rar(wxhw, i); + } + adapter->mac_table[i].state &= ~(NGBE_MAC_STATE_MODIFIED); + } + } +} + static void ngbe_mac_set_default_filter(struct ngbe_adapter *adapter, u8 *addr) { struct ngbe_hw *hw = &adapter->hw; @@ -52,6 +131,161 @@ static void ngbe_mac_set_default_filter(struct ngbe_adapter *adapter, u8 *addr) WX_PSR_MAC_SWC_AD_H_AV); } +static void ngbe_flush_sw_mac_table(struct ngbe_adapter *adapter) +{ + struct ngbe_hw *hw = &adapter->hw; + struct wx_hw *wxhw = &hw->wxhw; + u32 i; + + for (i = 0; i < wxhw->mac.num_rar_entries; i++) { + adapter->mac_table[i].state |= NGBE_MAC_STATE_MODIFIED; + adapter->mac_table[i].state &= ~NGBE_MAC_STATE_IN_USE; + memset(adapter->mac_table[i].addr, 0, ETH_ALEN); + adapter->mac_table[i].pools = 0; + } + ngbe_sync_mac_table(adapter); +} + +/** + * ngbe_watchdog_update_link_status - update the link status + * @adapter: pointer to the device adapter structure + **/ +static void ngbe_watchdog_update_link_status(struct ngbe_adapter *adapter) +{ + u32 link_speed = adapter->link_speed; + struct ngbe_hw *hw = &adapter->hw; + bool link_up = adapter->link_up; + struct wx_hw *wxhw = &hw->wxhw; + u32 lan_speed = 0; + u32 reg; + + if (!test_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags)) + return; + + link_speed = SPEED_1000; + link_up = true; + ngbe_phy_check_link(hw, &link_speed, &link_up, false); + + if (link_up || time_after(jiffies, (adapter->link_check_timeout + + 4 * HZ))) + set_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags); + + adapter->link_speed = link_speed; + switch (link_speed) { + case SPEED_1000: + lan_speed = 2; + break; + case SPEED_100: + lan_speed = 1; + break; + case SPEED_10: + lan_speed = 0; + break; + default: + break; + } + wr32m(wxhw, NGBE_CFG_LAN_SPEED, 0x3, lan_speed); + + adapter->link_up = link_up; + adapter->link_speed = link_speed; + if (link_up) { + if (link_speed & (SPEED_1000 | SPEED_100 | SPEED_10)) { + reg = rd32(wxhw, WX_MAC_TX_CFG); + reg &= ~WX_MAC_TX_CFG_SPEED_MASK; + reg |= WX_MAC_TX_CFG_SPEED_1G | WX_MAC_TX_CFG_TE; + wr32(wxhw, WX_MAC_TX_CFG, reg); + } + /* Re configure MAC RX */ + reg = rd32(wxhw, WX_MAC_RX_CFG); + wr32(wxhw, WX_MAC_RX_CFG, reg); + wr32(wxhw, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR); + reg = rd32(wxhw, WX_MAC_WDG_TIMEOUT); + wr32(wxhw, WX_MAC_WDG_TIMEOUT, reg); + } +} + +static void ngbe_watchdog_link_is_up(struct ngbe_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + const char *speed_str; + + /* only continue if link was previously down */ + if (netif_carrier_ok(netdev)) + return; + + switch (adapter->link_speed) { + case SPEED_1000: + speed_str = "1 Gbps"; + break; + case SPEED_100: + speed_str = "100 Mbps"; + break; + case SPEED_10: + speed_str = "10 Mbps"; + break; + default: + speed_str = "unknown speed"; + break; + } + + netif_info(adapter, drv, netdev, + "NIC Link is Up %s\n", speed_str); + + netif_carrier_on(netdev); +} + +static void ngbe_watchdog_link_is_down(struct ngbe_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + adapter->link_up = false; + adapter->link_speed = 0; + + /* only continue if link was up previously */ + if (!netif_carrier_ok(netdev)) + return; + + netif_info(adapter, drv, netdev, "NIC Link is Down\n"); + netif_carrier_off(netdev); +} + +/** + * ngbe_watchdog_subtask - check and bring link up + * @adapter: pointer to the device adapter structure + **/ +static void ngbe_watchdog_subtask(struct ngbe_adapter *adapter) +{ + /* if interface is down do nothing */ + if (test_bit(NGBE_DOWN, adapter->state) || + test_bit(NGBE_REMOVING, adapter->state) || + test_bit(NGBE_RESETTING, adapter->state)) + return; + + ngbe_watchdog_update_link_status(adapter); + + if (adapter->link_up) + ngbe_watchdog_link_is_up(adapter); + else + ngbe_watchdog_link_is_down(adapter); +} + +/** + * ngbe_service_task - manages and runs subtasks + * @work: pointer to work_struct containing our data + **/ +static void ngbe_service_task(struct work_struct *work) +{ + struct ngbe_adapter *adapter = container_of(work, + struct ngbe_adapter, + serv_task); + + if (test_bit(NGBE_DOWN, adapter->state)) + return; + + ngbe_watchdog_subtask(adapter); + ngbe_service_event_complete(adapter); +} + /** * ngbe_init_type_code - Initialize the shared code * @hw: pointer to hardware structure @@ -221,12 +455,96 @@ static void ngbe_oem_conf_in_rom(struct ngbe_hw *hw) &hw->phy.gphy_efuse[1]); } +static void ngbe_reset(struct ngbe_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct ngbe_hw *hw = &adapter->hw; + u8 old_addr[ETH_ALEN]; + int err; + + err = ngbe_reset_hw(hw); + if (err) + dev_err(&adapter->pdev->dev, "Hardware Error: %d\n", err); + /* do not flush user set addresses */ + memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len); + ngbe_flush_sw_mac_table(adapter); + ngbe_mac_set_default_filter(adapter, old_addr); +} + +static void ngbe_disable_device(struct ngbe_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct ngbe_hw *hw = &adapter->hw; + + /* signal that we are down to the interrupt handler */ + if (test_and_set_bit(NGBE_DOWN, adapter->state)) + return; /* do nothing if already down */ + + wx_disable_pcie_master(&hw->wxhw); + /* disable receives */ + wx_disable_rx(&hw->wxhw); + + /* call carrier off first to avoid false dev_watchdog timeouts */ + netif_carrier_off(netdev); + netif_tx_disable(netdev); + + clear_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags); + + del_timer_sync(&adapter->serv_tmr); +} + static void ngbe_down(struct ngbe_adapter *adapter) { - netif_carrier_off(adapter->netdev); - netif_tx_disable(adapter->netdev); + ngbe_disable_device(adapter); + + ngbe_reset(adapter); }; +/** + * ngbe_phy_setlink_config - set up non-SFP+ link + * @hw: pointer to private hardware struct + * + * Returns 0 on success, negative on failure + **/ +static int ngbe_phy_setlink_config(struct ngbe_hw *hw) +{ + u32 speed = 0; + int ret = 0; + + hw->phy.autoneg = 1; + speed = SPEED_1000 | SPEED_100 | SPEED_10; + + if (hw->wol_enabled || hw->ncsi_enabled) + return 0; + + ret = ngbe_phy_setup_link(hw, speed, false); + + return ret; +} + +static void ngbe_up_complete(struct ngbe_adapter *adapter) +{ + struct ngbe_hw *hw = &adapter->hw; + int err; + + wx_control_hw(&hw->wxhw, true); + + /* make sure to complete pre-operations */ + smp_mb__before_atomic(); + clear_bit(NGBE_DOWN, adapter->state); + + err = ngbe_phy_setlink_config(hw); + if (err) + dev_err(&adapter->pdev->dev, "Phy hardware Error: %d\n", err); + + /* bring the link up in the watchdog, this could race with our first + * link up interrupt but shouldn't be a problem + */ + set_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags); + adapter->link_check_timeout = jiffies; + mod_timer(&adapter->serv_tmr, round_jiffies(jiffies + adapter->serv_tmr_period)); +} + /** * ngbe_open - Called when a network interface is made active * @netdev: network interface device structure @@ -239,10 +557,8 @@ static void ngbe_down(struct ngbe_adapter *adapter) static int ngbe_open(struct net_device *netdev) { struct ngbe_adapter *adapter = netdev_priv(netdev); - struct ngbe_hw *hw = &adapter->hw; - struct wx_hw *wxhw = &hw->wxhw; - wx_control_hw(wxhw, true); + ngbe_up_complete(adapter); return 0; } @@ -491,6 +807,15 @@ static int ngbe_probe(struct pci_dev *pdev, eth_hw_addr_set(netdev, wxhw->mac.perm_addr); ngbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr); + /* setup service timer and periodic service task */ + timer_setup(&adapter->serv_tmr, ngbe_service_timer, 0); + adapter->serv_tmr_period = HZ; + INIT_WORK(&adapter->serv_task, ngbe_service_task); + clear_bit(NGBE_SERVICE_SCHED, adapter->state); + clear_bit(NGBE_SERVICE_DIS, adapter->state); + /* since everything is good, start the service timer */ + mod_timer(&adapter->serv_tmr, round_jiffies(jiffies + adapter->serv_tmr_period)); + err = register_netdev(netdev); if (err) goto err_register; @@ -505,6 +830,7 @@ static int ngbe_probe(struct pci_dev *pdev, return 0; err_register: + ngbe_service_event_stop(adapter); wx_control_hw(wxhw, false); err_free_mac_table: kfree(adapter->mac_table); @@ -531,6 +857,10 @@ static void ngbe_remove(struct pci_dev *pdev) struct ngbe_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev; + set_bit(NGBE_REMOVING, adapter->state); + cancel_work_sync(&adapter->serv_task); + ngbe_service_event_stop(adapter); + netdev = adapter->netdev; unregister_netdev(netdev); pci_release_selected_regions(pdev, @@ -550,7 +880,45 @@ static struct pci_driver ngbe_driver = { .shutdown = ngbe_shutdown, }; -module_pci_driver(ngbe_driver); +/** + * ngbe_module_init - Driver registration routine + * + * ngbe_module_init is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + */ +static int __init ngbe_module_init(void) +{ + int status; + + ngbe_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME); + if (!ngbe_wq) { + pr_err("Failed to create workqueue\n"); + return -ENOMEM; + } + + status = pci_register_driver(&ngbe_driver); + if (status) { + pr_err("failed to register PCI driver, err %d\n", status); + destroy_workqueue(ngbe_wq); + } + + return status; +} +module_init(ngbe_module_init); + +/** + * ngbe_module_exit - Driver exit cleanup routine + * + * ngbe_module_exit is called just before the driver is removed + * from memory. + */ +static void __exit ngbe_module_exit(void) +{ + pci_unregister_driver(&ngbe_driver); + destroy_workqueue(ngbe_wq); + pr_info("module unloaded\n"); +} +module_exit(ngbe_module_exit); MODULE_DEVICE_TABLE(pci, ngbe_pci_tbl); MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, "); diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h index f6b257e84319..672716e47042 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -83,6 +83,7 @@ enum NGBE_MSCA_CMD_value { #define NGBE_MSCC_BUSY BIT(22) #define NGBE_MDIO_CLK(v) ((0x7 & (v)) << 19) +#define NGBE_CFG_LAN_SPEED 0x14440 /* GPIO Registers */ #define NGBE_GPIO_DR 0x14800 #define NGBE_GPIO_DDR 0x14804