aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Glendinning <steve.glendinning@shawell.net>2012-09-27 20:57:53 -0400
committerDavid S. Miller <davem@davemloft.net>2012-09-28 18:35:47 -0400
commit6c636503260d1a5598f44f940f284cf679dc38f9 (patch)
tree13f14c2efa44d9319448cc862873aa9adc0408d3
parent16c79a04e262e51c790af4b074963dd592c617f2 (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.c188
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 @@
65struct smsc75xx_priv { 66struct 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
140static 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
152static 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 */
140static int smsc75xx_phy_wait_not_busy(struct usbnet *dev) 166static 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
607static 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
617static 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
581static const struct ethtool_ops smsc75xx_ethtool_ops = { 627static 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
594static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) 642static 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)
1109static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) 1157static 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
1135static int smsc75xx_resume(struct usb_interface *intf) 1272static 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");