diff options
Diffstat (limited to 'drivers/net/usb/smsc75xx.c')
-rw-r--r-- | drivers/net/usb/smsc75xx.c | 240 |
1 files changed, 237 insertions, 3 deletions
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 376143e8a1aa..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) |
@@ -756,6 +804,26 @@ static int smsc75xx_set_features(struct net_device *netdev, | |||
756 | return 0; | 804 | return 0; |
757 | } | 805 | } |
758 | 806 | ||
807 | static int smsc75xx_wait_ready(struct usbnet *dev) | ||
808 | { | ||
809 | int timeout = 0; | ||
810 | |||
811 | do { | ||
812 | u32 buf; | ||
813 | int ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); | ||
814 | check_warn_return(ret, "Failed to read PMT_CTL: %d", ret); | ||
815 | |||
816 | if (buf & PMT_CTL_DEV_RDY) | ||
817 | return 0; | ||
818 | |||
819 | msleep(10); | ||
820 | timeout++; | ||
821 | } while (timeout < 100); | ||
822 | |||
823 | netdev_warn(dev->net, "timeout waiting for device ready"); | ||
824 | return -EIO; | ||
825 | } | ||
826 | |||
759 | static int smsc75xx_reset(struct usbnet *dev) | 827 | static int smsc75xx_reset(struct usbnet *dev) |
760 | { | 828 | { |
761 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | 829 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); |
@@ -764,6 +832,9 @@ static int smsc75xx_reset(struct usbnet *dev) | |||
764 | 832 | ||
765 | netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset"); | 833 | netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset"); |
766 | 834 | ||
835 | ret = smsc75xx_wait_ready(dev); | ||
836 | check_warn_return(ret, "device not ready in smsc75xx_reset"); | ||
837 | |||
767 | ret = smsc75xx_read_reg(dev, HW_CFG, &buf); | 838 | ret = smsc75xx_read_reg(dev, HW_CFG, &buf); |
768 | check_warn_return(ret, "Failed to read HW_CFG: %d", ret); | 839 | check_warn_return(ret, "Failed to read HW_CFG: %d", ret); |
769 | 840 | ||
@@ -1083,6 +1154,169 @@ static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf) | |||
1083 | } | 1154 | } |
1084 | } | 1155 | } |
1085 | 1156 | ||
1157 | static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) | ||
1158 | { | ||
1159 | struct usbnet *dev = usb_get_intfdata(intf); | ||
1160 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | ||
1161 | int ret; | ||
1162 | u32 val; | ||
1163 | |||
1164 | ret = usbnet_suspend(intf, message); | ||
1165 | check_warn_return(ret, "usbnet_suspend error"); | ||
1166 | |||
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; | ||
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 */ | ||
1228 | ret = smsc75xx_read_reg(dev, PMT_CTL, &val); | ||
1229 | check_warn_return(ret, "Error reading PMT_CTL"); | ||
1230 | |||
1231 | val |= PMT_CTL_WOL_EN; | ||
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"); | ||
1256 | |||
1257 | /* clear wol status */ | ||
1258 | val &= ~PMT_CTL_WUPS; | ||
1259 | val |= PMT_CTL_WUPS_WOL; | ||
1260 | ret = smsc75xx_write_reg(dev, PMT_CTL, val); | ||
1261 | check_warn_return(ret, "Error writing PMT_CTL"); | ||
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 | |||
1269 | return 0; | ||
1270 | } | ||
1271 | |||
1272 | static int smsc75xx_resume(struct usb_interface *intf) | ||
1273 | { | ||
1274 | struct usbnet *dev = usb_get_intfdata(intf); | ||
1275 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | ||
1276 | int ret; | ||
1277 | u32 val; | ||
1278 | |||
1279 | if (pdata->wolopts & WAKE_MAGIC) { | ||
1280 | netdev_info(dev->net, "resuming from SUSPEND0"); | ||
1281 | |||
1282 | smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP); | ||
1283 | |||
1284 | /* Disable magic packup wake */ | ||
1285 | ret = smsc75xx_read_reg(dev, WUCSR, &val); | ||
1286 | check_warn_return(ret, "Error reading WUCSR"); | ||
1287 | |||
1288 | val &= ~WUCSR_MPEN; | ||
1289 | |||
1290 | ret = smsc75xx_write_reg(dev, WUCSR, val); | ||
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 | } | ||
1313 | |||
1314 | ret = smsc75xx_wait_ready(dev); | ||
1315 | check_warn_return(ret, "device not ready in smsc75xx_resume"); | ||
1316 | |||
1317 | return usbnet_resume(intf); | ||
1318 | } | ||
1319 | |||
1086 | static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb, | 1320 | static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb, |
1087 | u32 rx_cmd_a, u32 rx_cmd_b) | 1321 | u32 rx_cmd_a, u32 rx_cmd_b) |
1088 | { | 1322 | { |
@@ -1251,9 +1485,9 @@ static struct usb_driver smsc75xx_driver = { | |||
1251 | .name = SMSC_CHIPNAME, | 1485 | .name = SMSC_CHIPNAME, |
1252 | .id_table = products, | 1486 | .id_table = products, |
1253 | .probe = usbnet_probe, | 1487 | .probe = usbnet_probe, |
1254 | .suspend = usbnet_suspend, | 1488 | .suspend = smsc75xx_suspend, |
1255 | .resume = usbnet_resume, | 1489 | .resume = smsc75xx_resume, |
1256 | .reset_resume = usbnet_resume, | 1490 | .reset_resume = smsc75xx_resume, |
1257 | .disconnect = usbnet_disconnect, | 1491 | .disconnect = usbnet_disconnect, |
1258 | .disable_hub_initiated_lpm = 1, | 1492 | .disable_hub_initiated_lpm = 1, |
1259 | }; | 1493 | }; |