OSDN Git Service

net: hns3: add support for FEC encoding control
authorJian Shen <shenjian15@huawei.com>
Fri, 3 May 2019 09:50:39 +0000 (17:50 +0800)
committerDavid S. Miller <davem@davemloft.net>
Sat, 4 May 2019 04:02:41 +0000 (00:02 -0400)
This patch adds support for FEC encoding control, user can change
FEC mode by command ethtool --set-fec, and get FEC mode by command
ethtool --show-fec. The fec capability is changed follow the port
speed. If autoneg on, the user configure fec mode will be overwritten
by autoneg result.

Signed-off-by: Jian Shen <shenjian15@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h

index 7ee40ec..ad21b0e 100644 (file)
@@ -132,6 +132,13 @@ enum hnae3_module_type {
 
 };
 
+enum hnae3_fec_mode {
+       HNAE3_FEC_AUTO = 0,
+       HNAE3_FEC_BASER,
+       HNAE3_FEC_RS,
+       HNAE3_FEC_USER_DEF,
+};
+
 enum hnae3_reset_notify_type {
        HNAE3_UP_CLIENT,
        HNAE3_DOWN_CLIENT,
@@ -360,6 +367,9 @@ struct hnae3_ae_ops {
        void (*get_media_type)(struct hnae3_handle *handle, u8 *media_type,
                               u8 *module_type);
        int (*check_port_speed)(struct hnae3_handle *handle, u32 speed);
+       void (*get_fec)(struct hnae3_handle *handle, u8 *fec_ability,
+                       u8 *fec_mode);
+       int (*set_fec)(struct hnae3_handle *handle, u32 fec_mode);
        void (*adjust_link)(struct hnae3_handle *handle, int speed, int duplex);
        int (*set_loopback)(struct hnae3_handle *handle,
                            enum hnae3_loop loop_mode, bool en);
index 23ded8a..1746943 100644 (file)
@@ -1211,6 +1211,81 @@ static void hns3_set_msglevel(struct net_device *netdev, u32 msg_level)
        h->msg_enable = msg_level;
 }
 
+/* Translate local fec value into ethtool value. */
+static unsigned int loc_to_eth_fec(u8 loc_fec)
+{
+       u32 eth_fec = 0;
+
+       if (loc_fec & BIT(HNAE3_FEC_AUTO))
+               eth_fec |= ETHTOOL_FEC_AUTO;
+       if (loc_fec & BIT(HNAE3_FEC_RS))
+               eth_fec |= ETHTOOL_FEC_RS;
+       if (loc_fec & BIT(HNAE3_FEC_BASER))
+               eth_fec |= ETHTOOL_FEC_BASER;
+
+       /* if nothing is set, then FEC is off */
+       if (!eth_fec)
+               eth_fec = ETHTOOL_FEC_OFF;
+
+       return eth_fec;
+}
+
+/* Translate ethtool fec value into local value. */
+static unsigned int eth_to_loc_fec(unsigned int eth_fec)
+{
+       u32 loc_fec = 0;
+
+       if (eth_fec & ETHTOOL_FEC_OFF)
+               return loc_fec;
+
+       if (eth_fec & ETHTOOL_FEC_AUTO)
+               loc_fec |= BIT(HNAE3_FEC_AUTO);
+       if (eth_fec & ETHTOOL_FEC_RS)
+               loc_fec |= BIT(HNAE3_FEC_RS);
+       if (eth_fec & ETHTOOL_FEC_BASER)
+               loc_fec |= BIT(HNAE3_FEC_BASER);
+
+       return loc_fec;
+}
+
+static int hns3_get_fecparam(struct net_device *netdev,
+                            struct ethtool_fecparam *fec)
+{
+       struct hnae3_handle *handle = hns3_get_handle(netdev);
+       const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+       u8 fec_ability;
+       u8 fec_mode;
+
+       if (handle->pdev->revision == 0x20)
+               return -EOPNOTSUPP;
+
+       if (!ops->get_fec)
+               return -EOPNOTSUPP;
+
+       ops->get_fec(handle, &fec_ability, &fec_mode);
+
+       fec->fec = loc_to_eth_fec(fec_ability);
+       fec->active_fec = loc_to_eth_fec(fec_mode);
+
+       return 0;
+}
+
+static int hns3_set_fecparam(struct net_device *netdev,
+                            struct ethtool_fecparam *fec)
+{
+       struct hnae3_handle *handle = hns3_get_handle(netdev);
+       const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+       u32 fec_mode;
+
+       if (handle->pdev->revision == 0x20)
+               return -EOPNOTSUPP;
+
+       if (!ops->set_fec)
+               return -EOPNOTSUPP;
+       fec_mode = eth_to_loc_fec(fec->fec);
+       return ops->set_fec(handle, fec_mode);
+}
+
 static const struct ethtool_ops hns3vf_ethtool_ops = {
        .get_drvinfo = hns3_get_drvinfo,
        .get_ringparam = hns3_get_ringparam,
@@ -1264,6 +1339,8 @@ static const struct ethtool_ops hns3_ethtool_ops = {
        .set_phys_id = hns3_set_phys_id,
        .get_msglevel = hns3_get_msglevel,
        .set_msglevel = hns3_set_msglevel,
+       .get_fecparam = hns3_get_fecparam,
+       .set_fecparam = hns3_set_fecparam,
 };
 
 void hns3_ethtool_set_ops(struct net_device *netdev)
index 653ef6a..d79a209 100644 (file)
@@ -113,6 +113,7 @@ enum hclge_opcode_type {
        HCLGE_OPC_MAC_TNL_INT_EN        = 0x0311,
        HCLGE_OPC_CLEAR_MAC_TNL_INT     = 0x0312,
        HCLGE_OPC_SERDES_LOOPBACK       = 0x0315,
+       HCLGE_OPC_CONFIG_FEC_MODE       = 0x031A,
 
        /* PFC/Pause commands */
        HCLGE_OPC_CFG_MAC_PAUSE_EN      = 0x0701,
@@ -610,6 +611,21 @@ struct hclge_sfp_info_cmd {
        u8 rsv[8];
 };
 
+#define HCLGE_MAC_CFG_FEC_AUTO_EN_B    0
+#define HCLGE_MAC_CFG_FEC_MODE_S       1
+#define HCLGE_MAC_CFG_FEC_MODE_M       GENMASK(3, 1)
+#define HCLGE_MAC_CFG_FEC_SET_DEF_B    0
+#define HCLGE_MAC_CFG_FEC_CLR_DEF_B    1
+
+#define HCLGE_MAC_FEC_OFF              0
+#define HCLGE_MAC_FEC_BASER            1
+#define HCLGE_MAC_FEC_RS               2
+struct hclge_config_fec_cmd {
+       u8 fec_mode;
+       u8 default_config;
+       u8 rsv[22];
+};
+
 #define HCLGE_MAC_UPLINK_PORT          0x100
 
 struct hclge_config_max_frm_size_cmd {
index 87615c9..d3b1f8c 100644 (file)
@@ -966,6 +966,37 @@ static void hclge_convert_setting_kr(struct hclge_mac *mac, u8 speed_ability)
                                 mac->supported);
 }
 
+static void hclge_convert_setting_fec(struct hclge_mac *mac)
+{
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, mac->supported);
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, mac->supported);
+
+       switch (mac->speed) {
+       case HCLGE_MAC_SPEED_10G:
+       case HCLGE_MAC_SPEED_40G:
+               linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT,
+                                mac->supported);
+               mac->fec_ability =
+                       BIT(HNAE3_FEC_BASER) | BIT(HNAE3_FEC_AUTO);
+               break;
+       case HCLGE_MAC_SPEED_25G:
+       case HCLGE_MAC_SPEED_50G:
+               linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT,
+                                mac->supported);
+               mac->fec_ability =
+                       BIT(HNAE3_FEC_BASER) | BIT(HNAE3_FEC_RS) |
+                       BIT(HNAE3_FEC_AUTO);
+               break;
+       case HCLGE_MAC_SPEED_100G:
+               linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, mac->supported);
+               mac->fec_ability = BIT(HNAE3_FEC_RS) | BIT(HNAE3_FEC_AUTO);
+               break;
+       default:
+               mac->fec_ability = 0;
+               break;
+       }
+}
+
 static void hclge_parse_fiber_link_mode(struct hclge_dev *hdev,
                                        u8 speed_ability)
 {
@@ -978,9 +1009,12 @@ static void hclge_parse_fiber_link_mode(struct hclge_dev *hdev,
        hclge_convert_setting_sr(mac, speed_ability);
        hclge_convert_setting_lr(mac, speed_ability);
        hclge_convert_setting_cr(mac, speed_ability);
+       if (hdev->pdev->revision >= 0x21)
+               hclge_convert_setting_fec(mac);
 
        linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, mac->supported);
        linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mac->supported);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported);
 }
 
 static void hclge_parse_backplane_link_mode(struct hclge_dev *hdev,
@@ -989,8 +1023,11 @@ static void hclge_parse_backplane_link_mode(struct hclge_dev *hdev,
        struct hclge_mac *mac = &hdev->hw.mac;
 
        hclge_convert_setting_kr(mac, speed_ability);
+       if (hdev->pdev->revision >= 0x21)
+               hclge_convert_setting_fec(mac);
        linkmode_set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, mac->supported);
        linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mac->supported);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported);
 }
 
 static void hclge_parse_copper_link_mode(struct hclge_dev *hdev,
@@ -2279,6 +2316,64 @@ static int hclge_restart_autoneg(struct hnae3_handle *handle)
        return hclge_notify_client(hdev, HNAE3_UP_CLIENT);
 }
 
