]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
Merge remote-tracking branch 'input-current/for-linus'
[karo-tx-linux.git] / drivers / net / ethernet / intel / ixgbe / ixgbe_x550.c
index 2a1dca6e8d44f5bda3deb512672288d1b14a87bd..ebe0ac950b14e72a0d5dae2e51d3250c250846d1 100644 (file)
@@ -56,6 +56,292 @@ 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, &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
+ *
+ * This function assumes that the caller has acquired the proper semaphore.
+ * 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, &reg);
+       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, &reg);
+       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, &reg);
+       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, &reg);
+       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);
+       }
+       /* If still pending, assume other instance failed. */
+       if (retry == IXGBE_CS4227_RETRIES) {
+               status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
+               if (status) {
+                       hw_err(hw, "semaphore failed with %d\n", status);
+                       return;
+               }
+       }
+
+       /* 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 +354,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;
@@ -909,6 +1195,96 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed)
        return status;
 }
 
+/**
+ *  ixgbe_supported_sfp_modules_X550em - Check if SFP module type is supported
+ *  @hw: pointer to hardware structure
+ *  @linear: true if SFP module is linear
+ */
+static s32 ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear)
+{
+       switch (hw->phy.sfp_type) {
+       case ixgbe_sfp_type_not_present:
+               return IXGBE_ERR_SFP_NOT_PRESENT;
+       case ixgbe_sfp_type_da_cu_core0:
+       case ixgbe_sfp_type_da_cu_core1:
+               *linear = true;
+               break;
+       case ixgbe_sfp_type_srlr_core0:
+       case ixgbe_sfp_type_srlr_core1:
+       case ixgbe_sfp_type_da_act_lmt_core0:
+       case ixgbe_sfp_type_da_act_lmt_core1:
+       case ixgbe_sfp_type_1g_sx_core0:
+       case ixgbe_sfp_type_1g_sx_core1:
+       case ixgbe_sfp_type_1g_lx_core0:
+       case ixgbe_sfp_type_1g_lx_core1:
+               *linear = false;
+               break;
+       case ixgbe_sfp_type_unknown:
+       case ixgbe_sfp_type_1g_cu_core0:
+       case ixgbe_sfp_type_1g_cu_core1:
+       default:
+               return IXGBE_ERR_SFP_NOT_SUPPORTED;
+       }
+
+       return 0;
+}
+
+/**
+ *  ixgbe_setup_mac_link_sfp_x550em - Configure the KR PHY for SFP.
+ *  @hw: pointer to hardware structure
+ *
+ *  Configures the extern PHY and the integrated KR PHY for SFP support.
+ */
+static s32
+ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw,
+                               ixgbe_link_speed speed,
+                               __always_unused bool autoneg_wait_to_complete)
+{
+       s32 status;
+       u16 slice, value;
+       bool setup_linear = false;
+
+       /* Check if SFP module is supported and linear */
+       status = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear);
+
+       /* If no SFP module present, then return success. Return success since
+        * there is no reason to configure CS4227 and SFP not present error is
+        * not accepted in the setup MAC link flow.
+        */
+       if (status == IXGBE_ERR_SFP_NOT_PRESENT)
+               return 0;
+
+       if (status)
+               return status;
+
+       /* Configure CS4227 LINE side to 10G SR. */
+       slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12);
+       value = IXGBE_CS4227_SPEED_10G;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       /* Configure CS4227 for HOST connection rate then type. */
+       slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12);
+       value = speed & IXGBE_LINK_SPEED_10GB_FULL ?
+               IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12);
+       if (setup_linear)
+               value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
+       else
+               value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       /* If internal link mode is XFI, then setup XFI internal link. */
+       if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE))
+               status = ixgbe_setup_ixfi_x550em(hw, &speed);
+
+       return status;
+}
+
 /**
  * ixgbe_setup_mac_link_t_X550em - Sets the auto advertised link speed
  * @hw: pointer to hardware structure
@@ -1003,6 +1379,10 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
                mac->ops.disable_tx_laser = NULL;
                mac->ops.enable_tx_laser = NULL;
                mac->ops.flap_tx_laser = NULL;
+               mac->ops.setup_link = ixgbe_setup_mac_link_multispeed_fiber;
+               mac->ops.setup_mac_link = ixgbe_setup_mac_link_sfp_x550em;
+               mac->ops.set_rate_select_speed =
+                                       ixgbe_set_soft_rate_select_speed;
                break;
        case ixgbe_media_type_copper:
                mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em;
@@ -1018,53 +1398,18 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
  */
 static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw)
 {
-       bool setup_linear;
-       u16 reg_slice, edc_mode;
-       s32 ret_val;
+       s32 status;
+       bool linear;
 
-       switch (hw->phy.sfp_type) {
-       case ixgbe_sfp_type_unknown:
-               return 0;
-       case ixgbe_sfp_type_not_present:
-               return IXGBE_ERR_SFP_NOT_PRESENT;
-       case ixgbe_sfp_type_da_cu_core0:
-       case ixgbe_sfp_type_da_cu_core1:
-               setup_linear = true;
-               break;
-       case ixgbe_sfp_type_srlr_core0:
-       case ixgbe_sfp_type_srlr_core1:
-       case ixgbe_sfp_type_da_act_lmt_core0:
-       case ixgbe_sfp_type_da_act_lmt_core1:
-       case ixgbe_sfp_type_1g_sx_core0:
-       case ixgbe_sfp_type_1g_sx_core1:
-               setup_linear = false;
-               break;
-       default:
-               return IXGBE_ERR_SFP_NOT_SUPPORTED;
-       }
+       /* Check if SFP module is supported */
+       status = ixgbe_supported_sfp_modules_X550em(hw, &linear);
+       if (status)
+               return status;
 
        ixgbe_init_mac_link_ops_X550em(hw);
        hw->phy.ops.reset = NULL;
 
-       /* The CS4227 slice address is the base address + the port-pair reg
-        * offset. I.e. Slice 0 = 0x12B0 and slice 1 = 0x22B0.
-        */
-       reg_slice = IXGBE_CS4227_SPARE24_LSB + (hw->bus.lan_id << 12);
-
-       if (setup_linear)
-               edc_mode = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 0x1;
-       else
-               edc_mode = (IXGBE_CS4227_EDC_MODE_SR << 1) | 0x1;
-
-       /* Configure CS4227 for connection type. */
-       ret_val = hw->phy.ops.write_i2c_combined(hw, IXGBE_CS4227, reg_slice,
-                                                edc_mode);
-
-       if (ret_val)
-               ret_val = hw->phy.ops.write_i2c_combined(hw, 0x80, reg_slice,
-                                                        edc_mode);
-
-       return ret_val;
+       return 0;
 }
 
 /** ixgbe_get_link_capabilities_x550em - Determines link capabilities
@@ -1272,7 +1617,7 @@ static s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw)
        if (status)
                return status;
 
-       if (lsc)
+       if (lsc && phy->ops.setup_internal_link)
                return phy->ops.setup_internal_link(hw);
 
        return 0;
@@ -1927,6 +2272,62 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
        IXGBE_WRITE_REG(hw, IXGBE_PFFLPH, (u32)(pfflp >> 32));
 }
 
+/**
+ * ixgbe_set_mux - Set mux for port 1 access with CS4227
+ * @hw: pointer to hardware structure
+ * @state: set mux if 1, clear if 0
+ */
+static void ixgbe_set_mux(struct ixgbe_hw *hw, u8 state)
+{
+       u32 esdp;
+
+       if (!hw->bus.lan_id)
+               return;
+       esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
+       if (state)
+               esdp |= IXGBE_ESDP_SDP1;
+       else
+               esdp &= ~IXGBE_ESDP_SDP1;
+       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
+       IXGBE_WRITE_FLUSH(hw);
+}
+
+/**
+ * ixgbe_acquire_swfw_sync_X550em - Acquire SWFW semaphore
+ * @hw: pointer to hardware structure
+ * @mask: Mask to specify which semaphore to acquire
+ *
+ * Acquires the SWFW semaphore and sets the I2C MUX
+ */
+static s32 ixgbe_acquire_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask)
+{
+       s32 status;
+
+       status = ixgbe_acquire_swfw_sync_X540(hw, mask);
+       if (status)
+               return status;
+
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               ixgbe_set_mux(hw, 1);
+
+       return 0;
+}
+
+/**
+ * ixgbe_release_swfw_sync_X550em - Release SWFW semaphore
+ * @hw: pointer to hardware structure
+ * @mask: Mask to specify which semaphore to release
+ *
+ * Releases the SWFW semaphore and sets the I2C MUX
+ */
+static void ixgbe_release_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask)
+{
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               ixgbe_set_mux(hw, 0);
+
+       ixgbe_release_swfw_sync_X540(hw, mask);
+}
+
 #define X550_COMMON_MAC \
        .init_hw                        = &ixgbe_init_hw_generic, \
        .start_hw                       = &ixgbe_start_hw_X540, \
