diff options
author | Raju Lakkaraju <Raju.Lakkaraju@microsemi.com> | 2016-10-05 04:49:27 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-10-06 20:48:04 -0400 |
commit | 0a55c12f9734105c004e464b5eebb79f08634d7a (patch) | |
tree | a8f7cab0a0133a641d4fc6755fb7524994670eb3 /drivers/net | |
parent | ed2eb0fb873e792798258eb00071d68f7278fa78 (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.c | 128 |
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 | ||
17 | enum rgmii_rx_clock_delay { | 18 | enum 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 | ||
97 | static 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 | |||
168 | out_unlock: | ||
169 | mutex_unlock(&phydev->lock); | ||
170 | |||
171 | return rc; | ||
172 | } | ||
173 | |||
174 | static 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 | |||
204 | out_unlock: | ||
205 | mutex_unlock(&phydev->lock); | ||
206 | } | ||
207 | |||
84 | static u8 edge_rate_magic_get(u16 vddmac, | 208 | static 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 | }; |