net/ixgbe/base: support FW commands to control some PHYs
authorWei Dai <wei.dai@intel.com>
Wed, 21 Dec 2016 09:47:59 +0000 (17:47 +0800)
committerFerruh Yigit <ferruh.yigit@intel.com>
Tue, 17 Jan 2017 18:40:51 +0000 (19:40 +0100)
Implement support for new firmware commands to be used to access
and control some PHYs.

Signed-off-by: Wei Dai <wei.dai@intel.com>
drivers/net/ixgbe/base/ixgbe_common.h
drivers/net/ixgbe/base/ixgbe_osdep.h
drivers/net/ixgbe/base/ixgbe_type.h
drivers/net/ixgbe/base/ixgbe_x550.c

index 93e80ea..903f34d 100644 (file)
@@ -161,7 +161,9 @@ u8 ixgbe_calculate_checksum(u8 *buffer, u32 length);
 s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, u32 *buffer,
                                 u32 length, u32 timeout, bool return_data);
 s32 ixgbe_hic_unlocked(struct ixgbe_hw *, u32 *buffer, u32 length, u32 timeout);
-
+s32 ixgbe_shutdown_fw_phy(struct ixgbe_hw *);
+s32 ixgbe_fw_phy_activity(struct ixgbe_hw *, u16 activity,
+                         u32 (*data)[FW_PHY_ACT_DATA_COUNT]);
 void ixgbe_clear_tx_pending(struct ixgbe_hw *hw);
 
 extern s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw);
