]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
net: hns3: support wake on lan configuration and query
authorHao Lan <lanhao@huawei.com>
Mon, 27 Mar 2023 13:55:04 +0000 (21:55 +0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 29 Mar 2023 08:07:42 +0000 (09:07 +0100)
The HNS3 driver supports Wake-on-LAN, which can wake up
the server from power off state to power on state by magic
packet or magic security packet.

ChangeLog:
v1->v2:
Deleted the debugfs function that overlaps with the ethtool function
from suggestion of Andrew Lunn.

v2->v3:
Return the wol configuration stored in driver,
suggested by Alexander H Duyck.

v3->v4:
Add a helper to go from netdev to the local struct,
suggested by Simon Horman and Jakub Kicinski.

Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: Hao Lan <lanhao@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c
drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
drivers/net/ethernet/hisilicon/hns3/hns3_enet.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 40f4306449eb257f2c2488f06c1ca62bc1a57988..9c9c72dc57e00d886fa5f83aaaebb0ce7e25681d 100644 (file)
@@ -100,6 +100,7 @@ enum HNAE3_DEV_CAP_BITS {
        HNAE3_DEV_SUPPORT_CQ_B,
        HNAE3_DEV_SUPPORT_FEC_STATS_B,
        HNAE3_DEV_SUPPORT_LANE_NUM_B,
+       HNAE3_DEV_SUPPORT_WOL_B,
 };
 
 #define hnae3_ae_dev_fd_supported(ae_dev) \
@@ -168,6 +169,9 @@ enum HNAE3_DEV_CAP_BITS {
 #define hnae3_ae_dev_lane_num_supported(ae_dev) \
        test_bit(HNAE3_DEV_SUPPORT_LANE_NUM_B, (ae_dev)->caps)
 
+#define hnae3_ae_dev_wol_supported(ae_dev) \
+       test_bit(HNAE3_DEV_SUPPORT_WOL_B, (ae_dev)->caps)
+
 enum HNAE3_PF_CAP_BITS {
        HNAE3_PF_SUPPORT_VLAN_FLTR_MDF_B = 0,
 };
@@ -561,6 +565,10 @@ struct hnae3_ae_dev {
  *   Get phc info
  * clean_vf_config
  *   Clean residual vf info after disable sriov
+ * get_wol
+ *   Get wake on lan info
+ * set_wol
+ *   Config wake on lan
  */
 struct hnae3_ae_ops {
        int (*init_ae_dev)(struct hnae3_ae_dev *ae_dev);
@@ -760,6 +768,10 @@ struct hnae3_ae_ops {
        void (*clean_vf_config)(struct hnae3_ae_dev *ae_dev, int num_vfs);
        int (*get_dscp_prio)(struct hnae3_handle *handle, u8 dscp,
                             u8 *tc_map_mode, u8 *priority);
+       void (*get_wol)(struct hnae3_handle *handle,
+                       struct ethtool_wolinfo *wol);
+       int (*set_wol)(struct hnae3_handle *handle,
+                      struct ethtool_wolinfo *wol);
 };
 
 struct hnae3_dcb_ops {
index f671a63cecde4824f57054bf176038a261d58a60..cbbab5b2b402b70d4b1a2f680fa8b773c1cb7744 100644 (file)
@@ -155,6 +155,7 @@ static const struct hclge_comm_caps_bit_map hclge_pf_cmd_caps[] = {
        {HCLGE_COMM_CAP_FD_B, HNAE3_DEV_SUPPORT_FD_B},
        {HCLGE_COMM_CAP_FEC_STATS_B, HNAE3_DEV_SUPPORT_FEC_STATS_B},
        {HCLGE_COMM_CAP_LANE_NUM_B, HNAE3_DEV_SUPPORT_LANE_NUM_B},
+       {HCLGE_COMM_CAP_WOL_B, HNAE3_DEV_SUPPORT_WOL_B},
 };
 
 static const struct hclge_comm_caps_bit_map hclge_vf_cmd_caps[] = {
index b1f9383b418f4774544dcafecdaed2a7bfe48d3b..de72ecbfd5ad8549653dc730e986a1b3a0f37af9 100644 (file)
@@ -294,6 +294,8 @@ enum hclge_opcode_type {
        HCLGE_PPP_CMD0_INT_CMD          = 0x2100,
        HCLGE_PPP_CMD1_INT_CMD          = 0x2101,
        HCLGE_MAC_ETHERTYPE_IDX_RD      = 0x2105,
+       HCLGE_OPC_WOL_GET_SUPPORTED_MODE        = 0x2201,
+       HCLGE_OPC_WOL_CFG               = 0x2202,
        HCLGE_NCSI_INT_EN               = 0x2401,
 
        /* ROH MAC commands */
@@ -345,6 +347,7 @@ enum HCLGE_COMM_CAP_BITS {
        HCLGE_COMM_CAP_FD_B = 21,
        HCLGE_COMM_CAP_FEC_STATS_B = 25,
        HCLGE_COMM_CAP_LANE_NUM_B = 27,
+       HCLGE_COMM_CAP_WOL_B = 28,
 };
 
 enum HCLGE_COMM_API_CAP_BITS {
index 66feb23f7b7b6330683f8c5326e230008782c33f..4c3e90a1c4d07368ad4757926aeced0ea8832808 100644 (file)
@@ -408,6 +408,9 @@ static struct hns3_dbg_cap_info hns3_dbg_cap[] = {
        }, {
                .name = "support lane num",
                .cap_bit = HNAE3_DEV_SUPPORT_LANE_NUM_B,
+       }, {
+               .name = "support wake on lan",
+               .cap_bit = HNAE3_DEV_SUPPORT_WOL_B,
        }
 };
 
index 294a14b4fdefb5e844bac2bdf30c9c3f5967188e..88af34bbee34bad0be2aa1e28678fb00c37c35a3 100644 (file)
@@ -695,6 +695,12 @@ static inline unsigned int hns3_page_order(struct hns3_enet_ring *ring)
 #define hns3_get_handle(ndev) \
        (((struct hns3_nic_priv *)netdev_priv(ndev))->ae_handle)
 
+#define hns3_get_ae_dev(handle) \
+       (pci_get_drvdata((handle)->pdev))
+
+#define hns3_get_ops(handle) \
+       ((handle)->ae_algo->ops)
+
 #define hns3_gl_usec_to_reg(int_gl) ((int_gl) >> 1)
 #define hns3_gl_round_down(int_gl) round_down(int_gl, 2)
 
index 55306fe8a5402d6227a85b9019275a5402ca90f1..51d1278b18f6637d1e7bd13bc04970d6843d41b3 100644 (file)
@@ -2063,6 +2063,31 @@ static int hns3_get_link_ext_state(struct net_device *netdev,
        return -ENODATA;
 }
 
+static void hns3_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+{
+       struct hnae3_handle *handle = hns3_get_handle(netdev);
+       const struct hnae3_ae_ops *ops = hns3_get_ops(handle);
+       struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(handle);
+
+       if (!hnae3_ae_dev_wol_supported(ae_dev))
+               return;
+
+       ops->get_wol(handle, wol);
+}
+
+static int hns3_set_wol(struct net_device *netdev,
+                       struct ethtool_wolinfo *wol)
+{
+       struct hnae3_handle *handle = hns3_get_handle(netdev);
+       const struct hnae3_ae_ops *ops = hns3_get_ops(handle);
+       struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(handle);
+
+       if (!hnae3_ae_dev_wol_supported(ae_dev))
+               return -EOPNOTSUPP;
+
+       return ops->set_wol(handle, wol);
+}
+
 static const struct ethtool_ops hns3vf_ethtool_ops = {
        .supported_coalesce_params = HNS3_ETHTOOL_COALESCE,
        .supported_ring_params = HNS3_ETHTOOL_RING,
@@ -2139,6 +2164,8 @@ static const struct ethtool_ops hns3_ethtool_ops = {
        .set_tunable = hns3_set_tunable,
        .reset = hns3_set_reset,
        .get_link_ext_state = hns3_get_link_ext_state,
+       .get_wol = hns3_get_wol,
+       .set_wol = hns3_set_wol,
 };
 
 void hns3_ethtool_set_ops(struct net_device *netdev)
index 43cada51d8cb31f6576966a551510a50ba5a0f2e..91c173f40701a7b203fc2559dc2a972b8300b3d5 100644 (file)
@@ -872,6 +872,18 @@ struct hclge_phy_reg_cmd {
        u8 rsv1[18];
 };
 
+struct hclge_wol_cfg_cmd {
+       __le32 wake_on_lan_mode;
+       u8 sopass[SOPASS_MAX];
+       u8 sopass_size;
+       u8 rsv[13];
+};
+
+struct hclge_query_wol_supported_cmd {
+       __le32 supported_wake_mode;
+       u8 rsv[20];
+};
+
 struct hclge_hw;
 int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num);
 enum hclge_comm_cmd_status hclge_cmd_mdio_write(struct hclge_hw *hw,
index c3851a6e10c02dfa1fff87a8370161eb750e90fd..4fb5406c1951dce7095010a4e7c7528d8e59091b 100644 (file)
@@ -11522,6 +11522,124 @@ static void hclge_uninit_rxd_adv_layout(struct hclge_dev *hdev)
                hclge_write_dev(&hdev->hw, HCLGE_RXD_ADV_LAYOUT_EN_REG, 0);
 }
 
+static struct hclge_wol_info *hclge_get_wol_info(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+
+       return &vport->back->hw.mac.wol;
+}
+
+static int hclge_get_wol_supported_mode(struct hclge_dev *hdev,
+                                       u32 *wol_supported)
+{
+       struct hclge_query_wol_supported_cmd *wol_supported_cmd;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_WOL_GET_SUPPORTED_MODE,
+                                  true);
+       wol_supported_cmd = (struct hclge_query_wol_supported_cmd *)desc.data;
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed to query wol supported, ret = %d\n", ret);
+               return ret;
+       }
+
+       *wol_supported = le32_to_cpu(wol_supported_cmd->supported_wake_mode);
+
+       return 0;
+}
+
+static int hclge_set_wol_cfg(struct hclge_dev *hdev,
+                            struct hclge_wol_info *wol_info)
+{
+       struct hclge_wol_cfg_cmd *wol_cfg_cmd;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_WOL_CFG, false);
+       wol_cfg_cmd = (struct hclge_wol_cfg_cmd *)desc.data;
+       wol_cfg_cmd->wake_on_lan_mode = cpu_to_le32(wol_info->wol_current_mode);
+       wol_cfg_cmd->sopass_size = wol_info->wol_sopass_size;
+       memcpy(wol_cfg_cmd->sopass, wol_info->wol_sopass, SOPASS_MAX);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "failed to set wol config, ret = %d\n", ret);
+
+       return ret;
+}
+
+static int hclge_update_wol(struct hclge_dev *hdev)
+{
+       struct hclge_wol_info *wol_info = &hdev->hw.mac.wol;
+
+       if (!hnae3_ae_dev_wol_supported(hdev->ae_dev))
+               return 0;
+
+       return hclge_set_wol_cfg(hdev, wol_info);
+}
+
+static int hclge_init_wol(struct hclge_dev *hdev)
+{
+       struct hclge_wol_info *wol_info = &hdev->hw.mac.wol;
+       int ret;
+
+       if (!hnae3_ae_dev_wol_supported(hdev->ae_dev))
+               return 0;
+
+       memset(wol_info, 0, sizeof(struct hclge_wol_info));
+       ret = hclge_get_wol_supported_mode(hdev,
+                                          &wol_info->wol_support_mode);
+       if (ret) {
+               wol_info->wol_support_mode = 0;
+               return ret;
+       }
+
+       return hclge_update_wol(hdev);
+}
+
+static void hclge_get_wol(struct hnae3_handle *handle,
+                         struct ethtool_wolinfo *wol)
+{
+       struct hclge_wol_info *wol_info = hclge_get_wol_info(handle);
+
+       wol->supported = wol_info->wol_support_mode;
+       wol->wolopts = wol_info->wol_current_mode;
+       if (wol_info->wol_current_mode & WAKE_MAGICSECURE)
+               memcpy(wol->sopass, wol_info->wol_sopass, SOPASS_MAX);
+}
+
+static int hclge_set_wol(struct hnae3_handle *handle,
+                        struct ethtool_wolinfo *wol)
+{
+       struct hclge_wol_info *wol_info = hclge_get_wol_info(handle);
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       u32 wol_mode;
+       int ret;
+
+       wol_mode = wol->wolopts;
+       if (wol_mode & ~wol_info->wol_support_mode)
+               return -EINVAL;
+
+       wol_info->wol_current_mode = wol_mode;
+       if (wol_mode & WAKE_MAGICSECURE) {
+               memcpy(wol_info->wol_sopass, wol->sopass, SOPASS_MAX);
+               wol_info->wol_sopass_size = SOPASS_MAX;
+       } else {
+               wol_info->wol_sopass_size = 0;
+       }
+
+       ret = hclge_set_wol_cfg(vport->back, wol_info);
+       if (ret)
+               wol_info->wol_current_mode = 0;
+
+       return ret;
+}
+
 static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
        struct pci_dev *pdev = ae_dev->pdev;
@@ -11718,6 +11836,11 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        /* Enable MISC vector(vector0) */
        hclge_enable_vector(&hdev->misc_vector, true);
 
+       ret = hclge_init_wol(hdev);
+       if (ret)
+               dev_warn(&pdev->dev,
+                        "failed to wake on lan init, ret = %d\n", ret);
+
        hclge_state_init(hdev);
        hdev->last_reset_time = jiffies;
 
@@ -12096,6 +12219,11 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
 
        hclge_init_rxd_adv_layout(hdev);
 
+       ret = hclge_update_wol(hdev);
+       if (ret)
+               dev_warn(&pdev->dev,
+                        "failed to update wol config, ret = %d\n", ret);
+
        dev_info(&pdev->dev, "Reset done, %s driver initialization finished.\n",
                 HCLGE_DRIVER_NAME);
 
@@ -13142,6 +13270,8 @@ static const struct hnae3_ae_ops hclge_ops = {
        .get_link_diagnosis_info = hclge_get_link_diagnosis_info,
        .clean_vf_config = hclge_clean_vport_config,
        .get_dscp_prio = hclge_get_dscp_prio,
+       .get_wol = hclge_get_wol,
+       .set_wol = hclge_set_wol,
 };
 
 static struct hnae3_ae_algo ae_algo = {
index 13f23d606e77bdeb06692a73dad69b3a92b3c6f4..81aa6b0facf5a422a2b197c26b7f8fdda52a5d24 100644 (file)
@@ -249,6 +249,13 @@ enum HCLGE_MAC_DUPLEX {
 #define QUERY_SFP_SPEED                0
 #define QUERY_ACTIVE_SPEED     1
 
+struct hclge_wol_info {
+       u32 wol_support_mode; /* store the wake on lan info */
+       u32 wol_current_mode;
+       u8 wol_sopass[SOPASS_MAX];
+       u8 wol_sopass_size;
+};
+
 struct hclge_mac {
        u8 mac_id;
        u8 phy_addr;
@@ -268,6 +275,7 @@ struct hclge_mac {
        u32 user_fec_mode;
        u32 fec_ability;
        int link;       /* store the link status of mac & phy (if phy exists) */
+       struct hclge_wol_info wol;
        struct phy_device *phydev;
        struct mii_bus *mdio_bus;
        phy_interface_t phy_if;