mirror of https://gitee.com/openkylin/linux.git
ixgbe: Add logic to reset CS4227 when needed
On some hardware platforms, the CS4227 does not initialize properly. Detect those cases and reset it appropriately. Signed-off-by: Mark Rustad <mark.d.rustad@intel.com> Tested-by: Phil Schmitt <phillip.j.schmitt@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
parent
e23f333678
commit
542b6eecf4
|
@ -81,14 +81,29 @@
|
|||
#define IXGBE_I2C_EEPROM_STATUS_FAIL 0x2
|
||||
#define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS 0x3
|
||||
#define IXGBE_CS4227 0xBE /* CS4227 address */
|
||||
#define IXGBE_CS4227_SCRATCH 2
|
||||
#define IXGBE_CS4227_RESET_PENDING 0x1357
|
||||
#define IXGBE_CS4227_RESET_COMPLETE 0x5AA5
|
||||
#define IXGBE_CS4227_RETRIES 15
|
||||
#define IXGBE_CS4227_EFUSE_STATUS 0x0181
|
||||
#define IXGBE_CS4227_LINE_SPARE22_MSB 0x12AD /* Reg to set speed */
|
||||
#define IXGBE_CS4227_LINE_SPARE24_LSB 0x12B0 /* Reg to set EDC */
|
||||
#define IXGBE_CS4227_HOST_SPARE22_MSB 0x1AAD /* Reg to set speed */
|
||||
#define IXGBE_CS4227_HOST_SPARE24_LSB 0x1AB0 /* Reg to program EDC */
|
||||
#define IXGBE_CS4227_EEPROM_STATUS 0x5001
|
||||
#define IXGBE_CS4227_EEPROM_LOAD_OK 0x0001
|
||||
#define IXGBE_CS4227_SPEED_1G 0x8000
|
||||
#define IXGBE_CS4227_SPEED_10G 0
|
||||
#define IXGBE_CS4227_EDC_MODE_CX1 0x0002
|
||||
#define IXGBE_CS4227_EDC_MODE_SR 0x0004
|
||||
#define IXGBE_CS4227_EDC_MODE_DIAG 0x0008
|
||||
#define IXGBE_CS4227_RESET_HOLD 500 /* microseconds */
|
||||
#define IXGBE_CS4227_RESET_DELAY 500 /* milliseconds */
|
||||
#define IXGBE_CS4227_CHECK_DELAY 30 /* milliseconds */
|
||||
#define IXGBE_PE 0xE0 /* Port expander addr */
|
||||
#define IXGBE_PE_OUTPUT 1 /* Output reg offset */
|
||||
#define IXGBE_PE_CONFIG 3 /* Config reg offset */
|
||||
#define IXGBE_PE_BIT1 (1 << 1)
|
||||
|
||||
/* Flow control defines */
|
||||
#define IXGBE_TAF_SYM_PAUSE 0x400
|
||||
|
|
|
@ -56,6 +56,283 @@ static void ixgbe_setup_mux_ctl(struct ixgbe_hw *hw)
|
|||
IXGBE_WRITE_FLUSH(hw);
|
||||
}
|
||||
|
||||
/**
|
||||
* ixgbe_read_cs4227 - Read CS4227 register
|
||||
* @hw: pointer to hardware structure
|
||||
* @reg: register number to write
|
||||
* @value: pointer to receive value read
|
||||
*
|
||||
* Returns status code
|
||||
*/
|
||||
static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value)
|
||||
{
|
||||
return hw->phy.ops.read_i2c_combined_unlocked(hw, IXGBE_CS4227, reg,
|
||||
value);
|
||||
}
|
||||
|
||||
/**
|
||||
* ixgbe_write_cs4227 - Write CS4227 register
|
||||
* @hw: pointer to hardware structure
|
||||
* @reg: register number to write
|
||||
* @value: value to write to register
|
||||
*
|
||||
* Returns status code
|
||||
*/
|
||||
static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value)
|
||||
{
|
||||
return hw->phy.ops.write_i2c_combined_unlocked(hw, IXGBE_CS4227, reg,
|
||||
value);
|
||||
}
|
||||
|
||||
/**
|
||||
* ixgbe_check_cs4227_reg - Perform diag on a CS4227 register
|
||||
* @hw: pointer to hardware structure
|
||||
* @reg: the register to check
|
||||
*
|
||||
* Performs a diagnostic on a register in the CS4227 chip. Returns an error
|
||||
* if it is not operating correctly.
|
||||
* This function assumes that the caller has acquired the proper semaphore.
|
||||
*/
|
||||
static s32 ixgbe_check_cs4227_reg(struct ixgbe_hw *hw, u16 reg)
|
||||
{
|
||||
s32 status;
|
||||
u32 retry;
|
||||
u16 reg_val;
|
||||
|
||||
reg_val = (IXGBE_CS4227_EDC_MODE_DIAG << 1) | 1;
|
||||
status = ixgbe_write_cs4227(hw, reg, reg_val);
|
||||
if (status)
|
||||
return status;
|
||||
for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
|
||||
msleep(IXGBE_CS4227_CHECK_DELAY);
|
||||
reg_val = 0xFFFF;
|
||||
ixgbe_read_cs4227(hw, reg, ®_val);
|
||||
if (!reg_val)
|
||||
break;
|
||||
}
|
||||
if (reg_val) {
|
||||
hw_err(hw, "CS4227 reg 0x%04X failed diagnostic\n", reg);
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ixgbe_get_cs4227_status - Return CS4227 status
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
* Performs a diagnostic on the CS4227 chip. Returns an error if it is
|
||||
* not operating correctly.
|
||||
* This function assumes that the caller has acquired the proper semaphore.
|
||||
*/
|
||||
static s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw)
|
||||
{
|
||||
s32 status;
|
||||
u16 value = 0;
|
||||
|
||||
/* Exit if the diagnostic has already been performed. */
|
||||
status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
|
||||
if (status)
|
||||
return status;
|
||||
if (value == IXGBE_CS4227_RESET_COMPLETE)
|
||||
return 0;
|
||||
|
||||
/* Check port 0. */
|
||||
status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Check port 1. */
|
||||
status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB +
|
||||
(1 << 12));
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB +
|
||||
(1 << 12));
|
||||
}
|
||||
|
||||
/**
|
||||
* ixgbe_read_pe - Read register from port expander
|
||||
* @hw: pointer to hardware structure
|
||||
* @reg: register number to read
|
||||
* @value: pointer to receive read value
|
||||
*
|
||||
* Returns status code
|
||||
*/
|
||||
static s32 ixgbe_read_pe(struct ixgbe_hw *hw, u8 reg, u8 *value)
|
||||
{
|
||||
s32 status;
|
||||
|
||||
status = ixgbe_read_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, value);
|
||||
if (status)
|
||||
hw_err(hw, "port expander access failed with %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ixgbe_write_pe - Write register to port expander
|
||||
* @hw: pointer to hardware structure
|
||||
* @reg: register number to write
|
||||
* @value: value to write
|
||||
*
|
||||
* Returns status code
|
||||
*/
|
||||
static s32 ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value)
|
||||
{
|
||||
s32 status;
|
||||
|
||||
status = ixgbe_write_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE,
|
||||
value);
|
||||
if (status)
|
||||
hw_err(hw, "port expander access failed with %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ixgbe_reset_cs4227 - Reset CS4227 using port expander
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
* Returns error code
|
||||
*/
|
||||
static s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw)
|
||||
{
|
||||
s32 status;
|
||||
u32 retry;
|
||||
u16 value;
|
||||
u8 reg;
|
||||
|
||||
/* Trigger hard reset. */
|
||||
status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®);
|
||||
if (status)
|
||||
return status;
|
||||
reg |= IXGBE_PE_BIT1;
|
||||
status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = ixgbe_read_pe(hw, IXGBE_PE_CONFIG, ®);
|
||||
if (status)
|
||||
return status;
|
||||
reg &= ~IXGBE_PE_BIT1;
|
||||
status = ixgbe_write_pe(hw, IXGBE_PE_CONFIG, reg);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®);
|
||||
if (status)
|
||||
return status;
|
||||
reg &= ~IXGBE_PE_BIT1;
|
||||
status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
usleep_range(IXGBE_CS4227_RESET_HOLD, IXGBE_CS4227_RESET_HOLD + 100);
|
||||
|
||||
status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®);
|
||||
if (status)
|
||||
return status;
|
||||
reg |= IXGBE_PE_BIT1;
|
||||
status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Wait for the reset to complete. */
|
||||
msleep(IXGBE_CS4227_RESET_DELAY);
|
||||
for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
|
||||
status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EFUSE_STATUS,
|
||||
&value);
|
||||
if (!status && value == IXGBE_CS4227_EEPROM_LOAD_OK)
|
||||
break;
|
||||
msleep(IXGBE_CS4227_CHECK_DELAY);
|
||||
}
|
||||
if (retry == IXGBE_CS4227_RETRIES) {
|
||||
hw_err(hw, "CS4227 reset did not complete\n");
|
||||
return IXGBE_ERR_PHY;
|
||||
}
|
||||
|
||||
status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EEPROM_STATUS, &value);
|
||||
if (status || !(value & IXGBE_CS4227_EEPROM_LOAD_OK)) {
|
||||
hw_err(hw, "CS4227 EEPROM did not load successfully\n");
|
||||
return IXGBE_ERR_PHY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ixgbe_check_cs4227 - Check CS4227 and reset as needed
|
||||
* @hw: pointer to hardware structure
|
||||
*/
|
||||
static void ixgbe_check_cs4227(struct ixgbe_hw *hw)
|
||||
{
|
||||
u32 swfw_mask = hw->phy.phy_semaphore_mask;
|
||||
s32 status;
|
||||
u16 value;
|
||||
u8 retry;
|
||||
|
||||
for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
|
||||
status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
|
||||
if (status) {
|
||||
hw_err(hw, "semaphore failed with %d\n", status);
|
||||
msleep(IXGBE_CS4227_CHECK_DELAY);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get status of reset flow. */
|
||||
status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
|
||||
if (!status && value == IXGBE_CS4227_RESET_COMPLETE)
|
||||
goto out;
|
||||
|
||||
if (status || value != IXGBE_CS4227_RESET_PENDING)
|
||||
break;
|
||||
|
||||
/* Reset is pending. Wait and check again. */
|
||||
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
|
||||
msleep(IXGBE_CS4227_CHECK_DELAY);
|
||||
}
|
||||
|
||||
/* Reset the CS4227. */
|
||||
status = ixgbe_reset_cs4227(hw);
|
||||
if (status) {
|
||||
hw_err(hw, "CS4227 reset failed: %d", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Reset takes so long, temporarily release semaphore in case the
|
||||
* other driver instance is waiting for the reset indication.
|
||||
*/
|
||||
ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
|
||||
IXGBE_CS4227_RESET_PENDING);
|
||||
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
|
||||
usleep_range(10000, 12000);
|
||||
status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
|
||||
if (status) {
|
||||
hw_err(hw, "semaphore failed with %d", status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Is the CS4227 working correctly? */
|
||||
status = ixgbe_get_cs4227_status(hw);
|
||||
if (status) {
|
||||
hw_err(hw, "CS4227 status failed: %d", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Record completion for next time. */
|
||||
status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
|
||||
IXGBE_CS4227_RESET_COMPLETE);
|
||||
|
||||
out:
|
||||
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
|
||||
msleep(hw->eeprom.semaphore_delay);
|
||||
}
|
||||
|
||||
/** ixgbe_identify_phy_x550em - Get PHY type based on device id
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
|
@ -68,7 +345,7 @@ static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw)
|
|||
/* set up for CS4227 usage */
|
||||
hw->phy.phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM;
|
||||
ixgbe_setup_mux_ctl(hw);
|
||||
|
||||
ixgbe_check_cs4227(hw);
|
||||
return ixgbe_identify_module_generic(hw);
|
||||
case IXGBE_DEV_ID_X550EM_X_KX4:
|
||||
hw->phy.type = ixgbe_phy_x550em_kx4;
|
||||
|
|
Loading…
Reference in New Issue