@@ -1964,8 +2365,6 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
                                &ixgbe_set_source_address_pruning_X550, \
        .set_ethertype_anti_spoofing    = \
                                &ixgbe_set_ethertype_anti_spoofing_X550, \
-       .acquire_swfw_sync              = &ixgbe_acquire_swfw_sync_X540, \
-       .release_swfw_sync              = &ixgbe_release_swfw_sync_X540, \
        .disable_rx_buff                = &ixgbe_disable_rx_buff_generic, \
        .enable_rx_buff                 = &ixgbe_enable_rx_buff_generic, \
        .get_thermal_sensor_data        = NULL, \
@@ -1985,6 +2384,8 @@ static struct ixgbe_mac_operations mac_ops_X550 = {
        .get_link_capabilities  = &ixgbe_get_copper_link_capabilities_generic,
        .get_bus_info           = &ixgbe_get_bus_info_generic,
        .setup_sfp              = NULL,
+       .acquire_swfw_sync      = &ixgbe_acquire_swfw_sync_X540,
+       .release_swfw_sync      = &ixgbe_release_swfw_sync_X540,
 };
 
 static struct ixgbe_mac_operations mac_ops_X550EM_x = {
@@ -1997,7 +2398,8 @@ static struct ixgbe_mac_operations mac_ops_X550EM_x = {
        .get_link_capabilities  = &ixgbe_get_link_capabilities_X550em,
        .get_bus_info           = &ixgbe_get_bus_info_X550em,
        .setup_sfp              = ixgbe_setup_sfp_modules_X550em,
-
+       .acquire_swfw_sync      = &ixgbe_acquire_swfw_sync_X550em,
+       .release_swfw_sync      = &ixgbe_release_swfw_sync_X550em,
 };
 
 #define X550_COMMON_EEP \