diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index 69423b8..5b77f0c 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -20,6 +20,11 @@
 #include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/crc16.h>
+#include <linux/inetdevice.h>
+#include <net/ipv6.h>
+#include <net/if_inet6.h>
 #include <linux/smscphy.h>
 
 /* Vendor-specific PHY Definitions */
@@ -46,6 +51,7 @@ struct smsc_hw_stat {
 struct smsc_phy_priv {
 	u16 intmask;
 	bool energy_enable;
+	struct ethtool_wolinfo wol;
 	struct clk *refclk;
 };
 
@@ -246,6 +252,312 @@ static int lan87xx_read_status(struct phy_device *phydev)
 	return err;
 }
 
+static int lan874x_phy_config_init(struct phy_device *phydev)
+{
+	u16 val;
+	int rc;
+
+	/* set nPME self clear delay time */
+	rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_MCFGR,
+			   MII_LAN874X_PHY_PME_SELF_CLEAR_DELAY);
+	if (rc < 0)
+		return rc;
+
+	/* MII_LAN874X_PHY_PME1_SET set LED1/nINT/nPME pin function as nPME */
+	/* MII_LAN874X_PHY_PME2_SET set LED2/nINT/nPME pin function as nPME */
+	/* set nPME auto clear */
+	val = (MII_LAN874X_PHY_PME2_SET | MII_LAN874X_PHY_PME_SELF_CLEAR);
+	rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR,
+			   val);
+	if (rc < 0)
+		return rc;
+
+	return smsc_phy_config_init(phydev);
+}
+
+static void lan874x_get_wol(struct phy_device *phydev,
+			    struct ethtool_wolinfo *wol)
+{
+	struct smsc_phy_priv *priv = phydev->priv;
+	u16 val_wucsr;
+	int rc;
+
+	wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC |
+			  WAKE_ARP | WAKE_MCAST);
+	wol->wolopts = 0;
+
+	rc = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR);
+	if (rc < 0)
+		return;
+
+	val_wucsr = (u16)rc;
+
+	if (val_wucsr & MII_LAN874X_PHY_WOL_PFDAEN)
+		wol->wolopts |= WAKE_UCAST;
+
+	if (val_wucsr & MII_LAN874X_PHY_WOL_BCSTEN)
+		wol->wolopts |= WAKE_BCAST;
+
+	if (val_wucsr & MII_LAN874X_PHY_WOL_MPEN)
+		wol->wolopts |= WAKE_MAGIC;
+
+	if (val_wucsr & MII_LAN874X_PHY_WOL_WUEN) {
+		if (priv->wol.wolopts & WAKE_ARP)
+			wol->wolopts |= WAKE_ARP;
+		if (priv->wol.wolopts & WAKE_MCAST)
+			wol->wolopts |= WAKE_MCAST;
+	}
+
+	/* WoL event notification */
+	if (val_wucsr & MII_LAN874X_PHY_WOL_WUFR) {
+		if (wol->wolopts & WAKE_ARP)
+			phydev_info(phydev, "ARP WoL event received\n");
+		if (wol->wolopts & WAKE_MCAST)
+			phydev_info(phydev, "MCAST WoL event received\n");
+	}
+
+	if (val_wucsr & MII_LAN874X_PHY_WOL_PFDA_FR)
+		phydev_info(phydev, "UCAST WoL event received\n");
+
+	if (val_wucsr & MII_LAN874X_PHY_WOL_BCAST_FR)
+		phydev_info(phydev, "BCAST WoL event received\n");
+
+	if (val_wucsr & MII_LAN874X_PHY_WOL_MPR)
+		phydev_info(phydev, "Magic WoL event received\n");
+
+	/* clear WoL event */
+	rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR,
+			   val_wucsr);
+	if (rc < 0)
+		return;
+}
+
+static u16 smsc_crc16(const u8 *buffer, size_t len)
+{
+	u16 crc = bitrev16(crc16(0xFFFF, buffer, len));
+	return crc;
+}
+
+static int lan874x_chk_wol_pattern(const u8 pattern[], const u16 *mask,
+				   u8 len, u8 *data, u8 *datalen)
+{
+	size_t i, j, k;
+	u16 bits;
+
+	i = 0;
+	k = 0;
+	while (len > 0) {
+		bits = *mask;
+		for (j = 0; j < 16; j++, i++, len--) {
+			/* No more pattern. */
+			if (!len) {
+				/* The rest of bitmap is not empty. */
+				if (bits)
+					return i + 1;
+				break;
+			}
+			if (bits & 1)
+				data[k++] = pattern[i];
+			bits >>= 1;
+		}
+		mask++;
+	}
+	*datalen = k;
+	return 0;
+}
+
+static int lan874x_set_wol_pattern(struct phy_device *phydev, u16 val,
+				   const u8 data[], u8 datalen,
+				   const u16 *mask, u8 masklen)
+{
+	u16 crc, reg;
+	int rc;
+
+	val |= MII_LAN874X_PHY_WOL_FILTER_EN;
+	rc = phy_write_mmd(phydev, MDIO_MMD_PCS,
+			   MII_LAN874X_PHY_MMD_WOL_WUF_CFGA, val);
+	if (rc < 0)
+		return rc;
+
+	crc = smsc_crc16(data, datalen);
+	rc = phy_write_mmd(phydev, MDIO_MMD_PCS,
+			   MII_LAN874X_PHY_MMD_WOL_WUF_CFGB, crc);
+	if (rc < 0)
+		return rc;
+
+	masklen = (masklen + 15) & ~0xf;
+	reg = MII_LAN874X_PHY_MMD_WOL_WUF_MASK7;
+	while (masklen >= 16) {
+		rc = phy_write_mmd(phydev, MDIO_MMD_PCS, reg, *mask);
+		if (rc < 0)
+			return rc;
+		reg--;
+		mask++;
+		masklen -= 16;
+	}
+
+	/* Clear out the rest of mask registers. */
+	while (reg != MII_LAN874X_PHY_MMD_WOL_WUF_MASK0) {
+		phy_write_mmd(phydev, MDIO_MMD_PCS, reg, 0);
+		reg--;
+	}
+	return rc;
+}
+
+static int lan874x_set_wol(struct phy_device *phydev,
+			   struct ethtool_wolinfo *wol)
+{
+	struct net_device *ndev = phydev->attached_dev;
+	struct smsc_phy_priv *priv = phydev->priv;
+	u16 val, val_wucsr;
+	int i = 0, rc;
+	u8 data[128];
+	u8 datalen;
+
+	if (wol->wolopts & WAKE_PHY)
+		return -EOPNOTSUPP;
+
+	/* lan874x has only one WoL filter pattern */
+	if (wol->wolopts & (WAKE_ARP | WAKE_MCAST)) {
+		if (wol->wolopts & WAKE_ARP)
+			i++;
+		if (wol->wolopts & WAKE_MCAST)
+			i++;
+		if (i >= 2) {
+			phydev_info(phydev,
+				    "lan874x WoL supports one of ARP|MCAST at a time\n");
+			return -EOPNOTSUPP;
+		}
+	}
+
+	rc = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR);
+	if (rc < 0)
+		return rc;
+
+	val_wucsr = (u16)rc;
+
+	if (wol->wolopts & WAKE_UCAST)
+		val_wucsr |= MII_LAN874X_PHY_WOL_PFDAEN;
+	else
+		val_wucsr &= ~MII_LAN874X_PHY_WOL_PFDAEN;
+
+	if (wol->wolopts & WAKE_BCAST)
+		val_wucsr |= MII_LAN874X_PHY_WOL_BCSTEN;
+	else
+		val_wucsr &= ~MII_LAN874X_PHY_WOL_BCSTEN;
+
+	if (wol->wolopts & WAKE_MAGIC)
+		val_wucsr |= MII_LAN874X_PHY_WOL_MPEN;
+	else
+		val_wucsr &= ~MII_LAN874X_PHY_WOL_MPEN;
+
+	/* Need to use pattern matching */
+	if (i > 0)
+		val_wucsr |= MII_LAN874X_PHY_WOL_WUEN;
+	else
+		val_wucsr &= ~MII_LAN874X_PHY_WOL_WUEN;
+
+	if (wol->wolopts & WAKE_ARP) {
+		const u8 *ip_addr =
+			((const u8 *)&((ndev->ip_ptr)->ifa_list)->ifa_address);
+		u8 pattern[42] = {
+			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x08, 0x06,
+			0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00 };
+		const u16 mask[3] = { 0xF03F, 0x003F, 0x03C0 };
+		u16 offset = 0;
+		u8 len = 42;
+
+		memcpy(&pattern[38], ip_addr, 4);
+		rc = lan874x_chk_wol_pattern(&pattern[offset], mask, len,
+					     data, &datalen);
+		if (rc)
+			phydev_info(phydev, "pattern not valid at %d\n", rc);
+
+		/* Need to match broadcast destination address. */
+		val = offset |
+		      MII_LAN874X_PHY_WOL_FILTER_BCSTEN;
+		rc = lan874x_set_wol_pattern(phydev, val, data, datalen, mask,
+					     len);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (wol->wolopts & WAKE_MCAST) {
+		u8 pattern[6] = { 0x33, 0x33, 0xFF, 0x00, 0x00, 0x00 };
+		u16 mask[1] = { 0x0007 };
+		u8 len = 3;
+
+		/* Try to match IPv6 Neighbor Solicitation. */
+		if (ndev->ip6_ptr) {
+			struct list_head *addr_list =
+				&ndev->ip6_ptr->addr_list;
+			struct inet6_ifaddr *ifa;
+
+			list_for_each_entry(ifa, addr_list, if_list) {
+				if (ifa->scope == IFA_LINK) {
+					memcpy(&pattern[3],
+					       &ifa->addr.in6_u.u6_addr8[13],
+					       3);
+					mask[0] = 0x003F;
+					len = 6;
+					break;
+				}
+			}
+		}
+		rc = lan874x_chk_wol_pattern(pattern, mask, len,
+					     data, &datalen);
+		if (rc)
+			phydev_info(phydev, "pattern not valid at %d\n", rc);
+
+		/* Need to match multicast destination address. */
+		val = 0 |
+		      MII_LAN874X_PHY_WOL_FILTER_MCASTTEN;
+		rc = lan874x_set_wol_pattern(phydev, val, data, datalen, mask,
+					     len);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (wol->wolopts & (WAKE_MAGIC | WAKE_UCAST)) {
+		const u8 *mac = (const u8 *)ndev->dev_addr;
+
+		if (!is_valid_ether_addr(mac))
+			return -EINVAL;
+
+		rc = phy_write_mmd(phydev, MDIO_MMD_PCS,
+				   MII_LAN874X_PHY_MMD_WOL_RX_ADDRC,
+				   ((mac[1] << 8) | mac[0]));
+		if (rc < 0)
+			return rc;
+
+		rc = phy_write_mmd(phydev, MDIO_MMD_PCS,
+				   MII_LAN874X_PHY_MMD_WOL_RX_ADDRB,
+				   ((mac[3] << 8) | mac[2]));
+		if (rc < 0)
+			return rc;
+
+		rc = phy_write_mmd(phydev, MDIO_MMD_PCS,
+				   MII_LAN874X_PHY_MMD_WOL_RX_ADDRA,
+				   ((mac[5] << 8) | mac[4]));
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR,
+			   val_wucsr);
+	if (rc < 0)
+		return rc;
+
+	priv->wol = *wol;
+	return 0;
+}
+
 static int smsc_get_sset_count(struct phy_device *phydev)
 {
 	return ARRAY_SIZE(smsc_hw_stats);
@@ -460,7 +772,7 @@ static int smsc_phy_probe(struct phy_device *phydev)
 
 	/* basic functions */
 	.read_status	= lan87xx_read_status,
-	.config_init	= smsc_phy_config_init,
+	.config_init	= lan874x_phy_config_init,
 	.soft_reset	= smsc_phy_reset,
 
 	/* IRQ related */
@@ -472,6 +784,10 @@ static int smsc_phy_probe(struct phy_device *phydev)
 	.get_strings	= smsc_get_strings,
 	.get_stats	= smsc_get_stats,
 
+	/* WoL */
+	.set_wol	= lan874x_set_wol,
+	.get_wol	= lan874x_get_wol,
+
 	.suspend	= genphy_suspend,
 	.resume		= genphy_resume,
 }, {
@@ -490,7 +806,7 @@ static int smsc_phy_probe(struct phy_device *phydev)
 
 	/* basic functions */
 	.read_status	= lan87xx_read_status,
-	.config_init	= smsc_phy_config_init,
+	.config_init	= lan874x_phy_config_init,
 	.soft_reset	= smsc_phy_reset,
 
 	/* IRQ related */
@@ -502,6 +818,10 @@ static int smsc_phy_probe(struct phy_device *phydev)
 	.get_strings	= smsc_get_strings,
 	.get_stats	= smsc_get_stats,
 
+	/* WoL */
+	.set_wol	= lan874x_set_wol,
+	.get_wol	= lan874x_get_wol,
+
 	.suspend	= genphy_suspend,
 	.resume		= genphy_resume,
 } };
