brintos

brintos / linux-shallow public Read only

0
0
Text · 6.8 KiB · 0715ea5 Raw
265 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support4 *5 * Copyright (c) 2014-2024 Broadcom6 */7 8#define pr_fmt(fmt)				"bcmgenet_wol: " fmt9 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 Packet38 * Detection is supported through ethtool39 */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 48	/* MAC is not wake-up capable, return what the PHY does */49	if (!device_can_wakeup(kdev))50		return;51 52	/* Overlay MAC capabilities with that of the PHY queried before */53	wol->supported |= WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;54	wol->wolopts = priv->wolopts;55	memset(wol->sopass, 0, sizeof(wol->sopass));56 57	if (wol->wolopts & WAKE_MAGICSECURE)58		memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));59}60 61/* ethtool function - set WOL (Wake on LAN) settings.62 * Only for magic packet detection mode.63 */64int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)65{66	struct bcmgenet_priv *priv = netdev_priv(dev);67	struct device *kdev = &priv->pdev->dev;68	int ret;69 70	/* Try Wake-on-LAN from the PHY first */71	if (dev->phydev) {72		ret = phy_ethtool_set_wol(dev->phydev, wol);73		if (ret != -EOPNOTSUPP)74			return ret;75	}76 77	if (!device_can_wakeup(kdev))78		return -ENOTSUPP;79 80	if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))81		return -EINVAL;82 83	if (wol->wolopts & WAKE_MAGICSECURE)84		memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));85 86	/* Flag the device and relevant IRQ as wakeup capable */87	if (wol->wolopts) {88		device_set_wakeup_enable(kdev, 1);89		/* Avoid unbalanced enable_irq_wake calls */90		if (priv->wol_irq_disabled) {91			enable_irq_wake(priv->wol_irq);92			enable_irq_wake(priv->irq0);93		}94		priv->wol_irq_disabled = false;95	} else {96		device_set_wakeup_enable(kdev, 0);97		/* Avoid unbalanced disable_irq_wake calls */98		if (!priv->wol_irq_disabled) {99			disable_irq_wake(priv->wol_irq);100			disable_irq_wake(priv->irq0);101		}102		priv->wol_irq_disabled = true;103	}104 105	priv->wolopts = wol->wolopts;106 107	return 0;108}109 110static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)111{112	struct net_device *dev = priv->dev;113	int retries = 0;114 115	while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)116		& RBUF_STATUS_WOL)) {117		retries++;118		if (retries > 5) {119			netdev_crit(dev, "polling wol mode timeout\n");120			return -ETIMEDOUT;121		}122		mdelay(1);123	}124 125	return retries;126}127 128static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)129{130	bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),131			     UMAC_MPD_PW_MS);132	bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),133			     UMAC_MPD_PW_LS);134}135 136int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,137				enum bcmgenet_power_mode mode)138{139	struct net_device *dev = priv->dev;140	struct bcmgenet_rxnfc_rule *rule;141	u32 reg, hfb_ctrl_reg, hfb_enable = 0;142	int retries = 0;143 144	if (mode != GENET_POWER_WOL_MAGIC) {145		netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);146		return -EINVAL;147	}148 149	/* Can't suspend with WoL if MAC is still in reset */150	spin_lock_bh(&priv->reg_lock);151	reg = bcmgenet_umac_readl(priv, UMAC_CMD);152	if (reg & CMD_SW_RESET)153		reg &= ~CMD_SW_RESET;154 155	/* disable RX */156	reg &= ~CMD_RX_EN;157	bcmgenet_umac_writel(priv, reg, UMAC_CMD);158	spin_unlock_bh(&priv->reg_lock);159	mdelay(10);160 161	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {162		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);163		reg |= MPD_EN;164		if (priv->wolopts & WAKE_MAGICSECURE) {165			bcmgenet_set_mpd_password(priv);166			reg |= MPD_PW_EN;167		}168		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);169	}170 171	hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);172	if (priv->wolopts & WAKE_FILTER) {173		list_for_each_entry(rule, &priv->rxnfc_list, list)174			if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)175				hfb_enable |= (1 << rule->fs.location);176		reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;177		bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);178	}179 180	/* Do not leave UniMAC in MPD mode only */181	retries = bcmgenet_poll_wol_status(priv);182	if (retries < 0) {183		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);184		reg &= ~(MPD_EN | MPD_PW_EN);185		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);186		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);187		return retries;188	}189 190	netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",191		  retries);192 193	clk_prepare_enable(priv->clk_wol);194	priv->wol_active = 1;195 196	if (hfb_enable) {197		bcmgenet_hfb_reg_writel(priv, hfb_enable,198					HFB_FLT_ENABLE_V3PLUS + 4);199		hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;200		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);201	}202 203	/* Enable CRC forward */204	spin_lock_bh(&priv->reg_lock);205	reg = bcmgenet_umac_readl(priv, UMAC_CMD);206	priv->crc_fwd_en = 1;207	reg |= CMD_CRC_FWD;208 209	/* Receiver must be enabled for WOL MP detection */210	reg |= CMD_RX_EN;211	bcmgenet_umac_writel(priv, reg, UMAC_CMD);212	spin_unlock_bh(&priv->reg_lock);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	spin_lock_bh(&priv->reg_lock);260	reg = bcmgenet_umac_readl(priv, UMAC_CMD);261	reg &= ~CMD_CRC_FWD;262	bcmgenet_umac_writel(priv, reg, UMAC_CMD);263	spin_unlock_bh(&priv->reg_lock);264}265