index 77f0af5..b0977b6 100644 (file)
@@ -95,8 +95,9 @@ enum {
 #define STATIC static
 #define IXGBE_NTOHL(_i)        rte_be_to_cpu_32(_i)
 #define IXGBE_NTOHS(_i)        rte_be_to_cpu_16(_i)
+#define IXGBE_CPU_TO_LE16(_i)  rte_cpu_to_le_16(_i)
 #define IXGBE_CPU_TO_LE32(_i)  rte_cpu_to_le_32(_i)
-#define IXGBE_LE32_TO_CPU(_i) rte_le_to_cpu_32(_i)
+#define IXGBE_LE32_TO_CPU(_i)  rte_le_to_cpu_32(_i)
 #define IXGBE_LE32_TO_CPUS(_i) rte_le_to_cpu_32(_i)
 #define IXGBE_CPU_TO_BE16(_i)  rte_cpu_to_be_16(_i)
 #define IXGBE_CPU_TO_BE32(_i)  rte_cpu_to_be_32(_i)
index adc5fb3..c7100b0 100644 (file)
@@ -3063,6 +3063,59 @@ enum ixgbe_fdir_pballoc_type {
 #define FW_INT_PHY_REQ_LEN             10
 #define FW_INT_PHY_REQ_READ            0
 #define FW_INT_PHY_REQ_WRITE           1
+#define FW_PHY_ACT_REQ_CMD             5
+#define FW_PHY_ACT_DATA_COUNT          4
+#define FW_PHY_ACT_REQ_LEN             (4 + 4 * FW_PHY_ACT_DATA_COUNT)
+#define FW_PHY_ACT_INIT_PHY            1
+#define FW_PHY_ACT_SETUP_LINK          2
+#define FW_PHY_ACT_LINK_SPEED_10       (1u << 0)
+#define FW_PHY_ACT_LINK_SPEED_100      (1u << 1)
+#define FW_PHY_ACT_LINK_SPEED_1G       (1u << 2)
+#define FW_PHY_ACT_LINK_SPEED_2_5G     (1u << 3)
+#define FW_PHY_ACT_LINK_SPEED_5G       (1u << 4)
+#define FW_PHY_ACT_LINK_SPEED_10G      (1u << 5)
+#define FW_PHY_ACT_LINK_SPEED_20G      (1u << 6)
+#define FW_PHY_ACT_LINK_SPEED_25G      (1u << 7)
+#define FW_PHY_ACT_LINK_SPEED_40G      (1u << 8)
+#define FW_PHY_ACT_LINK_SPEED_50G      (1u << 9)
+#define FW_PHY_ACT_LINK_SPEED_100G     (1u << 10)
+#define FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT 16
+#define FW_PHY_ACT_SETUP_LINK_PAUSE_MASK (3u << \
+                                         FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT)
+#define FW_PHY_ACT_SETUP_LINK_PAUSE_NONE 0u
+#define FW_PHY_ACT_SETUP_LINK_PAUSE_TX 1u
+#define FW_PHY_ACT_SETUP_LINK_PAUSE_RX 2u
+#define FW_PHY_ACT_SETUP_LINK_PAUSE_RXTX 3u
+#define FW_PHY_ACT_SETUP_LINK_LP       (1u << 18)
+#define FW_PHY_ACT_SETUP_LINK_HP       (1u << 19)
+#define FW_PHY_ACT_SETUP_LINK_EEE      (1u << 20)
+#define FW_PHY_ACT_SETUP_LINK_AN       (1u << 22)
+#define FW_PHY_ACT_SETUP_LINK_RSP_DOWN (1u << 0)
+#define FW_PHY_ACT_GET_LINK_INFO       3
+#define FW_PHY_ACT_GET_LINK_INFO_EEE   (1u << 19)
+#define FW_PHY_ACT_GET_LINK_INFO_FC_TX (1u << 20)
+#define FW_PHY_ACT_GET_LINK_INFO_FC_RX (1u << 21)
+#define FW_PHY_ACT_GET_LINK_INFO_POWER (1u << 22)
+#define FW_PHY_ACT_GET_LINK_INFO_AN_COMPLETE   (1u << 24)
+#define FW_PHY_ACT_GET_LINK_INFO_TEMP  (1u << 25)
+#define FW_PHY_ACT_GET_LINK_INFO_LP_FC_TX      (1u << 28)
+#define FW_PHY_ACT_GET_LINK_INFO_LP_FC_RX      (1u << 29)
+#define FW_PHY_ACT_FORCE_LINK_DOWN     4
+#define FW_PHY_ACT_FORCE_LINK_DOWN_OFF (1u << 0)
+#define FW_PHY_ACT_PHY_SW_RESET                5
+#define FW_PHY_ACT_PHY_HW_RESET                6
+#define FW_PHY_ACT_GET_PHY_INFO                7
+#define FW_PHY_ACT_UD_2                        0x1002
+#define FW_PHY_ACT_UD_2_10G_KR_EEE     (1u << 6)
+#define FW_PHY_ACT_UD_2_10G_KX4_EEE    (1u << 5)
+#define FW_PHY_ACT_UD_2_1G_KX_EEE      (1u << 4)
+#define FW_PHY_ACT_UD_2_10G_T_EEE      (1u << 3)
+#define FW_PHY_ACT_UD_2_1G_T_EEE       (1u << 2)
+#define FW_PHY_ACT_UD_2_100M_TX_EEE    (1u << 1)
+#define FW_PHY_ACT_RETRIES             50
+#define FW_PHY_INFO_SPEED_MASK         0xFFFu
+#define FW_PHY_INFO_ID_HI_MASK         0xFFFF0000u
+#define FW_PHY_INFO_ID_LO_MASK         0x0000FFFFu
 
 /* Host Interface Command Structures */
 
@@ -3170,6 +3223,19 @@ struct ixgbe_hic_internal_phy_resp {
        __be32 read_data;
 };
 
+struct ixgbe_hic_phy_activity_req {
+       struct ixgbe_hic_hdr hdr;
+       u8 port_number;
+       u8 pad;
+       __le16 activity_id;
+       __be32 data[FW_PHY_ACT_DATA_COUNT];
+};
+
+struct ixgbe_hic_phy_activity_resp {
+       struct ixgbe_hic_hdr hdr;
+       __be32 data[FW_PHY_ACT_DATA_COUNT];
+};
+
 #ifdef C99
 #pragma pack(pop)
 #else
@@ -4046,8 +4112,8 @@ struct ixgbe_phy_info {
        bool reset_disable;
        ixgbe_autoneg_advertised autoneg_advertised;
        ixgbe_link_speed speeds_supported;
-       enum ixgbe_ms_type ms_type;
-       enum ixgbe_ms_type original_ms_type;
+       ixgbe_link_speed eee_speeds_supported;
+       ixgbe_link_speed eee_speeds_advertised;
        enum ixgbe_smart_speed smart_speed;
        bool smart_speed_active;
        bool multispeed_fiber;
index 97fbf88..0a041b7 100644 (file)
@@ -467,6 +467,133 @@ STATIC s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw)
        return IXGBE_SUCCESS;
 }
 
