diff options
author | Steve Glendinning <steve.glendinning@shawell.net> | 2012-09-27 20:57:53 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-09-28 18:35:47 -0400 |
commit | 6c636503260d1a5598f44f940f284cf679dc38f9 (patch) | |
tree | 13f14c2efa44d9319448cc862873aa9adc0408d3 | |
parent | 16c79a04e262e51c790af4b074963dd592c617f2 (diff) |
smsc75xx: add wol magic packet support
This patch enables wake from system suspend on magic packet.
Patch updated to change BUG_ON to WARN_ON_ONCE.
Signed-off-by: Steve Glendinning <steve.glendinning@shawell.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/usb/smsc75xx.c | 188 |
1 files changed, 174 insertions, 14 deletions
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 759e577008b0..b77ae76f4aa8 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c | |||
@@ -52,6 +52,7 @@ | |||
52 | #define USB_PRODUCT_ID_LAN7500 (0x7500) | 52 | #define USB_PRODUCT_ID_LAN7500 (0x7500) |
53 | #define USB_PRODUCT_ID_LAN7505 (0x7505) | 53 | #define USB_PRODUCT_ID_LAN7505 (0x7505) |
54 | #define RXW_PADDING 2 | 54 | #define RXW_PADDING 2 |
55 | #define SUPPORTED_WAKE (WAKE_MAGIC) | ||
55 | 56 | ||
56 | #define check_warn(ret, fmt, args...) \ | 57 | #define check_warn(ret, fmt, args...) \ |
57 | ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) | 58 | ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) |
@@ -65,6 +66,7 @@ | |||
65 | struct smsc75xx_priv { | 66 | struct smsc75xx_priv { |
66 | struct usbnet *dev; | 67 | struct usbnet *dev; |
67 | u32 rfe_ctl; | 68 | u32 rfe_ctl; |
69 | u32 wolopts; | ||
68 | u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN]; | 70 | u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN]; |
69 | struct mutex dataport_mutex; | 71 | struct mutex dataport_mutex; |
70 | spinlock_t rfe_ctl_lock; | 72 | spinlock_t rfe_ctl_lock; |
@@ -135,6 +137,30 @@ static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, | |||
135 | return ret; | 137 | return ret; |
136 | } | 138 | } |
137 | 139 | ||
140 | static int smsc75xx_set_feature(struct usbnet *dev, u32 feature) | ||
141 | { | ||
142 | if (WARN_ON_ONCE(!dev)) | ||
143 | return -EINVAL; | ||
144 | |||
145 | cpu_to_le32s(&feature); | ||
146 | |||
147 | return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), | ||
148 | USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0, | ||
149 | USB_CTRL_SET_TIMEOUT); | ||
150 | } | ||
151 | |||
152 | static int smsc75xx_clear_feature(struct usbnet *dev, u32 feature) | ||
153 | { | ||
154 | if (WARN_ON_ONCE(!dev)) | ||
155 | return -EINVAL; | ||
156 | |||
157 | cpu_to_le32s(&feature); | ||
158 | |||
159 | return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), | ||
160 | USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0, | ||
161 | USB_CTRL_SET_TIMEOUT); | ||
162 | } | ||
163 | |||
138 | /* Loop until the read is completed with timeout | 164 | /* Loop until the read is completed with timeout |
139 | * called with phy_mutex held */ | 165 | * called with phy_mutex held */ |
140 | static int smsc75xx_phy_wait_not_busy(struct usbnet *dev) | 166 | static int smsc75xx_phy_wait_not_busy(struct usbnet *dev) |
@@ -578,6 +604,26 @@ static int smsc75xx_ethtool_set_eeprom(struct net_device *netdev, | |||
578 | return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data); | 604 | return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data); |
579 | } | 605 | } |
580 | 606 | ||
607 | static void smsc75xx_ethtool_get_wol(struct net_device *net, | ||
608 | struct ethtool_wolinfo *wolinfo) | ||
609 | { | ||
610 | struct usbnet *dev = netdev_priv(net); | ||
611 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | ||
612 | |||
613 | wolinfo->supported = SUPPORTED_WAKE; | ||
614 | wolinfo->wolopts = pdata->wolopts; | ||
615 | } | ||
616 | |||
617 | static int smsc75xx_ethtool_set_wol(struct net_device *net, | ||
618 | struct ethtool_wolinfo *wolinfo) | ||
619 | { | ||
620 | struct usbnet *dev = netdev_priv(net); | ||
621 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | ||
622 | |||
623 | pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; | ||
624 | return 0; | ||
625 | } | ||
626 | |||
581 | static const struct ethtool_ops smsc75xx_ethtool_ops = { | 627 | static const struct ethtool_ops smsc75xx_ethtool_ops = { |
582 | .get_link = usbnet_get_link, | 628 | .get_link = usbnet_get_link, |
583 | .nway_reset = usbnet_nway_reset, | 629 | .nway_reset = usbnet_nway_reset, |
@@ -589,6 +635,8 @@ static const struct ethtool_ops smsc75xx_ethtool_ops = { | |||
589 | .get_eeprom_len = smsc75xx_ethtool_get_eeprom_len, | 635 | .get_eeprom_len = smsc75xx_ethtool_get_eeprom_len, |
590 | .get_eeprom = smsc75xx_ethtool_get_eeprom, | 636 | .get_eeprom = smsc75xx_ethtool_get_eeprom, |
591 | .set_eeprom = smsc75xx_ethtool_set_eeprom, | 637 | .set_eeprom = smsc75xx_ethtool_set_eeprom, |
638 | .get_wol = smsc75xx_ethtool_get_wol, | ||
639 | .set_wol = smsc75xx_ethtool_set_wol, | ||
592 | }; | 640 | }; |
593 | 641 | ||
594 | static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) | 642 | static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) |
@@ -1109,47 +1157,159 @@ static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf) | |||
1109 | static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) | 1157 | static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) |
1110 | { | 1158 | { |
1111 | struct usbnet *dev = usb_get_intfdata(intf); | 1159 | struct usbnet *dev = usb_get_intfdata(intf); |
1160 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | ||
1112 | int ret; | 1161 | int ret; |
1113 | u32 val; | 1162 | u32 val; |
1114 | 1163 | ||
1115 | if (WARN_ON_ONCE(!dev)) | ||
1116 | return -EINVAL; | ||
1117 | |||
1118 | ret = usbnet_suspend(intf, message); | 1164 | ret = usbnet_suspend(intf, message); |
1119 | check_warn_return(ret, "usbnet_suspend error"); | 1165 | check_warn_return(ret, "usbnet_suspend error"); |
1120 | 1166 | ||
1121 | netdev_info(dev->net, "entering SUSPEND2 mode"); | 1167 | /* if no wol options set, enter lowest power SUSPEND2 mode */ |
1168 | if (!(pdata->wolopts & SUPPORTED_WAKE)) { | ||
1169 | netdev_info(dev->net, "entering SUSPEND2 mode"); | ||
1170 | |||
1171 | /* disable energy detect (link up) & wake up events */ | ||
1172 | ret = smsc75xx_read_reg(dev, WUCSR, &val); | ||
1173 | check_warn_return(ret, "Error reading WUCSR"); | ||
1174 | |||
1175 | val &= ~(WUCSR_MPEN | WUCSR_WUEN); | ||
1176 | |||
1177 | ret = smsc75xx_write_reg(dev, WUCSR, val); | ||
1178 | check_warn_return(ret, "Error writing WUCSR"); | ||
1179 | |||
1180 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | ||
1181 | check_warn_return(ret, "Error reading PMT_CTL"); | ||
1182 | |||
1183 | val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN); | ||
1184 | |||
1185 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | ||
1186 | check_warn_return(ret, "Error writing PMT_CTL"); | ||
1187 | |||
1188 | /* enter suspend2 mode */ | ||
1189 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | ||
1190 | check_warn_return(ret, "Error reading PMT_CTL"); | ||
1191 | |||
1192 | val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); | ||
1193 | val |= PMT_CTL_SUS_MODE_2; | ||
1194 | |||
1195 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | ||
1196 | check_warn_return(ret, "Error writing PMT_CTL"); | ||
1197 | |||
1198 | return 0; | ||
1199 | } | ||
1200 | |||
1201 | if (pdata->wolopts & WAKE_MAGIC) { | ||
1202 | /* clear any pending magic packet status */ | ||
1203 | ret = smsc75xx_read_reg(dev, WUCSR, &val); | ||
1204 | check_warn_return(ret, "Error reading WUCSR"); | ||
1205 | |||
1206 | val |= WUCSR_MPR; | ||
1122 | 1207 | ||
1208 | ret = smsc75xx_write_reg(dev, WUCSR, val); | ||
1209 | check_warn_return(ret, "Error writing WUCSR"); | ||
1210 | } | ||
1211 | |||
1212 | /* enable/disable magic packup wake */ | ||
1213 | ret = smsc75xx_read_reg(dev, WUCSR, &val); | ||
1214 | check_warn_return(ret, "Error reading WUCSR"); | ||
1215 | |||
1216 | if (pdata->wolopts & WAKE_MAGIC) { | ||
1217 | netdev_info(dev->net, "enabling magic packet wakeup"); | ||
1218 | val |= WUCSR_MPEN; | ||
1219 | } else { | ||
1220 | netdev_info(dev->net, "disabling magic packet wakeup"); | ||
1221 | val &= ~WUCSR_MPEN; | ||
1222 | } | ||
1223 | |||
1224 | ret = smsc75xx_write_reg(dev, WUCSR, val); | ||
1225 | check_warn_return(ret, "Error writing WUCSR"); | ||
1226 | |||
1227 | /* enable wol wakeup source */ | ||
1123 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | 1228 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); |
1124 | check_warn_return(ret, "Error reading PMT_CTL"); | 1229 | check_warn_return(ret, "Error reading PMT_CTL"); |
1125 | 1230 | ||
1126 | val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); | 1231 | val |= PMT_CTL_WOL_EN; |
1127 | val |= PMT_CTL_SUS_MODE_2; | 1232 | |
1233 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | ||
1234 | check_warn_return(ret, "Error writing PMT_CTL"); | ||
1235 | |||
1236 | /* enable receiver */ | ||
1237 | ret = smsc75xx_read_reg(dev, MAC_RX, &val); | ||
1238 | check_warn_return(ret, "Failed to read MAC_RX: %d", ret); | ||
1239 | |||
1240 | val |= MAC_RX_RXEN; | ||
1241 | |||
1242 | ret = smsc75xx_write_reg(dev, MAC_RX, val); | ||
1243 | check_warn_return(ret, "Failed to write MAC_RX: %d", ret); | ||
1244 | |||
1245 | /* some wol options are enabled, so enter SUSPEND0 */ | ||
1246 | netdev_info(dev->net, "entering SUSPEND0 mode"); | ||
1247 | |||
1248 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | ||
1249 | check_warn_return(ret, "Error reading PMT_CTL"); | ||
1250 | |||
1251 | val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST)); | ||
1252 | val |= PMT_CTL_SUS_MODE_0; | ||
1253 | |||
1254 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | ||
1255 | check_warn_return(ret, "Error writing PMT_CTL"); | ||
1128 | 1256 | ||
1257 | /* clear wol status */ | ||
1258 | val &= ~PMT_CTL_WUPS; | ||
1259 | val |= PMT_CTL_WUPS_WOL; | ||
1129 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | 1260 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); |
1130 | check_warn_return(ret, "Error writing PMT_CTL"); | 1261 | check_warn_return(ret, "Error writing PMT_CTL"); |
1131 | 1262 | ||
1263 | /* read back PMT_CTL */ | ||
1264 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | ||
1265 | check_warn_return(ret, "Error reading PMT_CTL"); | ||
1266 | |||
1267 | smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); | ||
1268 | |||
1132 | return 0; | 1269 | return 0; |
1133 | } | 1270 | } |
1134 | 1271 | ||
1135 | static int smsc75xx_resume(struct usb_interface *intf) | 1272 | static int smsc75xx_resume(struct usb_interface *intf) |
1136 | { | 1273 | { |
1137 | struct usbnet *dev = usb_get_intfdata(intf); | 1274 | struct usbnet *dev = usb_get_intfdata(intf); |
1275 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | ||
1138 | int ret; | 1276 | int ret; |
1139 | u32 val; | 1277 | u32 val; |
1140 | 1278 | ||
1141 | if (WARN_ON_ONCE(!dev)) | 1279 | if (pdata->wolopts & WAKE_MAGIC) { |
1142 | return -EINVAL; | 1280 | netdev_info(dev->net, "resuming from SUSPEND0"); |
1143 | 1281 | ||
1144 | netdev_info(dev->net, "resuming from SUSPEND2"); | 1282 | smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP); |
1145 | 1283 | ||
1146 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | 1284 | /* Disable magic packup wake */ |
1147 | check_warn_return(ret, "Error reading PMT_CTL"); | 1285 | ret = smsc75xx_read_reg(dev, WUCSR, &val); |
1286 | check_warn_return(ret, "Error reading WUCSR"); | ||
1148 | 1287 | ||
1149 | val |= PMT_CTL_PHY_PWRUP; | 1288 | val &= ~WUCSR_MPEN; |
1150 | 1289 | ||
1151 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | 1290 | ret = smsc75xx_write_reg(dev, WUCSR, val); |
1152 | check_warn_return(ret, "Error writing PMT_CTL"); | 1291 | check_warn_return(ret, "Error writing WUCSR"); |
1292 | |||
1293 | /* clear wake-up status */ | ||
1294 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | ||
1295 | check_warn_return(ret, "Error reading PMT_CTL"); | ||
1296 | |||
1297 | val &= ~PMT_CTL_WOL_EN; | ||
1298 | val |= PMT_CTL_WUPS; | ||
1299 | |||
1300 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | ||
1301 | check_warn_return(ret, "Error writing PMT_CTL"); | ||
1302 | } else { | ||
1303 | netdev_info(dev->net, "resuming from SUSPEND2"); | ||
1304 | |||
1305 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | ||
1306 | check_warn_return(ret, "Error reading PMT_CTL"); | ||
1307 | |||
1308 | val |= PMT_CTL_PHY_PWRUP; | ||
1309 | |||
1310 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | ||
1311 | check_warn_return(ret, "Error writing PMT_CTL"); | ||
1312 | } | ||
1153 | 1313 | ||
1154 | ret = smsc75xx_wait_ready(dev); | 1314 | ret = smsc75xx_wait_ready(dev); |
1155 | check_warn_return(ret, "device not ready in smsc75xx_resume"); | 1315 | check_warn_return(ret, "device not ready in smsc75xx_resume"); |