i40e: support autoneg or force link speed
authorCunming Liang <cunming.liang@intel.com>
Thu, 31 Jul 2014 20:44:07 +0000 (04:44 +0800)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Mon, 25 Aug 2014 14:07:50 +0000 (16:07 +0200)
- i40e force link up/down
- i40e autoneg/force speed

Signed-off-by: Cunming Liang <cunming.liang@intel.com>
Acked-by: Helin Zhang <helin.zhang@intel.com>
Acked-by: Chen Jing D(Mark) <jing.d.chen@intel.com>
Tested-by: Xu HuilongX <huilongx.xu@intel.com>
app/test-pmd/cmdline.c
lib/librte_pmd_i40e/i40e_ethdev.c

index 345be11..0abc233 100644 (file)
@@ -527,7 +527,8 @@ static void cmd_help_long_parsed(void *parsed_result,
                        "port close (port_id|all)\n"
                        "    Close all ports or port_id.\n\n"
 
-                       "port config (port_id|all) speed (10|100|1000|10000|auto)"
+                       "port config (port_id|all)"
+                       " speed (10|100|1000|10000|40000|auto)"
                        " duplex (half|full|auto)\n"
                        "    Set speed and duplex for all ports or port_id\n\n"
 
@@ -801,7 +802,9 @@ cmd_config_speed_all_parsed(void *parsed_result,
        else if (!strcmp(res->value1, "1000"))
                link_speed = ETH_LINK_SPEED_1000;
        else if (!strcmp(res->value1, "10000"))
-               link_speed = ETH_LINK_SPEED_10000;
+               link_speed = ETH_LINK_SPEED_10G;
+       else if (!strcmp(res->value1, "40000"))
+               link_speed = ETH_LINK_SPEED_40G;
        else if (!strcmp(res->value1, "auto"))
                link_speed = ETH_LINK_SPEED_AUTONEG;
        else {
@@ -839,7 +842,7 @@ cmdline_parse_token_string_t cmd_config_speed_all_item1 =
        TOKEN_STRING_INITIALIZER(struct cmd_config_speed_all, item1, "speed");
 cmdline_parse_token_string_t cmd_config_speed_all_value1 =
        TOKEN_STRING_INITIALIZER(struct cmd_config_speed_all, value1,
-                                               "10#100#1000#10000#auto");
+                                               "10#100#1000#10000#40000#auto");
 cmdline_parse_token_string_t cmd_config_speed_all_item2 =
        TOKEN_STRING_INITIALIZER(struct cmd_config_speed_all, item2, "duplex");
 cmdline_parse_token_string_t cmd_config_speed_all_value2 =
@@ -849,7 +852,7 @@ cmdline_parse_token_string_t cmd_config_speed_all_value2 =
 cmdline_parse_inst_t cmd_config_speed_all = {
        .f = cmd_config_speed_all_parsed,
        .data = NULL,
-       .help_str = "port config all speed 10|100|1000|10000|auto duplex "
+       .help_str = "port config all speed 10|100|1000|10000|40000|auto duplex "
                                                        "half|full|auto",
        .tokens = {
                (void *)&cmd_config_speed_all_port,
@@ -901,6 +904,8 @@ cmd_config_speed_specific_parsed(void *parsed_result,
                link_speed = ETH_LINK_SPEED_1000;
        else if (!strcmp(res->value1, "10000"))
                link_speed = ETH_LINK_SPEED_10000;
+       else if (!strcmp(res->value1, "40000"))
+               link_speed = ETH_LINK_SPEED_40G;
        else if (!strcmp(res->value1, "auto"))
                link_speed = ETH_LINK_SPEED_AUTONEG;
        else {
@@ -939,7 +944,7 @@ cmdline_parse_token_string_t cmd_config_speed_specific_item1 =
                                                                "speed");
 cmdline_parse_token_string_t cmd_config_speed_specific_value1 =
        TOKEN_STRING_INITIALIZER(struct cmd_config_speed_specific, value1,
-                                               "10#100#1000#10000#auto");
+                                               "10#100#1000#10000#40000#auto");
 cmdline_parse_token_string_t cmd_config_speed_specific_item2 =
        TOKEN_STRING_INITIALIZER(struct cmd_config_speed_specific, item2,
                                                                "duplex");
@@ -950,7 +955,7 @@ cmdline_parse_token_string_t cmd_config_speed_specific_value2 =
 cmdline_parse_inst_t cmd_config_speed_specific = {
        .f = cmd_config_speed_specific_parsed,
        .data = NULL,
-       .help_str = "port config X speed 10|100|1000|10000|auto duplex "
+       .help_str = "port config X speed 10|100|1000|10000|40000|auto duplex "
                                                        "half|full|auto",
        .tokens = {
                (void *)&cmd_config_speed_specific_port,
index 7a823a0..0a918a6 100644 (file)
@@ -128,6 +128,8 @@ static void i40e_dev_promiscuous_enable(struct rte_eth_dev *dev);
 static void i40e_dev_promiscuous_disable(struct rte_eth_dev *dev);
 static void i40e_dev_allmulticast_enable(struct rte_eth_dev *dev);
 static void i40e_dev_allmulticast_disable(struct rte_eth_dev *dev);
+static int i40e_dev_set_link_up(struct rte_eth_dev *dev);
+static int i40e_dev_set_link_down(struct rte_eth_dev *dev);
 static void i40e_dev_stats_get(struct rte_eth_dev *dev,
                               struct rte_eth_stats *stats);
 static void i40e_dev_stats_reset(struct rte_eth_dev *dev);
@@ -222,6 +224,8 @@ static struct eth_dev_ops i40e_eth_dev_ops = {
        .promiscuous_disable          = i40e_dev_promiscuous_disable,
        .allmulticast_enable          = i40e_dev_allmulticast_enable,
        .allmulticast_disable         = i40e_dev_allmulticast_disable,
+       .dev_set_link_up              = i40e_dev_set_link_up,
+       .dev_set_link_down            = i40e_dev_set_link_down,
        .link_update                  = i40e_dev_link_update,
        .stats_get                    = i40e_dev_stats_get,
        .stats_reset                  = i40e_dev_stats_reset,
@@ -649,6 +653,102 @@ i40e_vsi_disable_queues_intr(struct i40e_vsi *vsi)
        I40E_WRITE_REG(hw, I40E_PFINT_DYN_CTLN(vsi->msix_intr - 1), 0);
 }
 
+static inline uint8_t
+i40e_parse_link_speed(uint16_t eth_link_speed)
+{
+       uint8_t link_speed = I40E_LINK_SPEED_UNKNOWN;
+
+       switch (eth_link_speed) {
+       case ETH_LINK_SPEED_40G:
+               link_speed = I40E_LINK_SPEED_40GB;
+               break;
+       case ETH_LINK_SPEED_20G:
+               link_speed = I40E_LINK_SPEED_20GB;
+               break;
+       case ETH_LINK_SPEED_10G:
+               link_speed = I40E_LINK_SPEED_10GB;
+               break;
+       case ETH_LINK_SPEED_1000:
+               link_speed = I40E_LINK_SPEED_1GB;
+               break;
+       case ETH_LINK_SPEED_100:
+               link_speed = I40E_LINK_SPEED_100MB;
+               break;
+       }
+
+       return link_speed;
+}
+
+static int
+i40e_phy_conf_link(struct i40e_hw *hw, uint8_t abilities, uint8_t force_speed)
+{
+       enum i40e_status_code status;
+       struct i40e_aq_get_phy_abilities_resp phy_ab;
+       struct i40e_aq_set_phy_config phy_conf;
+       const uint8_t mask = I40E_AQ_PHY_FLAG_PAUSE_TX |
+                       I40E_AQ_PHY_FLAG_PAUSE_RX |
+                       I40E_AQ_PHY_FLAG_LOW_POWER;
+       const uint8_t advt = I40E_LINK_SPEED_40GB |
+                       I40E_LINK_SPEED_10GB |
+                       I40E_LINK_SPEED_1GB |
+                       I40E_LINK_SPEED_100MB;
+       int ret = -ENOTSUP;
+
+       status = i40e_aq_get_phy_capabilities(hw, false, false, &phy_ab,
+                                             NULL);
+       if (status)
+               return ret;
+
+       memset(&phy_conf, 0, sizeof(phy_conf));
+
+       /* bits 0-2 use the values from get_phy_abilities_resp */
+       abilities &= ~mask;
+       abilities |= phy_ab.abilities & mask;
+
+       /* update ablities and speed */
+       if (abilities & I40E_AQ_PHY_AN_ENABLED)
+               phy_conf.link_speed = advt;
+       else
+               phy_conf.link_speed = force_speed;
+
+       phy_conf.abilities = abilities;
+
+       /* use get_phy_abilities_resp value for the rest */
+       phy_conf.phy_type = phy_ab.phy_type;
+       phy_conf.eee_capability = phy_ab.eee_capability;
+       phy_conf.eeer = phy_ab.eeer_val;
+       phy_conf.low_power_ctrl = phy_ab.d3_lpan;
+
+       PMD_DRV_LOG(DEBUG, "\n\tCurrent: abilities %x, link_speed %x\n"
+                   "\tConfig:  abilities %x, link_speed %x",
+                   phy_ab.abilities, phy_ab.link_speed,
+                   phy_conf.abilities, phy_conf.link_speed);
+
+       status = i40e_aq_set_phy_config(hw, &phy_conf, NULL);
+       if (status)
+               return ret;
+
+       return I40E_SUCCESS;
+}
+
+static int
+i40e_apply_link_speed(struct rte_eth_dev *dev)
+{
+       uint8_t speed;
+       uint8_t abilities = 0;
+       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       struct rte_eth_conf *conf = &dev->data->dev_conf;
+
+       speed = i40e_parse_link_speed(conf->link_speed);
+       abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
+       if (conf->link_speed == ETH_LINK_SPEED_AUTONEG)
+               abilities |= I40E_AQ_PHY_AN_ENABLED;
+       else
+               abilities |= I40E_AQ_PHY_LINK_ENABLED;
+
+       return i40e_phy_conf_link(hw, abilities, speed);
+}
+
 static int
 i40e_dev_start(struct rte_eth_dev *dev)
 {
@@ -657,6 +757,14 @@ i40e_dev_start(struct rte_eth_dev *dev)
        struct i40e_vsi *vsi = pf->main_vsi;
        int ret;
 
+       if ((dev->data->dev_conf.link_duplex != ETH_LINK_AUTONEG_DUPLEX) &&
+               (dev->data->dev_conf.link_duplex != ETH_LINK_FULL_DUPLEX)) {
+               PMD_INIT_LOG(ERR, "Invalid link_duplex (%hu) for port %hhu\n",
+                               dev->data->dev_conf.link_duplex,
+                               dev->data->port_id);
+               return -EINVAL;
+       }
+
        /* Initialize VSI */
        ret = i40e_vsi_init(vsi);
        if (ret != I40E_SUCCESS) {
@@ -682,6 +790,13 @@ i40e_dev_start(struct rte_eth_dev *dev)
                        PMD_DRV_LOG(INFO, "fail to set vsi broadcast\n");
        }
 
+       /* Apply link configure */
+       ret = i40e_apply_link_speed(dev);
+       if (I40E_SUCCESS != ret) {
+               PMD_DRV_LOG(ERR, "Fail to apply link setting\n");
+               goto err_up;
+       }
+
        return I40E_SUCCESS;
 
 err_up:
@@ -703,6 +818,9 @@ i40e_dev_stop(struct rte_eth_dev *dev)
        /* Clear all queues and release memory */
        i40e_dev_clear_queues(dev);
 
+       /* Set link down */
+       i40e_dev_set_link_down(dev);
+
        /* un-map queues with interrupt registers */
        i40e_vsi_disable_queues_intr(vsi);
        i40e_vsi_queues_unbind_intr(vsi);
@@ -798,6 +916,29 @@ i40e_dev_allmulticast_disable(struct rte_eth_dev *dev)
                PMD_DRV_LOG(ERR, "Failed to disable multicast promiscuous\n");
 }
 
+/*
+ * Set device link up.
+ */
+static int
+i40e_dev_set_link_up(struct rte_eth_dev *dev)
+{
+       /* re-apply link speed setting */
+       return i40e_apply_link_speed(dev);
+}
+
+/*
+ * Set device link down.
+ */
+static int
+i40e_dev_set_link_down(__rte_unused struct rte_eth_dev *dev)
+{
+       uint8_t speed = I40E_LINK_SPEED_UNKNOWN;
+       uint8_t abilities = I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
+       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+       return i40e_phy_conf_link(hw, abilities, speed);
+}
+
 int
 i40e_dev_link_update(struct rte_eth_dev *dev,
                     __rte_unused int wait_to_complete)