Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Mar 24-27, 2025, special US time zones
Register
Loading...
v6.8
  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/platform_device.h>
 22#include <net/arp.h>
 23
 24#include <linux/mii.h>
 25#include <linux/ethtool.h>
 26#include <linux/netdevice.h>
 27#include <linux/inetdevice.h>
 28#include <linux/etherdevice.h>
 29#include <linux/skbuff.h>
 30#include <linux/in.h>
 31#include <linux/ip.h>
 32#include <linux/ipv6.h>
 33#include <linux/phy.h>
 34
 35#include "bcmgenet.h"
 36
 37/* ethtool function - get WOL (Wake on LAN) settings, Only Magic Packet
 38 * Detection is supported through ethtool
 39 */
 40void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 41{
 42	struct bcmgenet_priv *priv = netdev_priv(dev);
 43	struct device *kdev = &priv->pdev->dev;
 44
 45	if (dev->phydev) {
 46		phy_ethtool_get_wol(dev->phydev, wol);
 47		if (wol->supported)
 48			return;
 49	}
 50
 51	if (!device_can_wakeup(kdev)) {
 52		wol->supported = 0;
 53		wol->wolopts = 0;
 54		return;
 55	}
 56
 57	wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
 58	wol->wolopts = priv->wolopts;
 59	memset(wol->sopass, 0, sizeof(wol->sopass));
 60
 61	if (wol->wolopts & WAKE_MAGICSECURE)
 62		memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
 63}
 64
 65/* ethtool function - set WOL (Wake on LAN) settings.
 66 * Only for magic packet detection mode.
 67 */
 68int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 69{
 70	struct bcmgenet_priv *priv = netdev_priv(dev);
 71	struct device *kdev = &priv->pdev->dev;
 72	int ret;
 73
 74	/* Try Wake-on-LAN from the PHY first */
 75	if (dev->phydev) {
 76		ret = phy_ethtool_set_wol(dev->phydev, wol);
 77		if (ret != -EOPNOTSUPP)
 78			return ret;
 79	}
 80
 81	if (!device_can_wakeup(kdev))
 82		return -ENOTSUPP;
 83
 84	if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
 85		return -EINVAL;
 86
 87	if (wol->wolopts & WAKE_MAGICSECURE)
 88		memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
 89
 90	/* Flag the device and relevant IRQ as wakeup capable */
 91	if (wol->wolopts) {
 92		device_set_wakeup_enable(kdev, 1);
 93		/* Avoid unbalanced enable_irq_wake calls */
 94		if (priv->wol_irq_disabled) {
 95			enable_irq_wake(priv->wol_irq);
 96			enable_irq_wake(priv->irq0);
 97		}
 98		priv->wol_irq_disabled = false;
 99	} else {
100		device_set_wakeup_enable(kdev, 0);
101		/* Avoid unbalanced disable_irq_wake calls */
102		if (!priv->wol_irq_disabled) {
103			disable_irq_wake(priv->wol_irq);
104			disable_irq_wake(priv->irq0);
105		}
106		priv->wol_irq_disabled = true;
107	}
108
109	priv->wolopts = wol->wolopts;
110
111	return 0;
112}
113
114static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
115{
116	struct net_device *dev = priv->dev;
117	int retries = 0;
118
119	while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
120		& RBUF_STATUS_WOL)) {
121		retries++;
122		if (retries > 5) {
123			netdev_crit(dev, "polling wol mode timeout\n");
124			return -ETIMEDOUT;
125		}
126		mdelay(1);
127	}
128
129	return retries;
130}
131
132static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
133{
134	bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
135			     UMAC_MPD_PW_MS);
136	bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
137			     UMAC_MPD_PW_LS);
138}
139
140int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
141				enum bcmgenet_power_mode mode)
142{
143	struct net_device *dev = priv->dev;
144	struct bcmgenet_rxnfc_rule *rule;
145	u32 reg, hfb_ctrl_reg, hfb_enable = 0;
146	int retries = 0;
147
148	if (mode != GENET_POWER_WOL_MAGIC) {
149		netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
150		return -EINVAL;
151	}
152
153	/* Can't suspend with WoL if MAC is still in reset */
154	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
155	if (reg & CMD_SW_RESET)
156		reg &= ~CMD_SW_RESET;
157
158	/* disable RX */
159	reg &= ~CMD_RX_EN;
160	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
161	mdelay(10);
162
163	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
164		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
165		reg |= MPD_EN;
166		if (priv->wolopts & WAKE_MAGICSECURE) {
167			bcmgenet_set_mpd_password(priv);
168			reg |= MPD_PW_EN;
169		}
170		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
171	}
172
173	hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
174	if (priv->wolopts & WAKE_FILTER) {
175		list_for_each_entry(rule, &priv->rxnfc_list, list)
176			if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
177				hfb_enable |= (1 << rule->fs.location);
178		reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
179		bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
180	}
181
182	/* Do not leave UniMAC in MPD mode only */
183	retries = bcmgenet_poll_wol_status(priv);
184	if (retries < 0) {
185		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
186		reg &= ~(MPD_EN | MPD_PW_EN);
187		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
188		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
189		return retries;
190	}
191
192	netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
193		  retries);
194
195	clk_prepare_enable(priv->clk_wol);
196	priv->wol_active = 1;
197
198	if (hfb_enable) {
199		bcmgenet_hfb_reg_writel(priv, hfb_enable,
200					HFB_FLT_ENABLE_V3PLUS + 4);
201		hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
202		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
203	}
204
205	/* Enable CRC forward */
206	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
207	priv->crc_fwd_en = 1;
208	reg |= CMD_CRC_FWD;
209
210	/* Receiver must be enabled for WOL MP detection */
211	reg |= CMD_RX_EN;
212	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
 
 
 
 
 
 
213
214	reg = UMAC_IRQ_MPD_R;
215	if (hfb_enable)
216		reg |=  UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
217
218	bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
219
220	return 0;
221}
222
223void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
224			       enum bcmgenet_power_mode mode)
225{
226	u32 reg;
227
228	if (mode != GENET_POWER_WOL_MAGIC) {
229		netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
230		return;
231	}
232
233	if (!priv->wol_active)
234		return;	/* failed to suspend so skip the rest */
235
236	priv->wol_active = 0;
237	clk_disable_unprepare(priv->clk_wol);
238	priv->crc_fwd_en = 0;
239
240	/* Disable Magic Packet Detection */
241	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
242		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
243		if (!(reg & MPD_EN))
244			return;	/* already reset so skip the rest */
245		reg &= ~(MPD_EN | MPD_PW_EN);
246		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
247	}
248
249	/* Disable WAKE_FILTER Detection */
250	if (priv->wolopts & WAKE_FILTER) {
251		reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
252		if (!(reg & RBUF_ACPI_EN))
253			return;	/* already reset so skip the rest */
254		reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
255		bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
256	}
257
258	/* Disable CRC Forward */
259	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
260	reg &= ~CMD_CRC_FWD;
261	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
262}
v5.9
  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
 45	wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
 46	wol->wolopts = priv->wolopts;
 47	memset(wol->sopass, 0, sizeof(wol->sopass));
 48
 49	if (wol->wolopts & WAKE_MAGICSECURE)
 50		memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
 51}
 52
 53/* ethtool function - set WOL (Wake on LAN) settings.
 54 * Only for magic packet detection mode.
 55 */
 56int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 57{
 58	struct bcmgenet_priv *priv = netdev_priv(dev);
 59	struct device *kdev = &priv->pdev->dev;
 
 
 
 
 
 
 
 
 60
 61	if (!device_can_wakeup(kdev))
 62		return -ENOTSUPP;
 63
 64	if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
 65		return -EINVAL;
 66
 67	if (wol->wolopts & WAKE_MAGICSECURE)
 68		memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
 69
 70	/* Flag the device and relevant IRQ as wakeup capable */
 71	if (wol->wolopts) {
 72		device_set_wakeup_enable(kdev, 1);
 73		/* Avoid unbalanced enable_irq_wake calls */
 74		if (priv->wol_irq_disabled)
 75			enable_irq_wake(priv->wol_irq);
 
 
 76		priv->wol_irq_disabled = false;
 77	} else {
 78		device_set_wakeup_enable(kdev, 0);
 79		/* Avoid unbalanced disable_irq_wake calls */
 80		if (!priv->wol_irq_disabled)
 81			disable_irq_wake(priv->wol_irq);
 
 
 82		priv->wol_irq_disabled = true;
 83	}
 84
 85	priv->wolopts = wol->wolopts;
 86
 87	return 0;
 88}
 89
 90static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
 91{
 92	struct net_device *dev = priv->dev;
 93	int retries = 0;
 94
 95	while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
 96		& RBUF_STATUS_WOL)) {
 97		retries++;
 98		if (retries > 5) {
 99			netdev_crit(dev, "polling wol mode timeout\n");
100			return -ETIMEDOUT;
101		}
102		mdelay(1);
103	}
104
105	return retries;
106}
107
108static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
109{
110	bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
111			     UMAC_MPD_PW_MS);
112	bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
113			     UMAC_MPD_PW_LS);
114}
115
116int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
117				enum bcmgenet_power_mode mode)
118{
119	struct net_device *dev = priv->dev;
120	struct bcmgenet_rxnfc_rule *rule;
121	u32 reg, hfb_ctrl_reg, hfb_enable = 0;
122	int retries = 0;
123
124	if (mode != GENET_POWER_WOL_MAGIC) {
125		netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
126		return -EINVAL;
127	}
128
129	/* Can't suspend with WoL if MAC is still in reset */
130	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
131	if (reg & CMD_SW_RESET)
132		reg &= ~CMD_SW_RESET;
133
134	/* disable RX */
135	reg &= ~CMD_RX_EN;
136	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
137	mdelay(10);
138
139	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
140		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
141		reg |= MPD_EN;
142		if (priv->wolopts & WAKE_MAGICSECURE) {
143			bcmgenet_set_mpd_password(priv);
144			reg |= MPD_PW_EN;
145		}
146		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
147	}
148
149	hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
150	if (priv->wolopts & WAKE_FILTER) {
151		list_for_each_entry(rule, &priv->rxnfc_list, list)
152			if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
153				hfb_enable |= (1 << rule->fs.location);
154		reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
155		bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
156	}
157
158	/* Do not leave UniMAC in MPD mode only */
159	retries = bcmgenet_poll_wol_status(priv);
160	if (retries < 0) {
161		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
162		reg &= ~(MPD_EN | MPD_PW_EN);
163		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
164		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
165		return retries;
166	}
167
168	netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
169		  retries);
170
171	clk_prepare_enable(priv->clk_wol);
172	priv->wol_active = 1;
173
174	if (hfb_enable) {
175		bcmgenet_hfb_reg_writel(priv, hfb_enable,
176					HFB_FLT_ENABLE_V3PLUS + 4);
177		hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
178		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
179	}
180
181	/* Enable CRC forward */
182	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
183	priv->crc_fwd_en = 1;
184	reg |= CMD_CRC_FWD;
185
186	/* Receiver must be enabled for WOL MP detection */
187	reg |= CMD_RX_EN;
188	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
189
190	if (priv->hw_params->flags & GENET_HAS_EXT) {
191		reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
192		reg &= ~EXT_ENERGY_DET_MASK;
193		bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
194	}
195
196	reg = UMAC_IRQ_MPD_R;
197	if (hfb_enable)
198		reg |=  UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
199
200	bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
201
202	return 0;
203}
204
205void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
206			       enum bcmgenet_power_mode mode)
207{
208	u32 reg;
209
210	if (mode != GENET_POWER_WOL_MAGIC) {
211		netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
212		return;
213	}
214
215	if (!priv->wol_active)
216		return;	/* failed to suspend so skip the rest */
217
218	priv->wol_active = 0;
219	clk_disable_unprepare(priv->clk_wol);
220	priv->crc_fwd_en = 0;
221
222	/* Disable Magic Packet Detection */
223	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
224		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
225		if (!(reg & MPD_EN))
226			return;	/* already reset so skip the rest */
227		reg &= ~(MPD_EN | MPD_PW_EN);
228		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
229	}
230
231	/* Disable WAKE_FILTER Detection */
232	if (priv->wolopts & WAKE_FILTER) {
233		reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
234		if (!(reg & RBUF_ACPI_EN))
235			return;	/* already reset so skip the rest */
236		reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
237		bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
238	}
239
240	/* Disable CRC Forward */
241	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
242	reg &= ~CMD_CRC_FWD;
243	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
244}