aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/broadcom/genet/Makefile2
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.h9
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c206
3 files changed, 216 insertions, 1 deletions
diff --git a/drivers/net/ethernet/broadcom/genet/Makefile b/drivers/net/ethernet/broadcom/genet/Makefile
index 31f55a90a197..9b6885efa9e7 100644
--- a/drivers/net/ethernet/broadcom/genet/Makefile
+++ b/drivers/net/ethernet/broadcom/genet/Makefile
@@ -1,2 +1,2 @@
1obj-$(CONFIG_BCMGENET) += genet.o 1obj-$(CONFIG_BCMGENET) += genet.o
2genet-objs := bcmgenet.o bcmmii.o 2genet-objs := bcmgenet.o bcmmii.o bcmgenet_wol.o
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
index 64bc53d86480..c61cd98b662e 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
@@ -456,6 +456,7 @@ struct enet_cb {
456enum bcmgenet_power_mode { 456enum bcmgenet_power_mode {
457 GENET_POWER_CABLE_SENSE = 0, 457 GENET_POWER_CABLE_SENSE = 0,
458 GENET_POWER_PASSIVE, 458 GENET_POWER_PASSIVE,
459 GENET_POWER_WOL_MAGIC,
459}; 460};
460 461
461struct bcmgenet_priv; 462struct bcmgenet_priv;
@@ -626,4 +627,12 @@ int bcmgenet_mii_config(struct net_device *dev);
626void bcmgenet_mii_exit(struct net_device *dev); 627void bcmgenet_mii_exit(struct net_device *dev);
627void bcmgenet_mii_reset(struct net_device *dev); 628void bcmgenet_mii_reset(struct net_device *dev);
628 629
630/* Wake-on-LAN routines */
631void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
632int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
633int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
634 enum bcmgenet_power_mode mode);
635void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
636 enum bcmgenet_power_mode mode);
637
629#endif /* __BCMGENET_H__ */ 638#endif /* __BCMGENET_H__ */
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c
new file mode 100644
index 000000000000..b82b7e4e06b2
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c
@@ -0,0 +1,206 @@
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 if (wol->wolopts & WAKE_MAGICSECURE) {
77 bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]),
78 UMAC_MPD_PW_MS);
79 bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]),
80 UMAC_MPD_PW_LS);
81 reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
82 reg |= MPD_PW_EN;
83 bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
84 }
85
86 /* Flag the device and relevant IRQ as wakeup capable */
87 if (wol->wolopts) {
88 device_set_wakeup_enable(kdev, 1);
89 enable_irq_wake(priv->wol_irq);
90 priv->wol_irq_disabled = false;
91 } else {
92 device_set_wakeup_enable(kdev, 0);
93 /* Avoid unbalanced disable_irq_wake calls */
94 if (!priv->wol_irq_disabled)
95 disable_irq_wake(priv->wol_irq);
96 priv->wol_irq_disabled = true;
97 }
98
99 priv->wolopts = wol->wolopts;
100
101 return 0;
102}
103
104static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
105{
106 struct net_device *dev = priv->dev;
107 int retries = 0;
108
109 while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
110 & RBUF_STATUS_WOL)) {
111 retries++;
112 if (retries > 5) {
113 netdev_crit(dev, "polling wol mode timeout\n");
114 return -ETIMEDOUT;
115 }
116 mdelay(1);
117 }
118
119 return retries;
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 u32 cpu_mask_clear;
127 int retries = 0;
128 u32 reg;
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 /* disable RX */
136 reg = bcmgenet_umac_readl(priv, UMAC_CMD);
137 reg &= ~CMD_RX_EN;
138 bcmgenet_umac_writel(priv, reg, UMAC_CMD);
139 mdelay(10);
140
141 reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
142 reg |= MPD_EN;
143 bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
144
145 /* Do not leave UniMAC in MPD mode only */
146 retries = bcmgenet_poll_wol_status(priv);
147 if (retries < 0) {
148 reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
149 reg &= ~MPD_EN;
150 bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
151 return retries;
152 }
153
154 netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
155 retries);
156
157 /* Enable CRC forward */
158 reg = bcmgenet_umac_readl(priv, UMAC_CMD);
159 priv->crc_fwd_en = 1;
160 reg |= CMD_CRC_FWD;
161
162 /* Receiver must be enabled for WOL MP detection */
163 reg |= CMD_RX_EN;
164 bcmgenet_umac_writel(priv, reg, UMAC_CMD);
165
166 if (priv->hw_params->flags & GENET_HAS_EXT) {
167 reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
168 reg &= ~EXT_ENERGY_DET_MASK;
169 bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
170 }
171
172 /* Enable the MPD interrupt */
173 cpu_mask_clear = UMAC_IRQ_MPD_R;
174
175 bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR);
176
177 return 0;
178}
179
180void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
181 enum bcmgenet_power_mode mode)
182{
183 u32 cpu_mask_set;
184 u32 reg;
185
186 if (mode != GENET_POWER_WOL_MAGIC) {
187 netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
188 return;
189 }
190
191 reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
192 reg &= ~MPD_EN;
193 bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
194
195 /* Disable CRC Forward */
196 reg = bcmgenet_umac_readl(priv, UMAC_CMD);
197 reg &= ~CMD_CRC_FWD;
198 bcmgenet_umac_writel(priv, reg, UMAC_CMD);
199 priv->crc_fwd_en = 0;
200
201 /* Stop monitoring magic packet IRQ */
202 cpu_mask_set = UMAC_IRQ_MPD_R;
203
204 /* Stop monitoring magic packet IRQ */
205 bcmgenet_intrl2_0_writel(priv, cpu_mask_set, INTRL2_CPU_MASK_SET);
206}