+/**
+ * ixgbe_fw_phy_activity - Perform an activity on a PHY
+ * @hw: pointer to hardware structure
+ * @activity: activity to perform
+ * @data: Pointer to 4 32-bit words of data
+ */
+s32 ixgbe_fw_phy_activity(struct ixgbe_hw *hw, u16 activity,
+                         u32 (*data)[FW_PHY_ACT_DATA_COUNT])
+{
+       union {
+               struct ixgbe_hic_phy_activity_req cmd;
+               struct ixgbe_hic_phy_activity_resp rsp;
+       } hic;
+       u16 retries = FW_PHY_ACT_RETRIES;
+       s32 rc;
+       u16 i;
+
+       do {
+               memset(&hic, 0, sizeof(hic));
+               hic.cmd.hdr.cmd = FW_PHY_ACT_REQ_CMD;
+               hic.cmd.hdr.buf_len = FW_PHY_ACT_REQ_LEN;
+               hic.cmd.hdr.checksum = FW_DEFAULT_CHECKSUM;
+               hic.cmd.port_number = hw->bus.lan_id;
+               hic.cmd.activity_id = IXGBE_CPU_TO_LE16(activity);
+               for (i = 0; i < FW_PHY_ACT_DATA_COUNT; ++i)
+                       hic.cmd.data[i] = IXGBE_CPU_TO_BE32((*data)[i]);
+
+               rc = ixgbe_host_interface_command(hw, (u32 *)&hic.cmd,
+                                                 sizeof(hic.cmd),
+                                                 IXGBE_HI_COMMAND_TIMEOUT,
+                                                 true);
+               if (rc != IXGBE_SUCCESS)
+                       return rc;
+               if (hic.rsp.hdr.cmd_or_resp.ret_status ==
+                   FW_CEM_RESP_STATUS_SUCCESS) {
+                       for (i = 0; i < FW_PHY_ACT_DATA_COUNT; ++i)
+                               (*data)[i] = IXGBE_BE32_TO_CPU(hic.rsp.data[i]);
+                       return IXGBE_SUCCESS;
+               }
+               usec_delay(20);
+               --retries;
+       } while (retries > 0);
+
+       return IXGBE_ERR_HOST_INTERFACE_COMMAND;
+}
+
+static const struct {
+       u16 fw_speed;
+       ixgbe_link_speed phy_speed;
+} ixgbe_fw_map[] = {
+       { FW_PHY_ACT_LINK_SPEED_10, IXGBE_LINK_SPEED_10_FULL },
+       { FW_PHY_ACT_LINK_SPEED_100, IXGBE_LINK_SPEED_100_FULL },
+       { FW_PHY_ACT_LINK_SPEED_1G, IXGBE_LINK_SPEED_1GB_FULL },
+       { FW_PHY_ACT_LINK_SPEED_2_5G, IXGBE_LINK_SPEED_2_5GB_FULL },
+       { FW_PHY_ACT_LINK_SPEED_5G, IXGBE_LINK_SPEED_5GB_FULL },
+       { FW_PHY_ACT_LINK_SPEED_10G, IXGBE_LINK_SPEED_10GB_FULL },
+};
+
+/**
+ * ixgbe_get_phy_id_fw - Get the phy ID via firmware command
+ * @hw: pointer to hardware structure
+ *
+ * Returns error code
+ */
+static s32 ixgbe_get_phy_id_fw(struct ixgbe_hw *hw)
+{
+       u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 };
+       u16 phy_speeds;
+       u16 phy_id_lo;
+       s32 rc;
+       u16 i;
+
+       rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_GET_PHY_INFO, &info);
+       if (rc)
+               return rc;
+
+       hw->phy.speeds_supported = 0;
+       phy_speeds = info[0] & FW_PHY_INFO_SPEED_MASK;
+       for (i = 0; i < sizeof(ixgbe_fw_map) / sizeof(ixgbe_fw_map[0]); ++i) {
+               if (phy_speeds & ixgbe_fw_map[i].fw_speed)
+                       hw->phy.speeds_supported |= ixgbe_fw_map[i].phy_speed;
+       }
+       if (!hw->phy.autoneg_advertised)
+               hw->phy.autoneg_advertised = hw->phy.speeds_supported;
+
+       hw->phy.id = info[0] & FW_PHY_INFO_ID_HI_MASK;
+       phy_id_lo = info[1] & FW_PHY_INFO_ID_LO_MASK;
+       hw->phy.id |= phy_id_lo & IXGBE_PHY_REVISION_MASK;
+       hw->phy.revision = phy_id_lo & ~IXGBE_PHY_REVISION_MASK;
+       if (!hw->phy.id || hw->phy.id == IXGBE_PHY_REVISION_MASK)
+               return IXGBE_ERR_PHY_ADDR_INVALID;
+       return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_identify_phy_fw - Get PHY type based on firmware command
+ * @hw: pointer to hardware structure
+ *
+ * Returns error code
+ */
+static s32 ixgbe_identify_phy_fw(struct ixgbe_hw *hw)
+{
+       if (hw->bus.lan_id)
+               hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY1_SM;
+       else
+               hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY0_SM;
+
+       hw->phy.type = ixgbe_phy_m88;
+       hw->phy.ops.read_reg = NULL;
+       hw->phy.ops.write_reg = NULL;
+       return ixgbe_get_phy_id_fw(hw);
+}
+
+/**
+ * ixgbe_shutdown_fw_phy - Shutdown a firmware-controlled PHY
+ * @hw: pointer to hardware structure
+ *
+ * Returns error code
+ */
+s32 ixgbe_shutdown_fw_phy(struct ixgbe_hw *hw)
+{
+       u32 setup[FW_PHY_ACT_DATA_COUNT] = { 0 };
+
+       setup[0] = FW_PHY_ACT_FORCE_LINK_DOWN_OFF;
+       return ixgbe_fw_phy_activity(hw, FW_PHY_ACT_FORCE_LINK_DOWN, &setup);
+}
+
 STATIC s32 ixgbe_read_phy_reg_x550em(struct ixgbe_hw *hw, u32 reg_addr,
                                     u32 device_type, u16 *phy_data)
 {
@@ -605,7 +732,18 @@ s32 ixgbe_init_ops_X550EM(struct ixgbe_hw *hw)
 
        /* PHY */
        phy->ops.init = ixgbe_init_phy_ops_X550em;
-       phy->ops.identify = ixgbe_identify_phy_x550em;
+       switch (hw->device_id) {
+       case IXGBE_DEV_ID_X550EM_A_1G_T:
+       case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+               mac->ops.setup_fc = NULL;
+               phy->ops.identify = ixgbe_identify_phy_fw;
+               phy->ops.set_phy_power = NULL;
+               phy->ops.get_firmware_version = NULL;
+               break;
+       default:
+               phy->ops.identify = ixgbe_identify_phy_x550em;
+       }
+
        if (mac->ops.get_media_type(hw) != ixgbe_media_type_copper)
                phy->ops.set_phy_power = NULL;
 
@@ -623,6 +761,92 @@ s32 ixgbe_init_ops_X550EM(struct ixgbe_hw *hw)
        return ret_val;
 }
 
