diff mbox series

[net-next,1/2] net: hns3: support wake on lan configuration and query

Message ID 20230104013405.65433-2-lanhao@huawei.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series net: hns3: support wake on lan for hns3 | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1 this patch: 1
netdev/cc_maintainers warning 2 maintainers not CCed: huangguangbin2@huawei.com wangjie125@huawei.com
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 5 this patch: 5
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 383 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Hao Lan Jan. 4, 2023, 1:34 a.m. UTC
Implement configuration and query WOL by ethtool and
added the needed device commands and structures to hns3.
Add it do not support suspend resume interface.

Signed-off-by: Hao Lan <lanhao@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hnae3.h   |  12 ++
 .../hns3/hns3_common/hclge_comm_cmd.c         |   1 +
 .../hns3/hns3_common/hclge_comm_cmd.h         |   3 +
 .../ethernet/hisilicon/hns3/hns3_ethtool.c    |  27 +++
 .../hisilicon/hns3/hns3pf/hclge_cmd.h         |  26 +++
 .../hisilicon/hns3/hns3pf/hclge_main.c        | 204 ++++++++++++++++++
 .../hisilicon/hns3/hns3pf/hclge_main.h        |  10 +
 7 files changed, 283 insertions(+)

Comments

Andrew Lunn Jan. 4, 2023, 2:10 a.m. UTC | #1
> +enum HCLGE_WOL_MODE {
> +	HCLGE_WOL_PHY		= BIT(0),
> +	HCLGE_WOL_UNICAST	= BIT(1),
> +	HCLGE_WOL_MULTICAST	= BIT(2),
> +	HCLGE_WOL_BROADCAST	= BIT(3),
> +	HCLGE_WOL_ARP		= BIT(4),
> +	HCLGE_WOL_MAGIC		= BIT(5),
> +	HCLGE_WOL_MAGICSECURED	= BIT(6),
> +	HCLGE_WOL_FILTER	= BIT(7),
> +	HCLGE_WOL_DISABLE	= 0,
> +};

These are the exact same values as WAKE_PHY, WAKE_CAST etc. Since they
are ABI, they will never change. So you may as well throw these away
and just use the Linux values.

