File Index
Files and directories
Sign in
FILE

bcmgenet_wol.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
  4 *
  5 * Copyright (c) 2014-2025 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	u32 phy_wolopts = 0;
 45
 46	if (dev->phydev) {
 47		phy_ethtool_get_wol(dev->phydev, wol);
 48		phy_wolopts = wol->wolopts;
 49	}
 50
 51	/* MAC is not wake-up capable, return what the PHY does */
 52	if (!device_can_wakeup(kdev))
 53		return;
 54
 55	/* Overlay MAC capabilities with that of the PHY queried before */
 56	wol->supported |= WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
 57	wol->wolopts |= priv->wolopts;
 58
 59	/* Return the PHY configured magic password */
 60	if (phy_wolopts & WAKE_MAGICSECURE)
 61		return;
 62
 63	/* Otherwise the MAC one */
 64	memset(wol->sopass, 0, sizeof(wol->sopass));
 65	if (wol->wolopts & WAKE_MAGICSECURE)
 66		memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
 67}
 68
 69/* ethtool function - set WOL (Wake on LAN) settings.
 70 * Only for magic packet detection mode.
 71 */
 72int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 73{
 74	struct bcmgenet_priv *priv = netdev_priv(dev);
 75	struct device *kdev = &priv->pdev->dev;
 76	int ret;
 77
 78	/* Try Wake-on-LAN from the PHY first */
 79	if (dev->phydev) {
 80		ret = phy_ethtool_set_wol(dev->phydev, wol);
 81		if (ret != -EOPNOTSUPP && wol->wolopts)
 82			return ret;
 83	}
 84
 85	if (!device_can_wakeup(kdev))
 86		return -ENOTSUPP;
 87
 88	if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
 89		return -EINVAL;
 90
 91	if (wol->wolopts & WAKE_MAGICSECURE)
 92		memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
 93
 94	/* Flag the device and relevant IRQ as wakeup capable */
 95	if (wol->wolopts) {
 96		device_set_wakeup_enable(kdev, 1);
 97		/* Avoid unbalanced enable_irq_wake calls */
 98		if (priv->wol_irq_disabled) {
 99			enable_irq_wake(priv->wol_irq);
100			enable_irq_wake(priv->irq0);
101		}
102		priv->wol_irq_disabled = false;
103	} else {
104		device_set_wakeup_enable(kdev, 0);
105		/* Avoid unbalanced disable_irq_wake calls */
106		if (!priv->wol_irq_disabled) {
107			disable_irq_wake(priv->wol_irq);
108			disable_irq_wake(priv->irq0);
109		}
110		priv->wol_irq_disabled = true;
111	}
112
113	priv->wolopts = wol->wolopts;
114
115	return 0;
116}
117
118static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
119{
120	struct net_device *dev = priv->dev;
121	int retries = 0;
122
123	while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
124		& RBUF_STATUS_WOL)) {
125		retries++;
126		if (retries > 50) {
127			netdev_crit(dev, "polling wol mode timeout\n");
128			return -ETIMEDOUT;
129		}
130		mdelay(1);
131	}
132
133	return retries;
134}
135
136static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
137{
138	bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
139			     UMAC_MPD_PW_MS);
140	bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
141			     UMAC_MPD_PW_LS);
142}
143
144int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
145				enum bcmgenet_power_mode mode)
146{
147	struct net_device *dev = priv->dev;
148	u32 reg, hfb_ctrl_reg;
149	int retries = 0;
150
151	if (mode != GENET_POWER_WOL_MAGIC) {
152		netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
153		return -EINVAL;
154	}
155
156	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
157		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
158		reg |= MPD_EN;
159		if (priv->wolopts & WAKE_MAGICSECURE) {
160			bcmgenet_set_mpd_password(priv);
161			reg |= MPD_PW_EN;
162		}
163		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
164	}
165
166	hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
167	reg = hfb_ctrl_reg | RBUF_ACPI_EN;
168	bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
169
170	/* Do not leave UniMAC in MPD mode only */
171	retries = bcmgenet_poll_wol_status(priv);
172	if (retries < 0) {
173		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
174		reg &= ~(MPD_EN | MPD_PW_EN);
175		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
176		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
177		return retries;
178	}
179
180	netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
181		  retries);
182
183	/* Disable phy status updates while suspending */
184	mutex_lock(&dev->phydev->lock);
185	dev->phydev->state = PHY_READY;
186	mutex_unlock(&dev->phydev->lock);
187
188	clk_prepare_enable(priv->clk_wol);
189
190	/* Enable CRC forward */
191	spin_lock_bh(&priv->reg_lock);
192	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
193	priv->crc_fwd_en = 1;
194	reg |= CMD_CRC_FWD;
195
196	/* Can't suspend with WoL if MAC is still in reset */
197	if (reg & CMD_SW_RESET)
198		reg &= ~CMD_SW_RESET;
199
200	/* Receiver must be enabled for WOL MP detection */
201	reg |= CMD_RX_EN;
202	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
203	spin_unlock_bh(&priv->reg_lock);
204
205	reg = UMAC_IRQ_MPD_R;
206	if (hfb_ctrl_reg & RBUF_HFB_EN)
207		reg |=  UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
208
209	bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
210
211	return 0;
212}
213
214int bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
215			      enum bcmgenet_power_mode mode)
216{
217	struct net_device *dev = priv->dev;
218	u32 reg;
219
220	if (mode != GENET_POWER_WOL_MAGIC) {
221		netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
222		return -EINVAL;
223	}
224
225	clk_disable_unprepare(priv->clk_wol);
226	priv->crc_fwd_en = 0;
227
228	bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_WAKE_EVENT,
229				 INTRL2_CPU_MASK_SET);
230	if (bcmgenet_has_mdio_intr(priv))
231		bcmgenet_intrl2_0_writel(priv,
232					 UMAC_IRQ_MDIO_EVENT,
233					 INTRL2_CPU_MASK_CLEAR);
234
235	/* Disable Magic Packet Detection */
236	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
237		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
238		if (!(reg & MPD_EN))
239			return -EPERM;	/* already reset so skip the rest */
240		reg &= ~(MPD_EN | MPD_PW_EN);
241		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
242	}
243
244	/* Disable ACPI mode */
245	reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
246	if (!(reg & RBUF_ACPI_EN))
247		return -EPERM;	/* already reset so skip the rest */
248	reg &= ~RBUF_ACPI_EN;
249	bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
250
251	/* Disable CRC Forward */
252	spin_lock_bh(&priv->reg_lock);
253	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
254	reg &= ~CMD_CRC_FWD;
255	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
256	spin_unlock_bh(&priv->reg_lock);
257
258	/* Resume link status tracking */
259	mutex_lock(&dev->phydev->lock);
260	if (dev->phydev->link)
261		dev->phydev->state = PHY_RUNNING;
262	else
263		dev->phydev->state = PHY_NOLINK;
264	mutex_unlock(&dev->phydev->lock);
265
266	return 0;
267}
~/ Read-only file preview.