+/**
+ * ixgbe_setup_fw_link - Setup firmware-controlled PHYs
+ * @hw: pointer to hardware structure
+ */
+static s32 ixgbe_setup_fw_link(struct ixgbe_hw *hw)
+{
+       u32 setup[FW_PHY_ACT_DATA_COUNT] = { 0 };
+       s32 rc;
+       u16 i;
+
+       if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw))
+               return 0;
+
+       if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) {
+               ERROR_REPORT1(IXGBE_ERROR_UNSUPPORTED,
+                             "ixgbe_fc_rx_pause not valid in strict IEEE mode\n");
+               return IXGBE_ERR_INVALID_LINK_SETTINGS;
+       }
+
+       switch (hw->fc.requested_mode) {
+       case ixgbe_fc_full:
+               setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_RXTX <<
+                           FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
+               break;
+       case ixgbe_fc_rx_pause:
+               setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_RX <<
+                           FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
+               break;
+       case ixgbe_fc_tx_pause:
+               setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_TX <<
+                           FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
+               break;
+       default:
+               break;
+       }
+
+       for (i = 0; i < sizeof(ixgbe_fw_map) / sizeof(ixgbe_fw_map[0]); ++i) {
+               if (hw->phy.autoneg_advertised & ixgbe_fw_map[i].phy_speed)
+                       setup[0] |= ixgbe_fw_map[i].fw_speed;
+       }
+       setup[0] |= FW_PHY_ACT_SETUP_LINK_HP | FW_PHY_ACT_SETUP_LINK_AN;
+
+       if (hw->phy.eee_speeds_advertised)
+               setup[0] |= FW_PHY_ACT_SETUP_LINK_EEE;
+
+       rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_SETUP_LINK, &setup);
+       if (rc)
+               return rc;
+       if (setup[0] == FW_PHY_ACT_SETUP_LINK_RSP_DOWN)
+               return IXGBE_ERR_OVERTEMP;
+       return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_fc_autoneg_fw _ Set up flow control for FW-controlled PHYs
+ * @hw: pointer to hardware structure
+ *
+ *  Called at init time to set up flow control.
+ */
+static s32 ixgbe_fc_autoneg_fw(struct ixgbe_hw *hw)
+{
+       if (hw->fc.requested_mode == ixgbe_fc_default)
+               hw->fc.requested_mode = ixgbe_fc_full;
+
+       return ixgbe_setup_fw_link(hw);
+}
+
+/**
+ * ixgbe_setup_eee_fw - Enable/disable EEE support
+ * @hw: pointer to the HW structure
+ * @enable_eee: boolean flag to enable EEE
+ *
+ * Enable/disable EEE based on enable_eee flag.
+ * This function controls EEE for firmware-based PHY implementations.
+ */
+static s32 ixgbe_setup_eee_fw(struct ixgbe_hw *hw, bool enable_eee)
+{
+       if (!!hw->phy.eee_speeds_advertised == enable_eee)
+               return IXGBE_SUCCESS;
+       if (enable_eee)
+               hw->phy.eee_speeds_advertised = hw->phy.eee_speeds_supported;
+       else
+               hw->phy.eee_speeds_advertised = 0;
+       return hw->phy.ops.setup_link(hw);
+}
+
 /**
 *  ixgbe_init_ops_X550EM_a - Inits func ptrs and MAC type
 *  @hw: pointer to hardware structure
@@ -667,13 +891,13 @@ s32 ixgbe_init_ops_X550EM_a(struct ixgbe_hw *hw)
        if ((hw->device_id == IXGBE_DEV_ID_X550EM_A_1G_T) ||
                (hw->device_id == IXGBE_DEV_ID_X550EM_A_1G_T_L)) {
                mac->ops.fc_autoneg = ixgbe_fc_autoneg_sgmii_x550em_a;
-               mac->ops.setup_fc = ixgbe_setup_fc_sgmii_x550em_a;
+               mac->ops.setup_fc = ixgbe_fc_autoneg_fw;
        }
 
        switch (hw->device_id) {
        case IXGBE_DEV_ID_X550EM_A_KR:
        case IXGBE_DEV_ID_X550EM_A_KR_L:
-               mac->ops.setup_eee = ixgbe_setup_eee_X550;
+               mac->ops.setup_eee = ixgbe_setup_eee_fw;
                break;
        default:
                mac->ops.setup_eee = NULL;