+static int hclge_set_fec_hw(struct hclge_dev *hdev, u32 fec_mode)
+{
+       struct hclge_config_fec_cmd *req;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_FEC_MODE, false);
+
+       req = (struct hclge_config_fec_cmd *)desc.data;
+       if (fec_mode & BIT(HNAE3_FEC_AUTO))
+               hnae3_set_bit(req->fec_mode, HCLGE_MAC_CFG_FEC_AUTO_EN_B, 1);
+       if (fec_mode & BIT(HNAE3_FEC_RS))
+               hnae3_set_field(req->fec_mode, HCLGE_MAC_CFG_FEC_MODE_M,
+                               HCLGE_MAC_CFG_FEC_MODE_S, HCLGE_MAC_FEC_RS);
+       if (fec_mode & BIT(HNAE3_FEC_BASER))
+               hnae3_set_field(req->fec_mode, HCLGE_MAC_CFG_FEC_MODE_M,
+                               HCLGE_MAC_CFG_FEC_MODE_S, HCLGE_MAC_FEC_BASER);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               dev_err(&hdev->pdev->dev, "set fec mode failed %d.\n", ret);
+
+       return ret;
+}
+
+static int hclge_set_fec(struct hnae3_handle *handle, u32 fec_mode)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+       struct hclge_mac *mac = &hdev->hw.mac;
+       int ret;
+
+       if (fec_mode && !(mac->fec_ability & fec_mode)) {
+               dev_err(&hdev->pdev->dev, "unsupported fec mode\n");
+               return -EINVAL;
+       }
+
+       ret = hclge_set_fec_hw(hdev, fec_mode);
+       if (ret)
+               return ret;
+
+       mac->user_fec_mode = fec_mode | BIT(HNAE3_FEC_USER_DEF);
+       return 0;
+}
+
+static void hclge_get_fec(struct hnae3_handle *handle, u8 *fec_ability,
+                         u8 *fec_mode)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+       struct hclge_mac *mac = &hdev->hw.mac;
+
+       if (fec_ability)
+               *fec_ability = mac->fec_ability;
+       if (fec_mode)
+               *fec_mode = mac->fec_mode;
+}
+
 static int hclge_mac_init(struct hclge_dev *hdev)
 {
        struct hclge_mac *mac = &hdev->hw.mac;
@@ -2296,6 +2391,15 @@ static int hclge_mac_init(struct hclge_dev *hdev)
 
        mac->link = 0;
 
+       if (mac->user_fec_mode & BIT(HNAE3_FEC_USER_DEF)) {
+               ret = hclge_set_fec_hw(hdev, mac->user_fec_mode);
+               if (ret) {
+                       dev_err(&hdev->pdev->dev,
+                               "Fec mode init fail, ret = %d\n", ret);
+                       return ret;
+               }
+       }
+
        ret = hclge_set_mac_mtu(hdev, hdev->mps);
        if (ret) {
                dev_err(&hdev->pdev->dev, "set mtu failed ret=%d\n", ret);
@@ -8753,6 +8857,8 @@ static const struct hnae3_ae_ops hclge_ops = {
        .cfg_mac_speed_dup_h = hclge_cfg_mac_speed_dup_h,
        .get_media_type = hclge_get_media_type,
        .check_port_speed = hclge_check_port_speed,
+       .get_fec = hclge_get_fec,
+       .set_fec = hclge_set_fec,
        .get_rss_key_size = hclge_get_rss_key_size,
        .get_rss_indir_size = hclge_get_rss_indir_size,
        .get_rss = hclge_get_rss,
index 197e702..dd06b11 100644 (file)
@@ -253,7 +253,10 @@ struct hclge_mac {
        u32 speed;
        u32 speed_ability; /* speed ability supported by current media */
        u32 module_type; /* sub media type, e.g. kr/cr/sr/lr */
-       int link;       /* store the link status of mac & phy (if phy exit)*/
+       u32 fec_mode; /* active fec mode */
+       u32 user_fec_mode;
+       u32 fec_ability;
+       int link;       /* store the link status of mac & phy (if phy exit) */
        struct phy_device *phydev;
        struct mii_bus *mdio_bus;
        phy_interface_t phy_if;