1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
4  *
5  * Copyright (c) 2014-2017 Broadcom
6  */
7 
8 #define pr_fmt(fmt)				"bcmgenet_wol: " fmt
9 
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/sched.h>
13 #include <linux/types.h>
14 #include <linux/interrupt.h>
15 #include <linux/string.h>
16 #include <linux/init.h>
17 #include <linux/errno.h>
18 #include <linux/delay.h>
19 #include <linux/pm.h>
20 #include <linux/clk.h>
21 #include <linux/version.h>
22 #include <linux/platform_device.h>
23 #include <net/arp.h>
24 
25 #include <linux/mii.h>
26 #include <linux/ethtool.h>
27 #include <linux/netdevice.h>
28 #include <linux/inetdevice.h>
29 #include <linux/etherdevice.h>
30 #include <linux/skbuff.h>
31 #include <linux/in.h>
32 #include <linux/ip.h>
33 #include <linux/ipv6.h>
34 #include <linux/phy.h>
35 
36 #include "bcmgenet.h"
37 
38 /* ethtool function - get WOL (Wake on LAN) settings, Only Magic Packet
39  * Detection is supported through ethtool
40  */
41 void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
42 {
43 	struct bcmgenet_priv *priv = netdev_priv(dev);
44 	u32 reg;
45 
46 	wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE;
47 	wol->wolopts = priv->wolopts;
48 	memset(wol->sopass, 0, sizeof(wol->sopass));
49 
50 	if (wol->wolopts & WAKE_MAGICSECURE) {
51 		reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_MS);
52 		put_unaligned_be16(reg, &wol->sopass[0]);
53 		reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_LS);
54 		put_unaligned_be32(reg, &wol->sopass[2]);
55 	}
56 }
57 
58 /* ethtool function - set WOL (Wake on LAN) settings.
59  * Only for magic packet detection mode.
60  */
61 int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
62 {
63 	struct bcmgenet_priv *priv = netdev_priv(dev);
64 	struct device *kdev = &priv->pdev->dev;
65 	u32 reg;
66 
67 	if (!device_can_wakeup(kdev))
68 		return -ENOTSUPP;
69 
70 	if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE))
71 		return -EINVAL;
72 
73 	reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
74 	if (wol->wolopts & WAKE_MAGICSECURE) {
75 		bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]),
76 				     UMAC_MPD_PW_MS);
77 		bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]),
78 				     UMAC_MPD_PW_LS);
79 		reg |= MPD_PW_EN;
80 	} else {
81 		reg &= ~MPD_PW_EN;
82 	}
83 	bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
84 
85 	/* Flag the device and relevant IRQ as wakeup capable */
86 	if (wol->wolopts) {
87 		device_set_wakeup_enable(kdev, 1);
88 		/* Avoid unbalanced enable_irq_wake calls */
89 		if (priv->wol_irq_disabled)
90 			enable_irq_wake(priv->wol_irq);
91 		priv->wol_irq_disabled = false;
92 	} else {
93 		device_set_wakeup_enable(kdev, 0);
94 		/* Avoid unbalanced disable_irq_wake calls */
95 		if (!priv->wol_irq_disabled)
96 			disable_irq_wake(priv->wol_irq);
97 		priv->wol_irq_disabled = true;
98 	}
99 
100 	priv->wolopts = wol->wolopts;
101 
102 	return 0;
103 }
104 
105 static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
106 {
107 	struct net_device *dev = priv->dev;
108 	int retries = 0;
109 
110 	while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
111 		& RBUF_STATUS_WOL)) {
112 		retries++;
113 		if (retries > 5) {
114 			netdev_crit(dev, "polling wol mode timeout\n");
115 			return -ETIMEDOUT;
116 		}
117 		mdelay(1);
118 	}
119 
120 	return retries;
121 }
122 
123 int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
124 				enum bcmgenet_power_mode mode)
125 {
126 	struct net_device *dev = priv->dev;
127 	int retries = 0;
128 	u32 reg;
129 
130 	if (mode != GENET_POWER_WOL_MAGIC) {
131 		netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
132 		return -EINVAL;
133 	}
134 
135 	/* disable RX */
136 	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
137 	reg &= ~CMD_RX_EN;
138 	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
139 	mdelay(10);
140 
141 	reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
142 	reg |= MPD_EN;
143 	bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
144 
145 	/* Do not leave UniMAC in MPD mode only */
146 	retries = bcmgenet_poll_wol_status(priv);
147 	if (retries < 0) {
148 		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
149 		reg &= ~MPD_EN;
150 		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
151 		return retries;
152 	}
153 
154 	netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
155 		  retries);
156 
157 	/* Enable CRC forward */
158 	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
159 	priv->crc_fwd_en = 1;
160 	reg |= CMD_CRC_FWD;
161 
162 	/* Receiver must be enabled for WOL MP detection */
163 	reg |= CMD_RX_EN;
164 	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
165 
166 	if (priv->hw_params->flags & GENET_HAS_EXT) {
167 		reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
168 		reg &= ~EXT_ENERGY_DET_MASK;
169 		bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
170 	}
171 
172 	return 0;
173 }
174 
175 void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
176 			       enum bcmgenet_power_mode mode)
177 {
178 	u32 reg;
179 
180 	if (mode != GENET_POWER_WOL_MAGIC) {
181 		netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
182 		return;
183 	}
184 
185 	reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
186 	if (!(reg & MPD_EN))
187 		return;	/* already powered up so skip the rest */
188 	reg &= ~MPD_EN;
189 	bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
190 
191 	/* Disable CRC Forward */
192 	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
193 	reg &= ~CMD_CRC_FWD;
194 	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
195 	priv->crc_fwd_en = 0;
196 }
197