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 */
41void 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 */
63int 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
97static 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
115static 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
123int 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
206void 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