>  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,
> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
> index 4e54f91f7a6c..88cb5c05bc43 100644
> --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
> @@ -11500,6 +11500,201 @@ static void hclge_uninit_rxd_adv_layout(struct hclge_dev *hdev)
>  		hclge_write_dev(&hdev->hw, HCLGE_RXD_ADV_LAYOUT_EN_REG, 0);
>  }
>  
> +static __u32 hclge_wol_mode_to_ethtool(u32 mode)
> +{
> +	__u32 ret = 0;
> +
> +	if (mode & HCLGE_WOL_PHY)
> +		ret |= WAKE_PHY;
> +
> +	if (mode & HCLGE_WOL_UNICAST)
> +		ret |= WAKE_UCAST;
> +
> +	if (mode & HCLGE_WOL_MULTICAST)
> +		ret |= WAKE_MCAST;
> +
> +	if (mode & HCLGE_WOL_BROADCAST)
> +		ret |= WAKE_BCAST;
> +
> +	if (mode & HCLGE_WOL_ARP)
> +		ret |= WAKE_ARP;
> +
> +	if (mode & HCLGE_WOL_MAGIC)
> +		ret |= WAKE_MAGIC;
> +
> +	if (mode & HCLGE_WOL_MAGICSECURED)
> +		ret |= WAKE_MAGICSECURE;
> +
> +	if (mode & HCLGE_WOL_FILTER)
> +		ret |= WAKE_FILTER;

Once you throw away HCLGE_WOL_*, this function becomes much simpler.

> +
> +	return ret;
> +}
> +
> +static u32 hclge_wol_mode_from_ethtool(__u32 mode)
> +{
> +	u32 ret = HCLGE_WOL_DISABLE;
> +
> +	if (mode & WAKE_PHY)
> +		ret |= HCLGE_WOL_PHY;
> +
> +	if (mode & WAKE_UCAST)
> +		ret |= HCLGE_WOL_UNICAST;

This one two.

> @@ -12075,6 +12275,8 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
>  
>  	hclge_init_rxd_adv_layout(hdev);
>  
> +	(void)hclge_update_wol(hdev);

Please avoid casts like this. If there is an error, you should not
ignore it. If it cannot fail, make it a void function.	

       Andrew
Hao Lan Jan. 4, 2023, 12:57 p.m. UTC | #2
Hi Andrew,
Thank you for reviewing our code. Thank you very much.
You're right, such as WAKE_PHY, WAKE_CAST, these are implemented
in the kernel, they are ABI, they will never change, we use it
will directly simplify our code, your advice is very useful, thank
you very much for your advice.
However, these interfaces serve as a buffer between our firmware
and the linux community. Considering our interface expansion and
evolution, we may add some private modes in the future.
If the Linux community does not accept our private modes,
we will not be able to carry out these work.
So please let us keep enum HCLGE_WOL_MODE, thank you.

Best regards,
Hao Lan

On 2023/1/4 10:10, Andrew Lunn wrote:
>> +enum HCLGE_WOL_MODE {
>> +	HCLGE_WOL_PHY		= BIT(0),
>> +	HCLGE_WOL_UNICAST	= BIT(1),
>> +	HCLGE_WOL_MULTICAST	= BIT(2),
>> +	HCLGE_WOL_BROADCAST	= BIT(3),
>> +	HCLGE_WOL_ARP		= BIT(4),
>> +	HCLGE_WOL_MAGIC		= BIT(5),
>> +	HCLGE_WOL_MAGICSECURED	= BIT(6),
>> +	HCLGE_WOL_FILTER	= BIT(7),
>> +	HCLGE_WOL_DISABLE	= 0,
>> +};
> 
> These are the exact same values as WAKE_PHY, WAKE_CAST etc. Since they
> are ABI, they will never change. So you may as well throw these away
> and just use the Linux values.
> 

>>  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,
>> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
>> index 4e54f91f7a6c..88cb5c05bc43 100644
>> --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
>> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
>> @@ -11500,6 +11500,201 @@ static void hclge_uninit_rxd_adv_layout(struct hclge_dev *hdev)
>>  		hclge_write_dev(&hdev->hw, HCLGE_RXD_ADV_LAYOUT_EN_REG, 0);
>>  }
>>  
>> +static __u32 hclge_wol_mode_to_ethtool(u32 mode)
>> +{
>> +	__u32 ret = 0;
>> +
>> +	if (mode & HCLGE_WOL_PHY)
>> +		ret |= WAKE_PHY;
>> +
>> +	if (mode & HCLGE_WOL_UNICAST)
>> +		ret |= WAKE_UCAST;
>> +
>> +	if (mode & HCLGE_WOL_MULTICAST)
>> +		ret |= WAKE_MCAST;
>> +
>> +	if (mode & HCLGE_WOL_BROADCAST)
>> +		ret |= WAKE_BCAST;
>> +
>> +	if (mode & HCLGE_WOL_ARP)
>> +		ret |= WAKE_ARP;
>> +
>> +	if (mode & HCLGE_WOL_MAGIC)
>> +		ret |= WAKE_MAGIC;
>> +
>> +	if (mode & HCLGE_WOL_MAGICSECURED)
>> +		ret |= WAKE_MAGICSECURE;
>> +
>> +	if (mode & HCLGE_WOL_FILTER)
>> +		ret |= WAKE_FILTER;
> 
> Once you throw away HCLGE_WOL_*, this function becomes much simpler.
> 
>> +
>> +	return ret;
>> +}
>> +
>> +static u32 hclge_wol_mode_from_ethtool(__u32 mode)
>> +{
>> +	u32 ret = HCLGE_WOL_DISABLE;
>> +
>> +	if (mode & WAKE_PHY)
>> +		ret |= HCLGE_WOL_PHY;
>> +
>> +	if (mode & WAKE_UCAST)
>> +		ret |= HCLGE_WOL_UNICAST;
> 
> This one two.
> 
>> @@ -12075,6 +12275,8 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
>>  
>>  	hclge_init_rxd_adv_layout(hdev);
>>  
>> +	(void)hclge_update_wol(hdev);
> 
> Please avoid casts like this. If there is an error, you should not
> ignore it. If it cannot fail, make it a void function.	
> 
>        Andrew
> .
>
Andrew Lunn Jan. 4, 2023, 3 p.m. UTC | #3
On Wed, Jan 04, 2023 at 08:57:40PM +0800, Hao Lan wrote:
> Hi Andrew,
> Thank you for reviewing our code. Thank you very much.
> You're right, such as WAKE_PHY, WAKE_CAST, these are implemented
> in the kernel, they are ABI, they will never change, we use it
> will directly simplify our code, your advice is very useful, thank
> you very much for your advice.
> However, these interfaces serve as a buffer between our firmware
> and the linux community. Considering our interface expansion and
> evolution, we may add some private modes in the future.

You cannot add private WOL modes, since they will be unusable without
ethtool support. And to add ethtool support, they need to be
public. And to make them public, you just add more WAKE_ macros.  Just
make sure the new modes you add are well described, so other drivers
can also implement them.

	Andrew
diff mbox series

Patch

diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 17137de9338c..312ac1cccd39 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -99,6 +99,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) \
@@ -167,6 +168,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,
 };
