cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bcmgenet_wol.c (6422B)


      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 (!device_can_wakeup(kdev)) {
     46		wol->supported = 0;
     47		wol->wolopts = 0;
     48		return;
     49	}
     50
     51	wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
     52	wol->wolopts = priv->wolopts;
     53	memset(wol->sopass, 0, sizeof(wol->sopass));
     54
     55	if (wol->wolopts & WAKE_MAGICSECURE)
     56		memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
     57}
     58
     59/* ethtool function - set WOL (Wake on LAN) settings.
     60 * Only for magic packet detection mode.
     61 */
     62int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
     63{
     64	struct bcmgenet_priv *priv = netdev_priv(dev);
     65	struct device *kdev = &priv->pdev->dev;
     66
     67	if (!device_can_wakeup(kdev))
     68		return -ENOTSUPP;
     69
     70	if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
     71		return -EINVAL;
     72
     73	if (wol->wolopts & WAKE_MAGICSECURE)
     74		memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
     75
     76	/* Flag the device and relevant IRQ as wakeup capable */
     77	if (wol->wolopts) {
     78		device_set_wakeup_enable(kdev, 1);
     79		/* Avoid unbalanced enable_irq_wake calls */
     80		if (priv->wol_irq_disabled)
     81			enable_irq_wake(priv->wol_irq);
     82		priv->wol_irq_disabled = false;
     83	} else {
     84		device_set_wakeup_enable(kdev, 0);
     85		/* Avoid unbalanced disable_irq_wake calls */
     86		if (!priv->wol_irq_disabled)
     87			disable_irq_wake(priv->wol_irq);
     88		priv->wol_irq_disabled = true;
     89	}
     90
     91	priv->wolopts = wol->wolopts;
     92
     93	return 0;
     94}
     95
     96static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
     97{
     98	struct net_device *dev = priv->dev;
     99	int retries = 0;
    100
    101	while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
    102		& RBUF_STATUS_WOL)) {
    103		retries++;
    104		if (retries > 5) {
    105			netdev_crit(dev, "polling wol mode timeout\n");
    106			return -ETIMEDOUT;
    107		}
    108		mdelay(1);
    109	}
    110
    111	return retries;
    112}
    113
    114static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
    115{
    116	bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
    117			     UMAC_MPD_PW_MS);
    118	bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
    119			     UMAC_MPD_PW_LS);
    120}
    121
    122int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
    123				enum bcmgenet_power_mode mode)
    124{
    125	struct net_device *dev = priv->dev;
    126	struct bcmgenet_rxnfc_rule *rule;
    127	u32 reg, hfb_ctrl_reg, hfb_enable = 0;
    128	int retries = 0;
    129
    130	if (mode != GENET_POWER_WOL_MAGIC) {
    131		netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
    132		return -EINVAL;
    133	}
    134
    135	/* Can't suspend with WoL if MAC is still in reset */
    136	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
    137	if (reg & CMD_SW_RESET)
    138		reg &= ~CMD_SW_RESET;
    139
    140	/* disable RX */
    141	reg &= ~CMD_RX_EN;
    142	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
    143	mdelay(10);
    144
    145	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
    146		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
    147		reg |= MPD_EN;
    148		if (priv->wolopts & WAKE_MAGICSECURE) {
    149			bcmgenet_set_mpd_password(priv);
    150			reg |= MPD_PW_EN;
    151		}
    152		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
    153	}
    154
    155	hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
    156	if (priv->wolopts & WAKE_FILTER) {
    157		list_for_each_entry(rule, &priv->rxnfc_list, list)
    158			if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
    159				hfb_enable |= (1 << rule->fs.location);
    160		reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
    161		bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
    162	}
    163
    164	/* Do not leave UniMAC in MPD mode only */
    165	retries = bcmgenet_poll_wol_status(priv);
    166	if (retries < 0) {
    167		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
    168		reg &= ~(MPD_EN | MPD_PW_EN);
    169		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
    170		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
    171		return retries;
    172	}
    173
    174	netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
    175		  retries);
    176
    177	clk_prepare_enable(priv->clk_wol);
    178	priv->wol_active = 1;
    179
    180	if (hfb_enable) {
    181		bcmgenet_hfb_reg_writel(priv, hfb_enable,
    182					HFB_FLT_ENABLE_V3PLUS + 4);
    183		hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
    184		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
    185	}
    186
    187	/* Enable CRC forward */
    188	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
    189	priv->crc_fwd_en = 1;
    190	reg |= CMD_CRC_FWD;
    191
    192	/* Receiver must be enabled for WOL MP detection */
    193	reg |= CMD_RX_EN;
    194	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
    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}