diff options
author | Steve Glendinning <steve.glendinning@shawell.net> | 2012-09-27 20:07:12 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-09-28 18:34:58 -0400 |
commit | e0e474a83c18f134e42c4845e19d0ef0751f43f0 (patch) | |
tree | c62d08a0812b975954f3e519b56a0aad4aa0792e /drivers/net/usb | |
parent | b5a044758672b6dc775fdab007337b47a54576f4 (diff) |
smsc95xx: add wol magic packet support
This patch enables wake from system suspend on magic packet.
Patch updated to replace BUG_ON with WARN_ON_ONCE and return.
Signed-off-by: Steve Glendinning <steve.glendinning@shawell.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 185 | ||||
-rw-r--r-- | drivers/net/usb/smsc95xx.h | 5 |
2 files changed, 182 insertions, 8 deletions
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index ba0360fb857f..7479a5761d0d 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #define SMSC95XX_INTERNAL_PHY_ID (1) | 46 | #define SMSC95XX_INTERNAL_PHY_ID (1) |
47 | #define SMSC95XX_TX_OVERHEAD (8) | 47 | #define SMSC95XX_TX_OVERHEAD (8) |
48 | #define SMSC95XX_TX_OVERHEAD_CSUM (12) | 48 | #define SMSC95XX_TX_OVERHEAD_CSUM (12) |
49 | #define SUPPORTED_WAKE (WAKE_MAGIC) | ||
49 | 50 | ||
50 | #define check_warn(ret, fmt, args...) \ | 51 | #define check_warn(ret, fmt, args...) \ |
51 | ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) | 52 | ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) |
@@ -60,6 +61,7 @@ struct smsc95xx_priv { | |||
60 | u32 mac_cr; | 61 | u32 mac_cr; |
61 | u32 hash_hi; | 62 | u32 hash_hi; |
62 | u32 hash_lo; | 63 | u32 hash_lo; |
64 | u32 wolopts; | ||
63 | spinlock_t mac_cr_lock; | 65 | spinlock_t mac_cr_lock; |
64 | }; | 66 | }; |
65 | 67 | ||
@@ -125,6 +127,30 @@ static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index, | |||
125 | return ret; | 127 | return ret; |
126 | } | 128 | } |
127 | 129 | ||
130 | static int smsc95xx_set_feature(struct usbnet *dev, u32 feature) | ||
131 | { | ||
132 | if (WARN_ON_ONCE(!dev)) | ||
133 | return -EINVAL; | ||
134 | |||
135 | cpu_to_le32s(&feature); | ||
136 | |||
137 | return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), | ||
138 | USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0, | ||
139 | USB_CTRL_SET_TIMEOUT); | ||
140 | } | ||
141 | |||
142 | static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature) | ||
143 | { | ||
144 | if (WARN_ON_ONCE(!dev)) | ||
145 | return -EINVAL; | ||
146 | |||
147 | cpu_to_le32s(&feature); | ||
148 | |||
149 | return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), | ||
150 | USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0, | ||
151 | USB_CTRL_SET_TIMEOUT); | ||
152 | } | ||
153 | |||
128 | /* Loop until the read is completed with timeout | 154 | /* Loop until the read is completed with timeout |
129 | * called with phy_mutex held */ | 155 | * called with phy_mutex held */ |
130 | static int __must_check smsc95xx_phy_wait_not_busy(struct usbnet *dev) | 156 | static int __must_check smsc95xx_phy_wait_not_busy(struct usbnet *dev) |
@@ -636,6 +662,26 @@ smsc95xx_ethtool_getregs(struct net_device *netdev, struct ethtool_regs *regs, | |||
636 | } | 662 | } |
637 | } | 663 | } |
638 | 664 | ||
665 | static void smsc95xx_ethtool_get_wol(struct net_device *net, | ||
666 | struct ethtool_wolinfo *wolinfo) | ||
667 | { | ||
668 | struct usbnet *dev = netdev_priv(net); | ||
669 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
670 | |||
671 | wolinfo->supported = SUPPORTED_WAKE; | ||
672 | wolinfo->wolopts = pdata->wolopts; | ||
673 | } | ||
674 | |||
675 | static int smsc95xx_ethtool_set_wol(struct net_device *net, | ||
676 | struct ethtool_wolinfo *wolinfo) | ||
677 | { | ||
678 | struct usbnet *dev = netdev_priv(net); | ||
679 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
680 | |||
681 | pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; | ||
682 | return 0; | ||
683 | } | ||
684 | |||
639 | static const struct ethtool_ops smsc95xx_ethtool_ops = { | 685 | static const struct ethtool_ops smsc95xx_ethtool_ops = { |
640 | .get_link = usbnet_get_link, | 686 | .get_link = usbnet_get_link, |
641 | .nway_reset = usbnet_nway_reset, | 687 | .nway_reset = usbnet_nway_reset, |
@@ -649,6 +695,8 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = { | |||
649 | .set_eeprom = smsc95xx_ethtool_set_eeprom, | 695 | .set_eeprom = smsc95xx_ethtool_set_eeprom, |
650 | .get_regs_len = smsc95xx_ethtool_getregslen, | 696 | .get_regs_len = smsc95xx_ethtool_getregslen, |
651 | .get_regs = smsc95xx_ethtool_getregs, | 697 | .get_regs = smsc95xx_ethtool_getregs, |
698 | .get_wol = smsc95xx_ethtool_get_wol, | ||
699 | .set_wol = smsc95xx_ethtool_set_wol, | ||
652 | }; | 700 | }; |
653 | 701 | ||
654 | static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) | 702 | static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) |
@@ -1021,26 +1069,147 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) | |||
1021 | static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) | 1069 | static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) |
1022 | { | 1070 | { |
1023 | struct usbnet *dev = usb_get_intfdata(intf); | 1071 | struct usbnet *dev = usb_get_intfdata(intf); |
1072 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
1024 | int ret; | 1073 | int ret; |
1025 | u32 val; | 1074 | u32 val; |
1026 | 1075 | ||
1027 | if (WARN_ON_ONCE(!dev)) | ||
1028 | return -EINVAL; | ||
1029 | |||
1030 | ret = usbnet_suspend(intf, message); | 1076 | ret = usbnet_suspend(intf, message); |
1031 | check_warn_return(ret, "usbnet_suspend error"); | 1077 | check_warn_return(ret, "usbnet_suspend error"); |
1032 | 1078 | ||
1033 | netdev_info(dev->net, "entering SUSPEND2 mode"); | 1079 | /* if no wol options set, enter lowest power SUSPEND2 mode */ |
1080 | if (!(pdata->wolopts & SUPPORTED_WAKE)) { | ||
1081 | netdev_info(dev->net, "entering SUSPEND2 mode"); | ||
1082 | |||
1083 | /* disable energy detect (link up) & wake up events */ | ||
1084 | ret = smsc95xx_read_reg(dev, WUCSR, &val); | ||
1085 | check_warn_return(ret, "Error reading WUCSR"); | ||
1086 | |||
1087 | val &= ~(WUCSR_MPEN_ | WUCSR_WAKE_EN_); | ||
1088 | |||
1089 | ret = smsc95xx_write_reg(dev, WUCSR, val); | ||
1090 | check_warn_return(ret, "Error writing WUCSR"); | ||
1091 | |||
1092 | ret = smsc95xx_read_reg(dev, PM_CTRL, &val); | ||
1093 | check_warn_return(ret, "Error reading PM_CTRL"); | ||
1094 | |||
1095 | val &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_); | ||
1096 | |||
1097 | ret = smsc95xx_write_reg(dev, PM_CTRL, val); | ||
1098 | check_warn_return(ret, "Error writing PM_CTRL"); | ||
1099 | |||
1100 | /* enter suspend2 mode */ | ||
1101 | ret = smsc95xx_read_reg(dev, PM_CTRL, &val); | ||
1102 | check_warn_return(ret, "Error reading PM_CTRL"); | ||
1103 | |||
1104 | val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); | ||
1105 | val |= PM_CTL_SUS_MODE_2; | ||
1106 | |||
1107 | ret = smsc95xx_write_reg(dev, PM_CTRL, val); | ||
1108 | check_warn_return(ret, "Error writing PM_CTRL"); | ||
1109 | |||
1110 | return 0; | ||
1111 | } | ||
1112 | |||
1113 | if (pdata->wolopts & WAKE_MAGIC) { | ||
1114 | /* clear any pending magic packet status */ | ||
1115 | ret = smsc95xx_read_reg(dev, WUCSR, &val); | ||
1116 | check_warn_return(ret, "Error reading WUCSR"); | ||
1117 | |||
1118 | val |= WUCSR_MPR_; | ||
1119 | |||
1120 | ret = smsc95xx_write_reg(dev, WUCSR, val); | ||
1121 | check_warn_return(ret, "Error writing WUCSR"); | ||
1122 | } | ||
1123 | |||
1124 | /* enable/disable magic packup wake */ | ||
1125 | ret = smsc95xx_read_reg(dev, WUCSR, &val); | ||
1126 | check_warn_return(ret, "Error reading WUCSR"); | ||
1127 | |||
1128 | if (pdata->wolopts & WAKE_MAGIC) { | ||
1129 | netdev_info(dev->net, "enabling magic packet wakeup"); | ||
1130 | val |= WUCSR_MPEN_; | ||
1131 | } else { | ||
1132 | netdev_info(dev->net, "disabling magic packet wakeup"); | ||
1133 | val &= ~WUCSR_MPEN_; | ||
1134 | } | ||
1135 | |||
1136 | ret = smsc95xx_write_reg(dev, WUCSR, val); | ||
1137 | check_warn_return(ret, "Error writing WUCSR"); | ||
1138 | |||
1139 | /* enable wol wakeup source */ | ||
1140 | ret = smsc95xx_read_reg(dev, PM_CTRL, &val); | ||
1141 | check_warn_return(ret, "Error reading PM_CTRL"); | ||
1142 | |||
1143 | val |= PM_CTL_WOL_EN_; | ||
1144 | |||
1145 | ret = smsc95xx_write_reg(dev, PM_CTRL, val); | ||
1146 | check_warn_return(ret, "Error writing PM_CTRL"); | ||
1147 | |||
1148 | /* enable receiver */ | ||
1149 | smsc95xx_start_rx_path(dev); | ||
1150 | |||
1151 | /* some wol options are enabled, so enter SUSPEND0 */ | ||
1152 | netdev_info(dev->net, "entering SUSPEND0 mode"); | ||
1034 | 1153 | ||
1035 | ret = smsc95xx_read_reg(dev, PM_CTRL, &val); | 1154 | ret = smsc95xx_read_reg(dev, PM_CTRL, &val); |
1036 | check_warn_return(ret, "Error reading PM_CTRL"); | 1155 | check_warn_return(ret, "Error reading PM_CTRL"); |
1037 | 1156 | ||
1038 | val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); | 1157 | val &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_)); |
1039 | val |= PM_CTL_SUS_MODE_2; | 1158 | val |= PM_CTL_SUS_MODE_0; |
1040 | 1159 | ||
1041 | ret = smsc95xx_write_reg(dev, PM_CTRL, val); | 1160 | ret = smsc95xx_write_reg(dev, PM_CTRL, val); |
1042 | check_warn_return(ret, "Error writing PM_CTRL"); | 1161 | check_warn_return(ret, "Error writing PM_CTRL"); |
1043 | 1162 | ||
1163 | /* clear wol status */ | ||
1164 | val &= ~PM_CTL_WUPS_; | ||
1165 | val |= PM_CTL_WUPS_WOL_; | ||
1166 | ret = smsc95xx_write_reg(dev, PM_CTRL, val); | ||
1167 | check_warn_return(ret, "Error writing PM_CTRL"); | ||
1168 | |||
1169 | /* read back PM_CTRL */ | ||
1170 | ret = smsc95xx_read_reg(dev, PM_CTRL, &val); | ||
1171 | check_warn_return(ret, "Error reading PM_CTRL"); | ||
1172 | |||
1173 | smsc95xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); | ||
1174 | |||
1175 | return 0; | ||
1176 | } | ||
1177 | |||
1178 | static int smsc95xx_resume(struct usb_interface *intf) | ||
1179 | { | ||
1180 | struct usbnet *dev = usb_get_intfdata(intf); | ||
1181 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
1182 | int ret; | ||
1183 | u32 val; | ||
1184 | |||
1185 | BUG_ON(!dev); | ||
1186 | |||
1187 | if (pdata->wolopts & WAKE_MAGIC) { | ||
1188 | smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP); | ||
1189 | |||
1190 | /* Disable magic packup wake */ | ||
1191 | ret = smsc95xx_read_reg(dev, WUCSR, &val); | ||
1192 | check_warn_return(ret, "Error reading WUCSR"); | ||
1193 | |||
1194 | val &= ~WUCSR_MPEN_; | ||
1195 | |||
1196 | ret = smsc95xx_write_reg(dev, WUCSR, val); | ||
1197 | check_warn_return(ret, "Error writing WUCSR"); | ||
1198 | |||
1199 | /* clear wake-up status */ | ||
1200 | ret = smsc95xx_read_reg(dev, PM_CTRL, &val); | ||
1201 | check_warn_return(ret, "Error reading PM_CTRL"); | ||
1202 | |||
1203 | val &= ~PM_CTL_WOL_EN_; | ||
1204 | val |= PM_CTL_WUPS_; | ||
1205 | |||
1206 | ret = smsc95xx_write_reg(dev, PM_CTRL, val); | ||
1207 | check_warn_return(ret, "Error writing PM_CTRL"); | ||
1208 | } | ||
1209 | |||
1210 | return usbnet_resume(intf); | ||
1211 | check_warn_return(ret, "usbnet_resume error"); | ||
1212 | |||
1044 | return 0; | 1213 | return 0; |
1045 | } | 1214 | } |
1046 | 1215 | ||
@@ -1307,8 +1476,8 @@ static struct usb_driver smsc95xx_driver = { | |||
1307 | .id_table = products, | 1476 | .id_table = products, |
1308 | .probe = usbnet_probe, | 1477 | .probe = usbnet_probe, |
1309 | .suspend = smsc95xx_suspend, | 1478 | .suspend = smsc95xx_suspend, |
1310 | .resume = usbnet_resume, | 1479 | .resume = smsc95xx_resume, |
1311 | .reset_resume = usbnet_resume, | 1480 | .reset_resume = smsc95xx_resume, |
1312 | .disconnect = usbnet_disconnect, | 1481 | .disconnect = usbnet_disconnect, |
1313 | .disable_hub_initiated_lpm = 1, | 1482 | .disable_hub_initiated_lpm = 1, |
1314 | }; | 1483 | }; |
diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h index 89ad9259d613..2ff9815aa27c 100644 --- a/drivers/net/usb/smsc95xx.h +++ b/drivers/net/usb/smsc95xx.h | |||
@@ -205,6 +205,11 @@ | |||
205 | #define WUFF (0x128) | 205 | #define WUFF (0x128) |
206 | 206 | ||
207 | #define WUCSR (0x12C) | 207 | #define WUCSR (0x12C) |
208 | #define WUCSR_GUE_ (0x00000200) | ||
209 | #define WUCSR_WUFR_ (0x00000040) | ||
210 | #define WUCSR_MPR_ (0x00000020) | ||
211 | #define WUCSR_WAKE_EN_ (0x00000004) | ||
212 | #define WUCSR_MPEN_ (0x00000002) | ||
208 | 213 | ||
209 | #define COE_CR (0x130) | 214 | #define COE_CR (0x130) |
210 | #define Tx_COE_EN_ (0x00010000) | 215 | #define Tx_COE_EN_ (0x00010000) |