aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorRaju Lakkaraju <Raju.Lakkaraju@microsemi.com>2016-10-05 04:49:27 -0400
committerDavid S. Miller <davem@davemloft.net>2016-10-06 20:48:04 -0400
commit0a55c12f9734105c004e464b5eebb79f08634d7a (patch)
treea8f7cab0a0133a641d4fc6755fb7524994670eb3 /drivers/net
parented2eb0fb873e792798258eb00071d68f7278fa78 (diff)
net: phy: Add Wake-on-LAN driver for Microsemi PHYs.
Wake-on-LAN (WoL) is an Ethernet networking standard that allows a computer/device to be turned on or awakened by a network message. VSC8531 PHY can support this feature configure by driver set function. WoL status get by driver get function. Tested on Beaglebone Black with VSC 8531 PHY. Signed-off-by: Raju Lakkaraju <Raju.Lakkaraju@microsemi.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/phy/mscc.c128
1 files changed, 128 insertions, 0 deletions
diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c
index a17573e3bd8a..77a6671d572e 100644
--- a/drivers/net/phy/mscc.c
+++ b/drivers/net/phy/mscc.c
@@ -13,6 +13,7 @@
13#include <linux/phy.h> 13#include <linux/phy.h>
14#include <linux/of.h> 14#include <linux/of.h>
15#include <dt-bindings/net/mscc-phy-vsc8531.h> 15#include <dt-bindings/net/mscc-phy-vsc8531.h>
16#include <linux/netdevice.h>
16 17
17enum rgmii_rx_clock_delay { 18enum rgmii_rx_clock_delay {
18 RGMII_RX_CLK_DELAY_0_2_NS = 0, 19 RGMII_RX_CLK_DELAY_0_2_NS = 0,
@@ -37,6 +38,7 @@ enum rgmii_rx_clock_delay {
37 38
38#define MII_VSC85XX_INT_MASK 25 39#define MII_VSC85XX_INT_MASK 25
39#define MII_VSC85XX_INT_MASK_MASK 0xa000 40#define MII_VSC85XX_INT_MASK_MASK 0xa000
41#define MII_VSC85XX_INT_MASK_WOL 0x0040
40#define MII_VSC85XX_INT_STATUS 26 42#define MII_VSC85XX_INT_STATUS 26
41 43
42#define MSCC_PHY_WOL_MAC_CONTROL 27 44#define MSCC_PHY_WOL_MAC_CONTROL 27
@@ -52,6 +54,17 @@ enum rgmii_rx_clock_delay {
52#define RGMII_RX_CLK_DELAY_MASK 0x0070 54#define RGMII_RX_CLK_DELAY_MASK 0x0070
53#define RGMII_RX_CLK_DELAY_POS 4 55#define RGMII_RX_CLK_DELAY_POS 4
54 56
57#define MSCC_PHY_WOL_LOWER_MAC_ADDR 21
58#define MSCC_PHY_WOL_MID_MAC_ADDR 22
59#define MSCC_PHY_WOL_UPPER_MAC_ADDR 23
60#define MSCC_PHY_WOL_LOWER_PASSWD 24
61#define MSCC_PHY_WOL_MID_PASSWD 25
62#define MSCC_PHY_WOL_UPPER_PASSWD 26
63
64#define MSCC_PHY_WOL_MAC_CONTROL 27
65#define SECURE_ON_ENABLE 0x8000
66#define SECURE_ON_PASSWD_LEN_4 0x4000
67
55/* Microsemi PHY ID's */ 68/* Microsemi PHY ID's */
56#define PHY_ID_VSC8531 0x00070570 69#define PHY_ID_VSC8531 0x00070570
57#define PHY_ID_VSC8541 0x00070770 70#define PHY_ID_VSC8541 0x00070770
@@ -81,6 +94,117 @@ static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
81 return rc; 94 return rc;
82} 95}
83 96
97static int vsc85xx_wol_set(struct phy_device *phydev,
98 struct ethtool_wolinfo *wol)
99{
100 int rc;
101 u16 reg_val;
102 u8 i;
103 u16 pwd[3] = {0, 0, 0};
104 struct ethtool_wolinfo *wol_conf = wol;
105 u8 *mac_addr = phydev->attached_dev->dev_addr;
106
107 mutex_lock(&phydev->lock);
108 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
109 if (rc != 0)
110 goto out_unlock;
111
112 if (wol->wolopts & WAKE_MAGIC) {
113 /* Store the device address for the magic packet */
114 for (i = 0; i < ARRAY_SIZE(pwd); i++)
115 pwd[i] = mac_addr[5 - (i * 2 + 1)] << 8 |
116 mac_addr[5 - i * 2];
117 phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, pwd[0]);
118 phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, pwd[1]);
119 phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, pwd[2]);
120 } else {
121 phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, 0);
122 phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, 0);
123 phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, 0);
124 }
125
126 if (wol_conf->wolopts & WAKE_MAGICSECURE) {
127 for (i = 0; i < ARRAY_SIZE(pwd); i++)
128 pwd[i] = wol_conf->sopass[5 - (i * 2 + 1)] << 8 |
129 wol_conf->sopass[5 - i * 2];
130 phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, pwd[0]);
131 phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, pwd[1]);
132 phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, pwd[2]);
133 } else {
134 phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, 0);
135 phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, 0);
136 phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, 0);
137 }
138
139 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
140 if (wol_conf->wolopts & WAKE_MAGICSECURE)
141 reg_val |= SECURE_ON_ENABLE;
142 else
143 reg_val &= ~SECURE_ON_ENABLE;
144 phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
145
146 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
147 if (rc != 0)
148 goto out_unlock;
149
150 if (wol->wolopts & WAKE_MAGIC) {
151 /* Enable the WOL interrupt */
152 reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
153 reg_val |= MII_VSC85XX_INT_MASK_WOL;
154 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
155 if (rc != 0)
156 goto out_unlock;
157 } else {
158 /* Disable the WOL interrupt */
159 reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
160 reg_val &= (~MII_VSC85XX_INT_MASK_WOL);
161 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
162 if (rc != 0)
163 goto out_unlock;
164 }
165 /* Clear WOL iterrupt status */
166 reg_val = phy_read(phydev, MII_VSC85XX_INT_STATUS);
167
168out_unlock:
169 mutex_unlock(&phydev->lock);
170
171 return rc;
172}
173
174static void vsc85xx_wol_get(struct phy_device *phydev,
175 struct ethtool_wolinfo *wol)
176{
177 int rc;
178 u16 reg_val;
179 u8 i;
180 u16 pwd[3] = {0, 0, 0};
181 struct ethtool_wolinfo *wol_conf = wol;
182
183 mutex_lock(&phydev->lock);
184 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
185 if (rc != 0)
186 goto out_unlock;
187
188 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
189 if (reg_val & SECURE_ON_ENABLE)
190 wol_conf->wolopts |= WAKE_MAGICSECURE;
191 if (wol_conf->wolopts & WAKE_MAGICSECURE) {
192 pwd[0] = phy_read(phydev, MSCC_PHY_WOL_LOWER_PASSWD);
193 pwd[1] = phy_read(phydev, MSCC_PHY_WOL_MID_PASSWD);
194 pwd[2] = phy_read(phydev, MSCC_PHY_WOL_UPPER_PASSWD);
195 for (i = 0; i < ARRAY_SIZE(pwd); i++) {
196 wol_conf->sopass[5 - i * 2] = pwd[i] & 0x00ff;
197 wol_conf->sopass[5 - (i * 2 + 1)] = (pwd[i] & 0xff00)
198 >> 8;
199 }
200 }
201
202 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
203
204out_unlock:
205 mutex_unlock(&phydev->lock);
206}
207
84static u8 edge_rate_magic_get(u16 vddmac, 208static u8 edge_rate_magic_get(u16 vddmac,
85 int slowdown) 209 int slowdown)
86{ 210{
@@ -301,6 +425,8 @@ static struct phy_driver vsc85xx_driver[] = {
301 .suspend = &genphy_suspend, 425 .suspend = &genphy_suspend,
302 .resume = &genphy_resume, 426 .resume = &genphy_resume,
303 .probe = &vsc85xx_probe, 427 .probe = &vsc85xx_probe,
428 .set_wol = &vsc85xx_wol_set,
429 .get_wol = &vsc85xx_wol_get,
304}, 430},
305{ 431{
306 .phy_id = PHY_ID_VSC8541, 432 .phy_id = PHY_ID_VSC8541,
@@ -318,6 +444,8 @@ static struct phy_driver vsc85xx_driver[] = {
318 .suspend = &genphy_suspend, 444 .suspend = &genphy_suspend,
319 .resume = &genphy_resume, 445 .resume = &genphy_resume,
320 .probe = &vsc85xx_probe, 446 .probe = &vsc85xx_probe,
447 .set_wol = &vsc85xx_wol_set,
448 .get_wol = &vsc85xx_wol_get,
321} 449}
322 450
323}; 451};