diff options
author | Steve Glendinning <steve.glendinning@shawell.net> | 2012-11-28 00:59:47 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-28 11:17:31 -0500 |
commit | f329ccdc6cd9ef866045af7ccc5fa7d2a9a1f6bb (patch) | |
tree | 7751a719a9e108568451cc5a7f122b2806291c77 /drivers/net | |
parent | 9deb2757b80fded37af33394d4d97e8341108525 (diff) |
smsc75xx: support PHY wakeup source
This patch enables LAN7500 family devices to wake from suspend
on either link up or link down events.
It also adds _nopm versions of mdio access functions, so we can
safely call them from suspend and resume functions
Updated patch to add newlines to printk messages
Signed-off-by: Steve Glendinning <steve.glendinning@shawell.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/usb/smsc75xx.c | 169 |
1 files changed, 152 insertions, 17 deletions
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index c26aeba1838..3e3b1014141 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c | |||
@@ -54,7 +54,7 @@ | |||
54 | #define USB_PRODUCT_ID_LAN7500 (0x7500) | 54 | #define USB_PRODUCT_ID_LAN7500 (0x7500) |
55 | #define USB_PRODUCT_ID_LAN7505 (0x7505) | 55 | #define USB_PRODUCT_ID_LAN7505 (0x7505) |
56 | #define RXW_PADDING 2 | 56 | #define RXW_PADDING 2 |
57 | #define SUPPORTED_WAKE (WAKE_UCAST | WAKE_BCAST | \ | 57 | #define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ |
58 | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) | 58 | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) |
59 | 59 | ||
60 | #define check_warn(ret, fmt, args...) \ | 60 | #define check_warn(ret, fmt, args...) \ |
@@ -185,14 +185,15 @@ static int smsc75xx_clear_feature(struct usbnet *dev, u32 feature) | |||
185 | 185 | ||
186 | /* Loop until the read is completed with timeout | 186 | /* Loop until the read is completed with timeout |
187 | * called with phy_mutex held */ | 187 | * called with phy_mutex held */ |
188 | static int smsc75xx_phy_wait_not_busy(struct usbnet *dev) | 188 | static __must_check int __smsc75xx_phy_wait_not_busy(struct usbnet *dev, |
189 | int in_pm) | ||
189 | { | 190 | { |
190 | unsigned long start_time = jiffies; | 191 | unsigned long start_time = jiffies; |
191 | u32 val; | 192 | u32 val; |
192 | int ret; | 193 | int ret; |
193 | 194 | ||
194 | do { | 195 | do { |
195 | ret = smsc75xx_read_reg(dev, MII_ACCESS, &val); | 196 | ret = __smsc75xx_read_reg(dev, MII_ACCESS, &val, in_pm); |
196 | check_warn_return(ret, "Error reading MII_ACCESS\n"); | 197 | check_warn_return(ret, "Error reading MII_ACCESS\n"); |
197 | 198 | ||
198 | if (!(val & MII_ACCESS_BUSY)) | 199 | if (!(val & MII_ACCESS_BUSY)) |
@@ -202,7 +203,8 @@ static int smsc75xx_phy_wait_not_busy(struct usbnet *dev) | |||
202 | return -EIO; | 203 | return -EIO; |
203 | } | 204 | } |
204 | 205 | ||
205 | static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) | 206 | static int __smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx, |
207 | int in_pm) | ||
206 | { | 208 | { |
207 | struct usbnet *dev = netdev_priv(netdev); | 209 | struct usbnet *dev = netdev_priv(netdev); |
208 | u32 val, addr; | 210 | u32 val, addr; |
@@ -211,7 +213,7 @@ static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) | |||
211 | mutex_lock(&dev->phy_mutex); | 213 | mutex_lock(&dev->phy_mutex); |
212 | 214 | ||
213 | /* confirm MII not busy */ | 215 | /* confirm MII not busy */ |
214 | ret = smsc75xx_phy_wait_not_busy(dev); | 216 | ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); |
215 | check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_read\n"); | 217 | check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_read\n"); |
216 | 218 | ||
217 | /* set the address, index & direction (read from PHY) */ | 219 | /* set the address, index & direction (read from PHY) */ |
@@ -220,13 +222,13 @@ static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) | |||
220 | addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) | 222 | addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) |
221 | | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) | 223 | | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) |
222 | | MII_ACCESS_READ | MII_ACCESS_BUSY; | 224 | | MII_ACCESS_READ | MII_ACCESS_BUSY; |
223 | ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); | 225 | ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); |
224 | check_warn_goto_done(ret, "Error writing MII_ACCESS\n"); | 226 | check_warn_goto_done(ret, "Error writing MII_ACCESS\n"); |
225 | 227 | ||
226 | ret = smsc75xx_phy_wait_not_busy(dev); | 228 | ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); |
227 | check_warn_goto_done(ret, "Timed out reading MII reg %02X\n", idx); | 229 | check_warn_goto_done(ret, "Timed out reading MII reg %02X\n", idx); |
228 | 230 | ||
229 | ret = smsc75xx_read_reg(dev, MII_DATA, &val); | 231 | ret = __smsc75xx_read_reg(dev, MII_DATA, &val, in_pm); |
230 | check_warn_goto_done(ret, "Error reading MII_DATA\n"); | 232 | check_warn_goto_done(ret, "Error reading MII_DATA\n"); |
231 | 233 | ||
232 | ret = (u16)(val & 0xFFFF); | 234 | ret = (u16)(val & 0xFFFF); |
@@ -236,8 +238,8 @@ done: | |||
236 | return ret; | 238 | return ret; |
237 | } | 239 | } |
238 | 240 | ||
239 | static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, | 241 | static void __smsc75xx_mdio_write(struct net_device *netdev, int phy_id, |
240 | int regval) | 242 | int idx, int regval, int in_pm) |
241 | { | 243 | { |
242 | struct usbnet *dev = netdev_priv(netdev); | 244 | struct usbnet *dev = netdev_priv(netdev); |
243 | u32 val, addr; | 245 | u32 val, addr; |
@@ -246,11 +248,11 @@ static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, | |||
246 | mutex_lock(&dev->phy_mutex); | 248 | mutex_lock(&dev->phy_mutex); |
247 | 249 | ||
248 | /* confirm MII not busy */ | 250 | /* confirm MII not busy */ |
249 | ret = smsc75xx_phy_wait_not_busy(dev); | 251 | ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); |
250 | check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_write\n"); | 252 | check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_write\n"); |
251 | 253 | ||
252 | val = regval; | 254 | val = regval; |
253 | ret = smsc75xx_write_reg(dev, MII_DATA, val); | 255 | ret = __smsc75xx_write_reg(dev, MII_DATA, val, in_pm); |
254 | check_warn_goto_done(ret, "Error writing MII_DATA\n"); | 256 | check_warn_goto_done(ret, "Error writing MII_DATA\n"); |
255 | 257 | ||
256 | /* set the address, index & direction (write to PHY) */ | 258 | /* set the address, index & direction (write to PHY) */ |
@@ -259,16 +261,39 @@ static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, | |||
259 | addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) | 261 | addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) |
260 | | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) | 262 | | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) |
261 | | MII_ACCESS_WRITE | MII_ACCESS_BUSY; | 263 | | MII_ACCESS_WRITE | MII_ACCESS_BUSY; |
262 | ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); | 264 | ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); |
263 | check_warn_goto_done(ret, "Error writing MII_ACCESS\n"); | 265 | check_warn_goto_done(ret, "Error writing MII_ACCESS\n"); |
264 | 266 | ||
265 | ret = smsc75xx_phy_wait_not_busy(dev); | 267 | ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); |
266 | check_warn_goto_done(ret, "Timed out writing MII reg %02X\n", idx); | 268 | check_warn_goto_done(ret, "Timed out writing MII reg %02X\n", idx); |
267 | 269 | ||
268 | done: | 270 | done: |
269 | mutex_unlock(&dev->phy_mutex); | 271 | mutex_unlock(&dev->phy_mutex); |
270 | } | 272 | } |
271 | 273 | ||
274 | static int smsc75xx_mdio_read_nopm(struct net_device *netdev, int phy_id, | ||
275 | int idx) | ||
276 | { | ||
277 | return __smsc75xx_mdio_read(netdev, phy_id, idx, 1); | ||
278 | } | ||
279 | |||
280 | static void smsc75xx_mdio_write_nopm(struct net_device *netdev, int phy_id, | ||
281 | int idx, int regval) | ||
282 | { | ||
283 | __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 1); | ||
284 | } | ||
285 | |||
286 | static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) | ||
287 | { | ||
288 | return __smsc75xx_mdio_read(netdev, phy_id, idx, 0); | ||
289 | } | ||
290 | |||
291 | static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, | ||
292 | int regval) | ||
293 | { | ||
294 | __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 0); | ||
295 | } | ||
296 | |||
272 | static int smsc75xx_wait_eeprom(struct usbnet *dev) | 297 | static int smsc75xx_wait_eeprom(struct usbnet *dev) |
273 | { | 298 | { |
274 | unsigned long start_time = jiffies; | 299 | unsigned long start_time = jiffies; |
@@ -1233,6 +1258,32 @@ static int smsc75xx_enter_suspend0(struct usbnet *dev) | |||
1233 | return 0; | 1258 | return 0; |
1234 | } | 1259 | } |
1235 | 1260 | ||
1261 | static int smsc75xx_enter_suspend1(struct usbnet *dev) | ||
1262 | { | ||
1263 | u32 val; | ||
1264 | int ret; | ||
1265 | |||
1266 | ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); | ||
1267 | check_warn_return(ret, "Error reading PMT_CTL\n"); | ||
1268 | |||
1269 | val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); | ||
1270 | val |= PMT_CTL_SUS_MODE_1; | ||
1271 | |||
1272 | ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); | ||
1273 | check_warn_return(ret, "Error writing PMT_CTL\n"); | ||
1274 | |||
1275 | /* clear wol status, enable energy detection */ | ||
1276 | val &= ~PMT_CTL_WUPS; | ||
1277 | val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); | ||
1278 | |||
1279 | ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); | ||
1280 | check_warn_return(ret, "Error writing PMT_CTL\n"); | ||
1281 | |||
1282 | smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); | ||
1283 | |||
1284 | return 0; | ||
1285 | } | ||
1286 | |||
1236 | static int smsc75xx_enter_suspend2(struct usbnet *dev) | 1287 | static int smsc75xx_enter_suspend2(struct usbnet *dev) |
1237 | { | 1288 | { |
1238 | u32 val; | 1289 | u32 val; |
@@ -1250,18 +1301,61 @@ static int smsc75xx_enter_suspend2(struct usbnet *dev) | |||
1250 | return 0; | 1301 | return 0; |
1251 | } | 1302 | } |
1252 | 1303 | ||
1304 | static int smsc75xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) | ||
1305 | { | ||
1306 | struct mii_if_info *mii = &dev->mii; | ||
1307 | int ret; | ||
1308 | |||
1309 | netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n"); | ||
1310 | |||
1311 | /* read to clear */ | ||
1312 | ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); | ||
1313 | check_warn_return(ret, "Error reading PHY_INT_SRC\n"); | ||
1314 | |||
1315 | /* enable interrupt source */ | ||
1316 | ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); | ||
1317 | check_warn_return(ret, "Error reading PHY_INT_MASK\n"); | ||
1318 | |||
1319 | ret |= mask; | ||
1320 | |||
1321 | smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); | ||
1322 | |||
1323 | return 0; | ||
1324 | } | ||
1325 | |||
1326 | static int smsc75xx_link_ok_nopm(struct usbnet *dev) | ||
1327 | { | ||
1328 | struct mii_if_info *mii = &dev->mii; | ||
1329 | int ret; | ||
1330 | |||
1331 | /* first, a dummy read, needed to latch some MII phys */ | ||
1332 | ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); | ||
1333 | check_warn_return(ret, "Error reading MII_BMSR\n"); | ||
1334 | |||
1335 | ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); | ||
1336 | check_warn_return(ret, "Error reading MII_BMSR\n"); | ||
1337 | |||
1338 | return !!(ret & BMSR_LSTATUS); | ||
1339 | } | ||
1340 | |||
1253 | static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) | 1341 | static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) |
1254 | { | 1342 | { |
1255 | struct usbnet *dev = usb_get_intfdata(intf); | 1343 | struct usbnet *dev = usb_get_intfdata(intf); |
1256 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | 1344 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); |
1345 | u32 val, link_up; | ||
1257 | int ret; | 1346 | int ret; |
1258 | u32 val; | ||
1259 | 1347 | ||
1260 | ret = usbnet_suspend(intf, message); | 1348 | ret = usbnet_suspend(intf, message); |
1261 | check_warn_return(ret, "usbnet_suspend error\n"); | 1349 | check_warn_return(ret, "usbnet_suspend error\n"); |
1262 | 1350 | ||
1263 | /* if no wol options set, enter lowest power SUSPEND2 mode */ | 1351 | /* determine if link is up using only _nopm functions */ |
1264 | if (!(pdata->wolopts & SUPPORTED_WAKE)) { | 1352 | link_up = smsc75xx_link_ok_nopm(dev); |
1353 | |||
1354 | /* if no wol options set, or if link is down and we're not waking on | ||
1355 | * PHY activity, enter lowest power SUSPEND2 mode | ||
1356 | */ | ||
1357 | if (!(pdata->wolopts & SUPPORTED_WAKE) || | ||
1358 | !(link_up || (pdata->wolopts & WAKE_PHY))) { | ||
1265 | netdev_info(dev->net, "entering SUSPEND2 mode\n"); | 1359 | netdev_info(dev->net, "entering SUSPEND2 mode\n"); |
1266 | 1360 | ||
1267 | /* disable energy detect (link up) & wake up events */ | 1361 | /* disable energy detect (link up) & wake up events */ |
@@ -1284,6 +1378,33 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) | |||
1284 | return smsc75xx_enter_suspend2(dev); | 1378 | return smsc75xx_enter_suspend2(dev); |
1285 | } | 1379 | } |
1286 | 1380 | ||
1381 | if (pdata->wolopts & WAKE_PHY) { | ||
1382 | ret = smsc75xx_enable_phy_wakeup_interrupts(dev, | ||
1383 | (PHY_INT_MASK_ANEG_COMP | PHY_INT_MASK_LINK_DOWN)); | ||
1384 | check_warn_return(ret, "error enabling PHY wakeup ints\n"); | ||
1385 | |||
1386 | /* if link is down then configure EDPD and enter SUSPEND1, | ||
1387 | * otherwise enter SUSPEND0 below | ||
1388 | */ | ||
1389 | if (!link_up) { | ||
1390 | struct mii_if_info *mii = &dev->mii; | ||
1391 | netdev_info(dev->net, "entering SUSPEND1 mode\n"); | ||
1392 | |||
1393 | /* enable energy detect power-down mode */ | ||
1394 | ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, | ||
1395 | PHY_MODE_CTRL_STS); | ||
1396 | check_warn_return(ret, "Error reading PHY_MODE_CTRL_STS\n"); | ||
1397 | |||
1398 | ret |= MODE_CTRL_STS_EDPWRDOWN; | ||
1399 | |||
1400 | smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, | ||
1401 | PHY_MODE_CTRL_STS, ret); | ||
1402 | |||
1403 | /* enter SUSPEND1 mode */ | ||
1404 | return smsc75xx_enter_suspend1(dev); | ||
1405 | } | ||
1406 | } | ||
1407 | |||
1287 | if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) { | 1408 | if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) { |
1288 | int i, filter = 0; | 1409 | int i, filter = 0; |
1289 | 1410 | ||
@@ -1350,6 +1471,20 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) | |||
1350 | ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); | 1471 | ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); |
1351 | check_warn_return(ret, "Error writing WUCSR\n"); | 1472 | check_warn_return(ret, "Error writing WUCSR\n"); |
1352 | 1473 | ||
1474 | if (pdata->wolopts & WAKE_PHY) { | ||
1475 | netdev_info(dev->net, "enabling PHY wakeup\n"); | ||
1476 | |||
1477 | ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); | ||
1478 | check_warn_return(ret, "Error reading PMT_CTL\n"); | ||
1479 | |||
1480 | /* clear wol status, enable energy detection */ | ||
1481 | val &= ~PMT_CTL_WUPS; | ||
1482 | val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); | ||
1483 | |||
1484 | ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); | ||
1485 | check_warn_return(ret, "Error writing PMT_CTL\n"); | ||
1486 | } | ||
1487 | |||
1353 | if (pdata->wolopts & WAKE_MAGIC) { | 1488 | if (pdata->wolopts & WAKE_MAGIC) { |
1354 | netdev_info(dev->net, "enabling magic packet wakeup\n"); | 1489 | netdev_info(dev->net, "enabling magic packet wakeup\n"); |
1355 | ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); | 1490 | ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); |