@@ -560,6 +564,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);
@@ -759,6 +767,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 {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c
index f671a63cecde..cbbab5b2b402 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c
@@ -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[] = {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h
index b1f9383b418f..de72ecbfd5ad 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h
@@ -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 {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
index 55306fe8a540..1bd95f04d327 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
@@ -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);
+	struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
+	const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+
+	if (!hnae3_ae_dev_wol_supported(ae_dev) || !ops->get_wol)
+		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);
+	struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
+	const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+
+	if (!hnae3_ae_dev_wol_supported(ae_dev) || !ops->set_wol)
+		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)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 43cada51d8cb..e6e94dae1b1a 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -872,6 +872,32 @@  struct hclge_phy_reg_cmd {
 	u8 rsv1[18];
 };
 
+enum HCLGE_WOL_MODE {
+	HCLGE_WOL_PHY		= BIT(0),
+	HCLGE_WOL_UNICAST	= BIT(1),
+	HCLGE_WOL_MULTICAST	= BIT(2),
+	HCLGE_WOL_BROADCAST	= BIT(3),
+	HCLGE_WOL_ARP		= BIT(4),
+	HCLGE_WOL_MAGIC		= BIT(5),
+	HCLGE_WOL_MAGICSECURED	= BIT(6),
+	HCLGE_WOL_FILTER	= BIT(7),
+	HCLGE_WOL_DISABLE	= 0,
+};
+
+#define HCLGE_SOPASS_MAX	6
+
+struct hclge_wol_cfg_cmd {
+	__le32 wake_on_lan_mode;
+	u8 sopass[HCLGE_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,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 4e54f91f7a6c..88cb5c05bc43 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -11500,6 +11500,201 @@  static void hclge_uninit_rxd_adv_layout(struct hclge_dev *hdev)
 		hclge_write_dev(&hdev->hw, HCLGE_RXD_ADV_LAYOUT_EN_REG, 0);
 }
 
+static __u32 hclge_wol_mode_to_ethtool(u32 mode)
+{
+	__u32 ret = 0;
+
+	if (mode & HCLGE_WOL_PHY)
+		ret |= WAKE_PHY;
+
+	if (mode & HCLGE_WOL_UNICAST)
+		ret |= WAKE_UCAST;
+
+	if (mode & HCLGE_WOL_MULTICAST)
+		ret |= WAKE_MCAST;
+
+	if (mode & HCLGE_WOL_BROADCAST)
+		ret |= WAKE_BCAST;
+
+	if (mode & HCLGE_WOL_ARP)
+		ret |= WAKE_ARP;
+
+	if (mode & HCLGE_WOL_MAGIC)
+		ret |= WAKE_MAGIC;
+
+	if (mode & HCLGE_WOL_MAGICSECURED)
+		ret |= WAKE_MAGICSECURE;
+
+	if (mode & HCLGE_WOL_FILTER)
+		ret |= WAKE_FILTER;
+
+	return ret;
+}
+
+static u32 hclge_wol_mode_from_ethtool(__u32 mode)
+{
+	u32 ret = HCLGE_WOL_DISABLE;
+
+	if (mode & WAKE_PHY)
+		ret |= HCLGE_WOL_PHY;
+
+	if (mode & WAKE_UCAST)
+		ret |= HCLGE_WOL_UNICAST;
+
+	if (mode & WAKE_MCAST)
+		ret |= HCLGE_WOL_MULTICAST;
+
+	if (mode & WAKE_BCAST)
+		ret |= HCLGE_WOL_BROADCAST;
+
+	if (mode & WAKE_ARP)
+		ret |= HCLGE_WOL_ARP;
+
+	if (mode & WAKE_MAGIC)
+		ret |= HCLGE_WOL_MAGIC;
+
+	if (mode & WAKE_MAGICSECURE)
+		ret |= HCLGE_WOL_MAGICSECURED;
+
+	if (mode & WAKE_FILTER)
+		ret |= HCLGE_WOL_FILTER;
+
+	return ret;
+}
+
+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;
+}
+
+int hclge_get_wol_cfg(struct hclge_dev *hdev, u32 *mode)
+{
+	struct hclge_wol_cfg_cmd *wol_cfg_cmd;
+	struct hclge_desc desc;
+	int ret;
+
+	hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_WOL_CFG, true);
+	ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+	if (ret) {
+		dev_err(&hdev->pdev->dev,
+			"failed to get wol config, ret = %d\n", ret);
+		return ret;
+	}
+
+	wol_cfg_cmd = (struct hclge_wol_cfg_cmd *)desc.data;
+	*mode = le32_to_cpu(wol_cfg_cmd->wake_on_lan_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, HCLGE_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 = HCLGE_WOL_DISABLE;
+		return ret;
+	}
+
+	return hclge_update_wol(hdev);
+}
+
+static void hclge_get_wol(struct hnae3_handle *handle,
+			  struct ethtool_wolinfo *wol)
+{
+	struct hclge_vport *vport = hclge_get_vport(handle);
+	struct hclge_dev *hdev = vport->back;
+	struct hclge_wol_info *wol_info = &hdev->hw.mac.wol;
+
+	wol->supported = hclge_wol_mode_to_ethtool(wol_info->wol_support_mode);
+	wol->wolopts = hclge_wol_mode_to_ethtool(wol_info->wol_current_mode);
+	if (wol_info->wol_current_mode & HCLGE_WOL_MAGICSECURED)
+		memcpy(wol->sopass, wol_info->wol_sopass, HCLGE_SOPASS_MAX);
+}
+
+static int hclge_set_wol(struct hnae3_handle *handle,
+			 struct ethtool_wolinfo *wol)
+{
+	struct hclge_vport *vport = hclge_get_vport(handle);
+	struct hclge_dev *hdev = vport->back;
+	struct hclge_wol_info *wol_info = &hdev->hw.mac.wol;
+	u32 wol_mode;
+	int ret;
+
+	wol_mode = hclge_wol_mode_from_ethtool(wol->wolopts);
+	if (wol_mode & ~wol_info->wol_support_mode)
+		return -EINVAL;
+
+	wol_info->wol_current_mode = wol_mode;
+	if (wol_mode & HCLGE_WOL_MAGICSECURED) {
+		memcpy(wol_info->wol_sopass, wol->sopass, HCLGE_SOPASS_MAX);
+		wol_info->wol_sopass_size = HCLGE_SOPASS_MAX;
+	} else {
+		wol_info->wol_sopass_size = 0;
+	}
+
+	ret = hclge_set_wol_cfg(hdev, wol_info);
+	if (ret)
+		wol_info->wol_current_mode = HCLGE_WOL_DISABLE;
+
+	return ret;
+}
+
 static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
 	struct pci_dev *pdev = ae_dev->pdev;
@@ -11696,6 +11891,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;
 
@@ -12075,6 +12275,8 @@  static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
 
 	hclge_init_rxd_adv_layout(hdev);
 
+	(void)hclge_update_wol(hdev);
+
 	dev_info(&pdev->dev, "Reset done, %s driver initialization finished.\n",
 		 HCLGE_DRIVER_NAME);
 
@@ -13105,6 +13307,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 = {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index 495b639b0dc2..3be92ceb5744 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -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[HCLGE_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;
@@ -1141,4 +1149,6 @@  int hclge_dbg_dump_rst_info(struct hclge_dev *hdev, char *buf, int len);
 int hclge_push_vf_link_status(struct hclge_vport *vport);
 int hclge_enable_vport_vlan_filter(struct hclge_vport *vport, bool request_en);
 int hclge_mac_update_stats(struct hclge_dev *hdev);
+int hclge_get_wol_supported_mode(struct hclge_dev *hdev, u32 *wol_supported);
+int hclge_get_wol_cfg(struct hclge_dev *hdev, u32 *mode);
 #endif