diff options
-rw-r--r-- | drivers/net/ethernet/broadcom/genet/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/genet/bcmgenet.h | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c | 206 |
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 @@ | |||
1 | obj-$(CONFIG_BCMGENET) += genet.o | 1 | obj-$(CONFIG_BCMGENET) += genet.o |
2 | genet-objs := bcmgenet.o bcmmii.o | 2 | genet-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 { | |||
456 | enum bcmgenet_power_mode { | 456 | enum 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 | ||
461 | struct bcmgenet_priv; | 462 | struct bcmgenet_priv; |
@@ -626,4 +627,12 @@ int bcmgenet_mii_config(struct net_device *dev); | |||
626 | void bcmgenet_mii_exit(struct net_device *dev); | 627 | void bcmgenet_mii_exit(struct net_device *dev); |
627 | void bcmgenet_mii_reset(struct net_device *dev); | 628 | void bcmgenet_mii_reset(struct net_device *dev); |
628 | 629 | ||
630 | /* Wake-on-LAN routines */ | ||
631 | void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol); | ||
632 | int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol); | ||
633 | int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, | ||
634 | enum bcmgenet_power_mode mode); | ||
635 | void 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 | */ | ||
44 | void 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 | */ | ||
64 | int 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 | |||
104 | static 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 | |||
122 | int 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 | |||
180 | void 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 | } | ||