linux/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c
<<
>>
Prefs
   1/*
   2 * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
   3 *
   4 * Copyright (c) 2014 Broadcom Corporation
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#define pr_fmt(fmt)                             "bcmgenet_wol: " fmt
  12
  13#include <linux/kernel.h>
  14#include <linux/module.h>
  15#include <linux/sched.h>
  16#include <linux/types.h>
  17#include <linux/interrupt.h>
  18#include <linux/string.h>
  19#include <linux/init.h>
  20#include <linux/errno.h>
  21#include <linux/delay.h>
  22#include <linux/pm.h>
  23#include <linux/clk.h>
  24#include <linux/version.h>
  25#include <linux/platform_device.h>
  26#include <net/arp.h>
  27
  28#include <linux/mii.h>
  29#include <linux/ethtool.h>
  30#include <linux/netdevice.h>
  31#include <linux/inetdevice.h>
  32#include <linux/etherdevice.h>
  33#include <linux/skbuff.h>
  34#include <linux/in.h>
  35#include <linux/ip.h>
  36#include <linux/ipv6.h>
  37#include <linux/phy.h>
  38
  39#include "bcmgenet.h"
  40
  41/* ethtool function - get WOL (Wake on LAN) settings, Only Magic Packet
  42 * Detection is supported through ethtool
  43 */
  44void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
  45{
  46        struct bcmgenet_priv *priv = netdev_priv(dev);
  47        u32 reg;
  48
  49        wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE;
  50        wol->wolopts = priv->wolopts;
  51        memset(wol->sopass, 0, sizeof(wol->sopass));
  52
  53        if (wol->wolopts & WAKE_MAGICSECURE) {
  54                reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_MS);
  55                put_unaligned_be16(reg, &wol->sopass[0]);
  56                reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_LS);
  57                put_unaligned_be32(reg, &wol->sopass[2]);
  58        }
  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        u32 reg;
  69
  70        if (!device_can_wakeup(kdev))
  71                return -ENOTSUPP;
  72
  73        if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE))
  74                return -EINVAL;
  75
  76        reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
  77        if (wol->wolopts & WAKE_MAGICSECURE) {
  78                bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]),
  79                                     UMAC_MPD_PW_MS);
  80                bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]),
  81                                     UMAC_MPD_PW_LS);
  82                reg |= MPD_PW_EN;
  83        } else {
  84                reg &= ~MPD_PW_EN;
  85        }
  86        bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
  87
  88        /* Flag the device and relevant IRQ as wakeup capable */
  89        if (wol->wolopts) {
  90                device_set_wakeup_enable(kdev, 1);
  91                /* Avoid unbalanced enable_irq_wake calls */
  92                if (priv->wol_irq_disabled)
  93                        enable_irq_wake(priv->wol_irq);
  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                priv->wol_irq_disabled = true;
 101        }
 102
 103        priv->wolopts = wol->wolopts;
 104
 105        return 0;
 106}
 107
 108static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
 109{
 110        struct net_device *dev = priv->dev;
 111        int retries = 0;
 112
 113        while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
 114                & RBUF_STATUS_WOL)) {
 115                retries++;
 116                if (retries > 5) {
 117                        netdev_crit(dev, "polling wol mode timeout\n");
 118                        return -ETIMEDOUT;
 119                }
 120                mdelay(1);
 121        }
 122
 123        return retries;
 124}
 125
 126int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
 127                                enum bcmgenet_power_mode mode)
 128{
 129        struct net_device *dev = priv->dev;
 130        u32 cpu_mask_clear;
 131        int retries = 0;
 132        u32 reg;
 133
 134        if (mode != GENET_POWER_WOL_MAGIC) {
 135                netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
 136                return -EINVAL;
 137        }
 138
 139        /* disable RX */
 140        reg = bcmgenet_umac_readl(priv, UMAC_CMD);
 141        reg &= ~CMD_RX_EN;
 142        bcmgenet_umac_writel(priv, reg, UMAC_CMD);
 143        mdelay(10);
 144
 145        reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
 146        reg |= MPD_EN;
 147        bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
 148
 149        /* Do not leave UniMAC in MPD mode only */
 150        retries = bcmgenet_poll_wol_status(priv);
 151        if (retries < 0) {
 152                reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
 153                reg &= ~MPD_EN;
 154                bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
 155                return retries;
 156        }
 157
 158        netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
 159                  retries);
 160
 161        /* Enable CRC forward */
 162        reg = bcmgenet_umac_readl(priv, UMAC_CMD);
 163        priv->crc_fwd_en = 1;
 164        reg |= CMD_CRC_FWD;
 165
 166        /* Receiver must be enabled for WOL MP detection */
 167        reg |= CMD_RX_EN;
 168        bcmgenet_umac_writel(priv, reg, UMAC_CMD);
 169
 170        if (priv->hw_params->flags & GENET_HAS_EXT) {
 171                reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
 172                reg &= ~EXT_ENERGY_DET_MASK;
 173                bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
 174        }
 175
 176        /* Enable the MPD interrupt */
 177        cpu_mask_clear = UMAC_IRQ_MPD_R;
 178
 179        bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR);
 180
 181        return 0;
 182}
 183
 184void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
 185                               enum bcmgenet_power_mode mode)
 186{
 187        u32 cpu_mask_set;
 188        u32 reg;
 189
 190        if (mode != GENET_POWER_WOL_MAGIC) {
 191                netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
 192                return;
 193        }
 194
 195        reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
 196        reg &= ~MPD_EN;
 197        bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
 198
 199        /* Disable CRC Forward */
 200        reg = bcmgenet_umac_readl(priv, UMAC_CMD);
 201        reg &= ~CMD_CRC_FWD;
 202        bcmgenet_umac_writel(priv, reg, UMAC_CMD);
 203        priv->crc_fwd_en = 0;
 204
 205        /* Stop monitoring magic packet IRQ */
 206        cpu_mask_set = UMAC_IRQ_MPD_R;
 207
 208        /* Stop monitoring magic packet IRQ */
 209        bcmgenet_intrl2_0_writel(priv, cpu_mask_set, INTRL2_CPU_MASK_SET);
 210}
 211