• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
4  *
5  * Copyright (c) 2014-2020 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  */
bcmgenet_get_wol(struct net_device * dev,struct ethtool_wolinfo * wol)41 void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
42 {
43 	struct bcmgenet_priv *priv = netdev_priv(dev);
44 	struct device *kdev = &priv->pdev->dev;
45 
46 	if (!device_can_wakeup(kdev)) {
47 		wol->supported = 0;
48 		wol->wolopts = 0;
49 		return;
50 	}
51 
52 	wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
53 	wol->wolopts = priv->wolopts;
54 	memset(wol->sopass, 0, sizeof(wol->sopass));
55 
56 	if (wol->wolopts & WAKE_MAGICSECURE)
57 		memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
58 }
59 
60 /* ethtool function - set WOL (Wake on LAN) settings.
61  * Only for magic packet detection mode.
62  */
bcmgenet_set_wol(struct net_device * dev,struct ethtool_wolinfo * wol)63 int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
64 {
65 	struct bcmgenet_priv *priv = netdev_priv(dev);
66 	struct device *kdev = &priv->pdev->dev;
67 
68 	if (!device_can_wakeup(kdev))
69 		return -ENOTSUPP;
70 
71 	if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
72 		return -EINVAL;
73 
74 	if (wol->wolopts & WAKE_MAGICSECURE)
75 		memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
76 
77 	/* Flag the device and relevant IRQ as wakeup capable */
78 	if (wol->wolopts) {
79 		device_set_wakeup_enable(kdev, 1);
80 		/* Avoid unbalanced enable_irq_wake calls */
81 		if (priv->wol_irq_disabled)
82 			enable_irq_wake(priv->wol_irq);
83 		priv->wol_irq_disabled = false;
84 	} else {
85 		device_set_wakeup_enable(kdev, 0);
86 		/* Avoid unbalanced disable_irq_wake calls */
87 		if (!priv->wol_irq_disabled)
88 			disable_irq_wake(priv->wol_irq);
89 		priv->wol_irq_disabled = true;
90 	}
91 
92 	priv->wolopts = wol->wolopts;
93 
94 	return 0;
95 }
96 
bcmgenet_poll_wol_status(struct bcmgenet_priv * priv)97 static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
98 {
99 	struct net_device *dev = priv->dev;
100 	int retries = 0;
101 
102 	while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
103 		& RBUF_STATUS_WOL)) {
104 		retries++;
105 		if (retries > 5) {
106 			netdev_crit(dev, "polling wol mode timeout\n");
107 			return -ETIMEDOUT;
108 		}
109 		mdelay(1);
110 	}
111 
112 	return retries;
113 }
114 
bcmgenet_set_mpd_password(struct bcmgenet_priv * priv)115 static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
116 {
117 	bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
118 			     UMAC_MPD_PW_MS);
119 	bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
120 			     UMAC_MPD_PW_LS);
121 }
122 
bcmgenet_wol_power_down_cfg(struct bcmgenet_priv * priv,enum bcmgenet_power_mode mode)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 	struct bcmgenet_rxnfc_rule *rule;
128 	u32 reg, hfb_ctrl_reg, hfb_enable = 0;
129 	int retries = 0;
130 
131 	if (mode != GENET_POWER_WOL_MAGIC) {
132 		netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
133 		return -EINVAL;
134 	}
135 
136 	/* Can't suspend with WoL if MAC is still in reset */
137 	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
138 	if (reg & CMD_SW_RESET)
139 		reg &= ~CMD_SW_RESET;
140 
141 	/* disable RX */
142 	reg &= ~CMD_RX_EN;
143 	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
144 	mdelay(10);
145 
146 	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
147 		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
148 		reg |= MPD_EN;
149 		if (priv->wolopts & WAKE_MAGICSECURE) {
150 			bcmgenet_set_mpd_password(priv);
151 			reg |= MPD_PW_EN;
152 		}
153 		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
154 	}
155 
156 	hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
157 	if (priv->wolopts & WAKE_FILTER) {
158 		list_for_each_entry(rule, &priv->rxnfc_list, list)
159 			if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
160 				hfb_enable |= (1 << rule->fs.location);
161 		reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
162 		bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
163 	}
164 
165 	/* Do not leave UniMAC in MPD mode only */
166 	retries = bcmgenet_poll_wol_status(priv);
167 	if (retries < 0) {
168 		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
169 		reg &= ~(MPD_EN | MPD_PW_EN);
170 		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
171 		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
172 		return retries;
173 	}
174 
175 	netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
176 		  retries);
177 
178 	clk_prepare_enable(priv->clk_wol);
179 	priv->wol_active = 1;
180 
181 	if (hfb_enable) {
182 		bcmgenet_hfb_reg_writel(priv, hfb_enable,
183 					HFB_FLT_ENABLE_V3PLUS + 4);
184 		hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
185 		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
186 	}
187 
188 	/* Enable CRC forward */
189 	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
190 	priv->crc_fwd_en = 1;
191 	reg |= CMD_CRC_FWD;
192 
193 	/* Receiver must be enabled for WOL MP detection */
194 	reg |= CMD_RX_EN;
195 	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
196 
197 	reg = UMAC_IRQ_MPD_R;
198 	if (hfb_enable)
199 		reg |=  UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
200 
201 	bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
202 
203 	return 0;
204 }
205 
bcmgenet_wol_power_up_cfg(struct bcmgenet_priv * priv,enum bcmgenet_power_mode mode)206 void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
207 			       enum bcmgenet_power_mode mode)
208 {
209 	u32 reg;
210 
211 	if (mode != GENET_POWER_WOL_MAGIC) {
212 		netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
213 		return;
214 	}
215 
216 	if (!priv->wol_active)
217 		return;	/* failed to suspend so skip the rest */
218 
219 	priv->wol_active = 0;
220 	clk_disable_unprepare(priv->clk_wol);
221 	priv->crc_fwd_en = 0;
222 
223 	/* Disable Magic Packet Detection */
224 	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
225 		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
226 		if (!(reg & MPD_EN))
227 			return;	/* already reset so skip the rest */
228 		reg &= ~(MPD_EN | MPD_PW_EN);
229 		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
230 	}
231 
232 	/* Disable WAKE_FILTER Detection */
233 	if (priv->wolopts & WAKE_FILTER) {
234 		reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
235 		if (!(reg & RBUF_ACPI_EN))
236 			return;	/* already reset so skip the rest */
237 		reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
238 		bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
239 	}
240 
241 	/* Disable CRC Forward */
242 	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
243 	reg &= ~CMD_CRC_FWD;
244 	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
245 }
246