diff --git a/include/linux/smscphy.h b/include/linux/smscphy.h
index 1a13627..f5e123b 100644
--- a/include/linux/smscphy.h
+++ b/include/linux/smscphy.h
@@ -28,4 +28,40 @@
 #define MII_LAN83C185_MODE_POWERDOWN 0xC0 /* Power Down mode */
 #define MII_LAN83C185_MODE_ALL       0xE0 /* All capable mode */
 
+#define MII_LAN874X_PHY_MMD_WOL_WUCSR		0x8010
+#define MII_LAN874X_PHY_MMD_WOL_WUF_CFGA	0x8011
+#define MII_LAN874X_PHY_MMD_WOL_WUF_CFGB	0x8012
+#define MII_LAN874X_PHY_MMD_WOL_WUF_MASK0	0x8021
+#define MII_LAN874X_PHY_MMD_WOL_WUF_MASK1	0x8022
+#define MII_LAN874X_PHY_MMD_WOL_WUF_MASK2	0x8023
+#define MII_LAN874X_PHY_MMD_WOL_WUF_MASK3	0x8024
+#define MII_LAN874X_PHY_MMD_WOL_WUF_MASK4	0x8025
+#define MII_LAN874X_PHY_MMD_WOL_WUF_MASK5	0x8026
+#define MII_LAN874X_PHY_MMD_WOL_WUF_MASK6	0x8027
+#define MII_LAN874X_PHY_MMD_WOL_WUF_MASK7	0x8028
+#define MII_LAN874X_PHY_MMD_WOL_RX_ADDRA	0x8061
+#define MII_LAN874X_PHY_MMD_WOL_RX_ADDRB	0x8062
+#define MII_LAN874X_PHY_MMD_WOL_RX_ADDRC	0x8063
+#define MII_LAN874X_PHY_MMD_MCFGR		0x8064
+
+#define MII_LAN874X_PHY_PME1_SET		(2<<13)
+#define MII_LAN874X_PHY_PME2_SET		(2<<11)
+#define MII_LAN874X_PHY_PME_SELF_CLEAR		BIT(9)
+#define MII_LAN874X_PHY_WOL_PFDA_FR		BIT(7)
+#define MII_LAN874X_PHY_WOL_WUFR		BIT(6)
+#define MII_LAN874X_PHY_WOL_MPR			BIT(5)
+#define MII_LAN874X_PHY_WOL_BCAST_FR		BIT(4)
+#define MII_LAN874X_PHY_WOL_PFDAEN		BIT(3)
+#define MII_LAN874X_PHY_WOL_WUEN		BIT(2)
+#define MII_LAN874X_PHY_WOL_MPEN		BIT(1)
+#define MII_LAN874X_PHY_WOL_BCSTEN		BIT(0)
+
+#define MII_LAN874X_PHY_WOL_FILTER_EN		BIT(15)
+#define MII_LAN874X_PHY_WOL_FILTER_MCASTTEN	BIT(9)
+#define MII_LAN874X_PHY_WOL_FILTER_BCSTEN	BIT(8)
+
+#define MII_LAN874X_PHY_PME_SELF_CLEAR_DELAY	0x1000 /* 81 milliseconds */
+
+#define MII_LAN874X_PHY_WOL_IMEN		BIT(8) /* WoL intr enable  */
+
 #endif /* __LINUX_SMSCPHY_H__ */
